From nobody Thu Jan 1 09:00:42 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1698225060; cv=none; d=zohomail.com; s=zohoarc; b=XPuQyQYEq59oX+NtkuBG6+Bf4Rvp/s9+4agGeMo1Q7+up/Slj42pNsGTc1Cm8SV+V5NREpUDeeIXsUWTmNUQBA2CTvVnRWMIr/cm/0DDN1IcrYDE9JGAN420j6f295QqOMQR3NNORbr69ajicq8Fvkz/K+YCj+xTIQ8BeQ5LZ/0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1698225060; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=Ch/U/SPHHFbFA9jfiVD2BM5RJXyyrKztc/NdX3duD10=; b=YhVNiTTZXZzflEiTGB3mySvSm6f8Ehv/aBm5VZzC75+5PYF3mPLJp2LMcZ3QtuuVwCeHNbUuakUvd6yEO44V+c0cjCOlqP0U5q0EPudRzyWAy27BYdYAMhwjFDPoyUavN69Cj81bniBoiUVHPPj0DDrjWM29e86kTckbWij9tgs= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1698225060988478.5227417161934; Wed, 25 Oct 2023 02:11:00 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qvZtI-0005cm-4W; Wed, 25 Oct 2023 05:09:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qvWSZ-00060s-Sj for qemu-devel@nongnu.org; Wed, 25 Oct 2023 01:29:40 -0400 Received: from 60-248-80-70.hinet-ip.hinet.net ([60.248.80.70] helo=Atcsqr.andestech.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qvWSW-0001Jg-HB for qemu-devel@nongnu.org; Wed, 25 Oct 2023 01:29:39 -0400 Received: from Atcsqr.andestech.com (localhost [127.0.0.2] (may be forged)) by Atcsqr.andestech.com with ESMTP id 39P5FWbQ024688 for ; Wed, 25 Oct 2023 13:15:32 +0800 (+08) (envelope-from ethan84@andestech.com) Received: from mail.andestech.com (ATCPCS16.andestech.com [10.0.1.222]) by Atcsqr.andestech.com with ESMTP id 39P5FNuu024599; Wed, 25 Oct 2023 13:15:23 +0800 (+08) (envelope-from ethan84@andestech.com) Received: from ethan84-VirtualBox.andestech.com (10.0.12.51) by ATCPCS16.andestech.com (10.0.1.222) with Microsoft SMTP Server id 14.3.498.0; Wed, 25 Oct 2023 13:15:18 +0800 To: CC: Ethan Chen , Paolo Bonzini Subject: [PATCH 5/6] hw/dma: Add Andes ATCDMAC300 support Date: Wed, 25 Oct 2023 13:14:29 +0800 Message-ID: <20231025051430.493079-6-ethan84@andestech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231025051430.493079-1-ethan84@andestech.com> References: <20231025051430.493079-1-ethan84@andestech.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.0.12.51] X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: Atcsqr.andestech.com 39P5FWbQ024688 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=60.248.80.70; envelope-from=ethan84@andestech.com; helo=Atcsqr.andestech.com X-Spam_score_int: -8 X-Spam_score: -0.9 X-Spam_bar: / X-Spam_report: (-0.9 / 5.0 requ) BAYES_00=-1.9, RDNS_DYNAMIC=0.982, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, TVD_RCVD_IP=0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 25 Oct 2023 05:09:25 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Ethan Chen From: Ethan Chen via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1698225063232100003 Content-Type: text/plain; charset="utf-8" Signed-off-by: Ethan Chen --- hw/dma/Kconfig | 3 + hw/dma/atcdmac300.c | 435 ++++++++++++++++++++++++++++++++++++ hw/dma/meson.build | 1 + include/hw/dma/atcdmac300.h | 171 ++++++++++++++ 4 files changed, 610 insertions(+) create mode 100644 hw/dma/atcdmac300.c create mode 100644 include/hw/dma/atcdmac300.h diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 98fbb1bb04..b3f4edc873 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -30,3 +30,6 @@ config SIFIVE_PDMA config XLNX_CSU_DMA bool select REGISTER + +config ATCDMAC300 + bool diff --git a/hw/dma/atcdmac300.c b/hw/dma/atcdmac300.c new file mode 100644 index 0000000000..4cc37060d6 --- /dev/null +++ b/hw/dma/atcdmac300.c @@ -0,0 +1,435 @@ +/* + * Andes ATCDMAC300 (Andes Technology DMA Controller) + * + * Copyright (c) 2022 Andes Tech. Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 f= or + * more details. + * + * You should have received a copy of the GNU General Public License along= with + * this program. If not, see + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/dma/atcdmac300.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/memattrs.h" +#include "exec/address-spaces.h" + +/* #define DEBUG_ANDES_ATCDMAC300 */ +#define LOGGE(x...) qemu_log_mask(LOG_GUEST_ERROR, x) +#define xLOG(x...) +#define yLOG(x...) qemu_log(x) +#ifdef DEBUG_ANDES_ATCDMAC300 + #define LOG(x...) yLOG(x) +#else + #define LOG(x...) xLOG(x) +#endif + +#define MEMTX_IOPMP_STALL (1 << 3) + +static void atcdmac300_dma_int_stat_update(ATCDMAC300State *s, int status, + int ch) +{ + s->IntStatus |=3D (1 << (status + ch)); +} + +static void atcdmac300_dma_reset_chan(ATCDMAC300State *s, int ch) +{ + if (s) { + s->chan[ch].ChnCtrl &=3D ~(1 << CHAN_CTL_ENABLE); + s->ChEN &=3D ~(1 << ch); + } +} + +static void atcdmac300_dma_reset(ATCDMAC300State *s) +{ + int ch; + for (ch =3D 0; ch < ATCDMAC300_MAX_CHAN; ch++) { + atcdmac300_dma_reset_chan(s, ch); + } +} + +static uint64_t atcdmac300_read(void *opaque, hwaddr offset, unsigned size) +{ + ATCDMAC300State *s =3D opaque; + int ch =3D 0; + uint64_t result =3D 0; + + if (offset >=3D 0x40) { + ch =3D ATCDMAC300_GET_CHAN(offset); + offset =3D ATCDMAC300_GET_OFF(offset, ch); + } + + switch (offset) { + case ATCDMAC300_DMA_CFG: + result =3D s->DMACfg; + break; + case ATCDMAC300_DMAC_CTRL: + break; + case ATCDMAC300_CHN_ABT: + break; + case ATCDMAC300_INT_STATUS: + result =3D s->IntStatus; + break; + case ATCDMAC300_CHAN_ENABLE: + result =3D s->ChEN; + break; + case ATCDMAC300_CHAN_CTL: + result =3D s->chan[ch].ChnCtrl; + break; + default: + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } + + LOG("### atcdmac300_read()=3D0x%lx, val=3D0x%lx\n", offset, result); + return result; +} + +static MemTxResult dma_iopmp_read(ATCDMAC300State *s, hwaddr addr, void *b= uf, + hwaddr len) +{ + if (s->iopmp_as) { + MemTxAttrs dma_attrs =3D {.iopmp_sid =3D s->sid}; + return address_space_rw(s->iopmp_as, addr, dma_attrs, + buf, len, false); + } + cpu_physical_memory_read(addr, buf, len); + return MEMTX_OK; +} + +static MemTxResult dma_iopmp_write(ATCDMAC300State *s, hwaddr addr, void *= buf, + hwaddr len) +{ + if (s->iopmp_as) { + MemTxAttrs dma_attrs =3D {.iopmp_sid =3D s->sid}; + return address_space_rw(s->iopmp_as, addr, dma_attrs, + buf, len, true); + } + cpu_physical_memory_write(addr, buf, len); + return MEMTX_OK; +} + +static void atcdmac300_co_run(void *opaque) +{ + ATCDMAC300State *s =3D opaque; + int64_t remain_size, burst_remain_size, copy_size, copy_size_dst; + uint64_t src_addr, dst_addr, src_width, dst_width, burst_size, + src_addr_ctl, dst_addr_ctl, int_tc_mask, int_err_mask, + int_abort_mask; + uint64_t buf[ATCDMAC300_MAX_BURST_SIZE]; + int result =3D 0; + + while (1) { + for (int ch =3D 0; ch < ATCDMAC300_MAX_CHAN; ch++) { + if (((s->chan[ch].ChnCtrl >> CHAN_CTL_ENABLE) & 0x1) =3D=3D 0x= 1) { + src_width =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_WIDTH) & + CHAN_CTL_SRC_WIDTH_MASK; + dst_width =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_WIDTH) & + CHAN_CTL_DST_WIDTH_MASK; + burst_size =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_BURST_= SZ) & + CHAN_CTL_SRC_BURST_SZ_MASK; + src_addr =3D (s->chan[ch].ChnSrcAddrH << 32) | + s->chan[ch].ChnSrcAddr; + dst_addr =3D (s->chan[ch].ChnDstAddrH << 32) | + s->chan[ch].ChnDstAddr; + src_addr_ctl =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_ADDR= _CTL) & + CHAN_CTL_SRC_ADDR_CTL_MASK; + dst_addr_ctl =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_ADDR= _CTL) & + CHAN_CTL_DST_ADDR_CTL_MASK; + + src_width =3D 1 << src_width; + dst_width =3D 1 << dst_width; + burst_size =3D 1 << burst_size; + remain_size =3D s->chan[ch].ChnTranSize * src_width; + int_tc_mask =3D (s->chan[ch].ChnCtrl >> CHAN_CTL_INT_TC_MA= SK_POS) + & 0x1; + int_err_mask =3D (s->chan[ch].ChnCtrl >> + CHAN_CTL_INT_ERR_MASK_POS) & 0x1; + int_abort_mask =3D (s->chan[ch].ChnCtrl >> + CHAN_CTL_INT_ABT_MASK_POS) & 0x1; + if (remain_size && burst_size < (1 << 11) && + src_width < (1 << 6) && dst_width < (1 << 6) && + (src_addr & (src_width - 1)) =3D=3D 0 && + (dst_addr & (dst_width - 1)) =3D=3D 0 && + (remain_size & (dst_width - 1)) =3D=3D 0 && + (burst_size * src_width & (dst_width - 1)) =3D=3D 0) { + + while (remain_size > 0) { + /* check current abort status before a brust start= */ + if (s->ChAbort & (1 << ch)) { + result =3D DMA_ABT_RESULT; + s->ChAbort &=3D ~(1 << ch); + atcdmac300_dma_reset_chan(s, ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_A= BT, + ch); + if (!int_abort_mask) { + qemu_irq_raise(s->irq); + } + break; + } + + int i; + for (i =3D 0; i < burst_size; i++) { + if (remain_size <=3D 0) { + break; + } + + memset(buf, 0, sizeof(buf)); + + copy_size =3D MIN(remain_size, src_width); + result =3D dma_iopmp_read(s, src_addr, buf, + copy_size); + while (result =3D=3D MEMTX_IOPMP_STALL) { + qemu_coroutine_yield(); + result =3D dma_iopmp_read(s, src_addr, buf, + copy_size); + } + burst_remain_size =3D copy_size; + uint64_t *curr_ptr =3D buf; + while (burst_remain_size > 0) { + copy_size_dst =3D MIN(burst_remain_size, + dst_width); + result =3D dma_iopmp_write(s, dst_addr, cu= rr_ptr, + copy_size_dst); + while (result =3D=3D MEMTX_IOPMP_STALL) { + qemu_coroutine_yield(); + result =3D dma_iopmp_read(s, src_addr,= buf, + copy_size); + } + curr_ptr +=3D copy_size_dst; + burst_remain_size -=3D copy_size_dst; + } + + if (result !=3D MEMTX_OK) { + break; + } + + remain_size -=3D copy_size; + + if (src_addr_ctl =3D=3D 0) { + src_addr +=3D copy_size; + } + if (src_addr_ctl =3D=3D 1) { + src_addr -=3D copy_size; + } + if (dst_addr_ctl =3D=3D 0) { + dst_addr +=3D copy_size; + } + if (dst_addr_ctl =3D=3D 1) { + dst_addr -=3D copy_size; + } + } + if (result !=3D MEMTX_OK) { + break; + } + qemu_coroutine_yield(); + } + if (result =3D=3D MEMTX_OK) { + s->ChAbort &=3D ~(1 << ch); + atcdmac300_dma_reset_chan(s, ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_TC, c= h); + if (!int_tc_mask) { + qemu_irq_raise(s->irq); + } + } else if (result !=3D DMA_ABT_RESULT) { + s->ChAbort &=3D ~(1 << ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_ERR, = ch); + atcdmac300_dma_reset_chan(s, ch); + if (!int_err_mask) { + qemu_irq_raise(s->irq); + } + } + } else { + s->ChAbort &=3D ~(1 << ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_ERR, ch); + atcdmac300_dma_reset_chan(s, ch); + if (!int_err_mask) { + qemu_irq_raise(s->irq); + } + } + } + } + qemu_coroutine_yield(); + } +} + +static void atcdmac300_bh_cb(void *opaque) +{ + ATCDMAC300State *s =3D opaque; + + int rearm =3D 0; + if (s->running) { + rearm =3D 1; + goto out; + } else { + s->running =3D 1; + } + + AioContext *ctx =3D qemu_get_current_aio_context(); + aio_co_enter(ctx, s->co); + + s->running =3D 0; +out: + if (rearm) { + qemu_bh_schedule_idle(s->bh); + s->dma_bh_scheduled =3D true; + } + qemu_bh_schedule_idle(s->bh); + s->dma_bh_scheduled =3D true; + s->running =3D 0; +} + +static void atcdmac300_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + ATCDMAC300State *s =3D opaque; + int ch =3D 0; + + LOG("@@@ atcdmac300_write()=3D0x%lx, value=3D0x%lx\n", offset, value); + + if (offset >=3D 0x40) { + ch =3D ATCDMAC300_GET_CHAN(offset); + offset =3D ATCDMAC300_GET_OFF(offset, ch); + } + + switch (offset) { + case ATCDMAC300_INT_STATUS: + /* Write 1 to clear */ + s->IntStatus =3D 0; + break; + case ATCDMAC300_DMAC_CTRL: + atcdmac300_dma_reset(s); + break; + case ATCDMAC300_CHN_ABT: + for (int i =3D 0; i < ATCDMAC300_MAX_CHAN; i++) { + if (value & 0x1 && (s->chan[i].ChnCtrl & (1 << CHAN_CTL_ENABLE= ))) { + s->ChAbort |=3D (0x1 << i); + } + value >>=3D 1; + } + break; + case ATCDMAC300_CHAN_CTL: + s->chan[ch].ChnCtrl =3D value; + qemu_bh_schedule_idle(s->bh); + break; + case ATCDMAC300_CHAN_TRAN_SZ: + s->chan[ch].ChnTranSize =3D value; + break; + case ATCDMAC300_CHAN_SRC_ADDR: + s->chan[ch].ChnSrcAddr =3D value; + break; + case ATCDMAC300_CHAN_SRC_ADDR_H: + s->chan[ch].ChnSrcAddrH =3D value; + break; + case ATCDMAC300_CHAN_DST_ADDR: + s->chan[ch].ChnDstAddr =3D value; + break; + case ATCDMAC300_CHAN_DST_ADDR_H: + s->chan[ch].ChnDstAddrH =3D value; + break; + case ATCDMAC300_CHAN_LL_POINTER: + s->chan[ch].ChnLLPointer =3D value; + break; + case ATCDMAC300_CHAN_LL_POINTER_H: + s->chan[ch].ChnLLPointerH =3D value; + break; + default: + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps atcdmac300_ops =3D { + .read =3D atcdmac300_read, + .write =3D atcdmac300_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8 + } +}; + +static void atcdmac300_init(Object *obj) +{ + ATCDMAC300State *s =3D ATCDMAC300(obj); + SysBusDevice *sbus =3D SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbus, &s->irq); + memory_region_init_io(&s->mmio, obj, &atcdmac300_ops, s, TYPE_ATCDMAC3= 00, + s->mmio_size); + sysbus_init_mmio(sbus, &s->mmio); + if (s->iothread) { + s->ctx =3D iothread_get_aio_context(s->iothread); + } else { + s->ctx =3D qemu_get_aio_context(); + } + s->bh =3D aio_bh_new(s->ctx, atcdmac300_bh_cb, s); + s->co =3D qemu_coroutine_create(atcdmac300_co_run, s); +} + +static Property atcdmac300_properties[] =3D { + DEFINE_PROP_UINT32("mmio-size", ATCDMAC300State, mmio_size, 0x100000), + DEFINE_PROP_UINT32("id-and-revision", ATCDMAC300State, IdRev, + (ATCDMAC300_PRODUCT_ID << 8) | + ((ATCDMAC300_PRODUCT_ID & 0x7) << 4) | + ((ATCDMAC300_PRODUCT_ID & 0x7))), + DEFINE_PROP_UINT32("inturrupt-status", ATCDMAC300State, IntStatus, 0), + DEFINE_PROP_UINT32("dmac-configuration", ATCDMAC300State, + DMACfg, 0xc3404108), + DEFINE_PROP_LINK("iothread", ATCDMAC300State, iothread, + TYPE_IOTHREAD, IOThread *), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void atcdmac300_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k =3D DEVICE_CLASS(klass); + device_class_set_props(k, atcdmac300_properties); +} + +static const TypeInfo atcdmac300_info =3D { + .name =3D TYPE_ATCDMAC300, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(ATCDMAC300State), + .class_init =3D atcdmac300_class_init, + .instance_init =3D atcdmac300_init, +}; + +DeviceState * +atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_size, qemu_ir= q irq) +{ + DeviceState *dev; + dev =3D sysbus_create_varargs(TYPE_ATCDMAC300, addr, irq, NULL); + return dev; +} + +static void atcdmac300_register_types(void) +{ + type_register_static(&atcdmac300_info); +} + +void atcdmac300_connect_iopmp_as(DeviceState *dev, AddressSpace *iopmp_as, + uint32_t sid) +{ + ATCDMAC300State *s =3D ATCDMAC300(dev); + s->iopmp_as =3D iopmp_as; + s->sid =3D sid; +} + +type_init(atcdmac300_register_types) diff --git a/hw/dma/meson.build b/hw/dma/meson.build index a96c1be2c8..dfe37de32d 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -14,3 +14,4 @@ system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2= xx_dma.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c'= )) +system_ss.add(when: 'CONFIG_ATCDMAC300', if_true: files('atcdmac300.c')) \ No newline at end of file diff --git a/include/hw/dma/atcdmac300.h b/include/hw/dma/atcdmac300.h new file mode 100644 index 0000000000..c2688bc18f --- /dev/null +++ b/include/hw/dma/atcdmac300.h @@ -0,0 +1,171 @@ +/* + * Andes ATCDMAC300 (Andes Technology DMA Controller) + * + * Copyright (c) 2022 Andes Tech. Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 f= or + * more details. + * + * You should have received a copy of the GNU General Public License along= with + * this program. If not, see + * + */ + +#ifndef ATCDMAC300_H +#define ATCDMAC300_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "qemu/coroutine.h" +#include "block/aio.h" +#include "sysemu/iothread.h" +#include "sysemu/dma.h" + +#define TYPE_ATCDMAC300 "atcdmac300" +OBJECT_DECLARE_SIMPLE_TYPE(ATCDMAC300State, ATCDMAC300) + +#define ATCDMAC300_IOPMP_SID 0 + +#define ATCDMAC300_PRODUCT_ID 0x010230 +#define ATCDMAC300_REV_MAJOR 0x0 +#define ATCDMAC300_REV_MINOR 0x1 + +/* DMAC Configuration Register (Offset 0x10) */ +#define ATCDMAC300_DMA_CFG 0x10 +#define DMA_CFG_CHAIN_XFR 31 +#define DMA_CFG_REQ_SYNC 30 +#define DMA_CFG_DATA_WITDTH 24 +#define DMA_CFG_ADDR_WIDTH 17 +#define DMA_CFG_CORE_NUM 16 +#define DMA_CFG_BUS_NUM 15 +#define DMA_CFG_REQ_NUM 10 +#define DMA_CFG_FIFO_DEPTH 4 +#define DMA_CFG_CHAN_NUM 0 + +/* Interrupt Status Register (Offset 0x20) */ +#define ATCDMAC300_DMAC_CTRL 0x20 + +/* Channel Abort Register (Offset 0x24) */ +#define ATCDMAC300_CHN_ABT 0x24 + +/* Interrupt Status Register (Offset 0x30) */ +#define ATCDMAC300_INT_STATUS 0x30 +#define INT_STATUS_TC 16 +#define INT_STATUS_ABT 8 +#define INT_STATUS_ERR 0 + +/* Interrupt Status Register (Offset 0x34) */ +#define ATCDMAC300_CHAN_ENABLE 0x34 + +/* Channel n Control Register (Offset 0x40 + n*0x20) */ +#define CHAN_CTL_SRC_BUS_IDX 31 +#define CHAN_CTL_DST_BUS_IDX 30 +#define CHAN_CTL_PRIORITY 29 +#define CHAN_CTL_SRC_BURST_SZ 24 +#define CHAN_CTL_SRC_WIDTH 21 +#define CHAN_CTL_DST_WIDTH 18 +#define CHAN_CTL_SRC_MODE 17 +#define CHAN_CTL_DST_MODE 16 +#define CHAN_CTL_SRC_ADDR_CTL 14 +#define CHAN_CTL_DST_ADDR_CTL 12 +#define CHAN_CTL_SRC_REQ_SEL 8 +#define CHAN_CTL_DST_REQ_SEL 4 +#define CHAN_CTL_INT_ABT_MASK_POS 3 +#define CHAN_CTL_INT_ERR_MASK_POS 2 +#define CHAN_CTL_INT_TC_MASK_POS 1 +#define CHAN_CTL_ENABLE 0 + +#define CHAN_CTL_SRC_WIDTH_MASK 0x7 +#define CHAN_CTL_DST_WIDTH_MASK 0x7 +#define CHAN_CTL_SRC_BURST_SZ_MASK 0xf +#define CHAN_CTL_SRC_ADDR_CTL_MASK 0x3 +#define CHAN_CTL_DST_ADDR_CTL_MASK 0x3 + +#define ATCDMAC300_CHAN_CTL 0x40 +#define ATCDMAC300_CHAN_TRAN_SZ 0x44 +#define ATCDMAC300_CHAN_SRC_ADDR 0x48 +#define ATCDMAC300_CHAN_SRC_ADDR_H 0x4C +#define ATCDMAC300_CHAN_DST_ADDR 0x50 +#define ATCDMAC300_CHAN_DST_ADDR_H 0x54 +#define ATCDMAC300_CHAN_LL_POINTER 0x58 +#define ATCDMAC300_CHAN_LL_POINTER_H 0x5C + +#define ATCDMAC300_IRQ_START 0x40 +#define ATCDMAC300_IRQ_END (ATCDMAC300_IRQ_START + \ + ATCDMAC300_MAX_CHAN) + +#define ATCDMAC300_MAX_BURST_SIZE 1024 +#define ATCDMAC300_MAX_CHAN 0x8 + +#define PER_CHAN_OFFSET 0x20 +#define ATCDMAC300_FIRST_CHAN_BASE ATCDMAC300_CHAN_CTL +#define ATCDMAC300_GET_CHAN(reg) (((reg - ATCDMAC300_FIRST_CHAN_BAS= E) / \ + PER_CHAN_OFFSET)) +#define ATCDMAC300_GET_OFF(reg, ch) (reg - (ch * PER_CHAN_OFFSET)) + +#define DMA_ABT_RESULT (1 << 3) + +typedef struct { + qemu_irq irq; + + /* Channel control registers (n=3D0~7) */ + uint32_t ChnCtrl; + uint32_t ChnTranSize; + uint32_t ChnSrcAddr; + uint64_t ChnSrcAddrH; + uint32_t ChnDstAddr; + uint64_t ChnDstAddrH; + uint32_t ChnLLPointer; + uint32_t ChnLLPointerH; +} ATCDMAC300Chan; + +struct ATCDMAC300State { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + qemu_irq irq; + MemoryRegion mmio; + uint32_t mmio_size; + + /* ID and revision register */ + uint32_t IdRev; + + /* Configuration register */ + uint32_t DMACfg; + + /* Global control registers */ + uint32_t DMACtrl; + uint32_t ChAbort; + + /* Channel status registers */ + uint32_t IntStatus; + uint32_t ChEN; + + ATCDMAC300Chan chan[ATCDMAC300_MAX_CHAN]; + + /* To support iopmp */ + AddressSpace *iopmp_as; + uint32_t sid; + + Coroutine *co; + QEMUBH *bh; + bool running; + bool dma_bh_scheduled; + AioContext *ctx; + IOThread *iothread; +}; + +DeviceState *atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_= size, + qemu_irq irq); + +void atcdmac300_connect_iopmp_as(DeviceState *dev, AddressSpace *iopmp_as, + uint32_t sid); + +#endif /* ATCDMAC300_H */ --=20 2.34.1