From nobody Mon Feb 9 08:57:22 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1598924931; cv=none; d=zohomail.com; s=zohoarc; b=MC+brHEyMFakuQdklR2mzASpw+t+1pWrt5ERwWAjyw8+L+3dn+S2xdS1SBMGIVncgC9BRRw7kP+IWKAN+jpC7Cm78HUJElOhfI9luRICFze3WKpBBwpj3krlR9aJ7EOwbGqZ/hb4PnePhYaSHkM40307ZBIc1GhlKFrzt6KaVbI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1598924931; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To; bh=bXIVfvpKQPtNAPe1+jrOvtUCUMq1RSWy8OrFZDkFxw4=; b=g49vmGnwL9mvCXERHCUQvrglmf0ZuV29m77ub4S/PTuG02P36Ct1kL1iSU2B3a19FQFH5sRm9GVV7UK7DyJRhnTELYfc39hkQV5NPfkUTn14eVdYZoSIwB3ST1TWcNtkg1maN5yFBf4F8LPgCZ6/2UUMFPjXdARH9uZ/H2AW2C0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1598924931831761.2227671551569; Mon, 31 Aug 2020 18:48:51 -0700 (PDT) Received: from localhost ([::1]:55068 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kCvPn-0008UC-3R for importer@patchew.org; Mon, 31 Aug 2020 21:48:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:40932) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kCvIB-0004t8-T7; Mon, 31 Aug 2020 21:40:59 -0400 Received: from mail-pf1-x442.google.com ([2607:f8b0:4864:20::442]:34507) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kCvI9-0005bm-Ag; Mon, 31 Aug 2020 21:40:59 -0400 Received: by mail-pf1-x442.google.com with SMTP id g207so1748159pfb.1; Mon, 31 Aug 2020 18:40:56 -0700 (PDT) Received: from pek-vx-bsp2.wrs.com (unknown-146-144.windriver.com. [147.11.146.144]) by smtp.gmail.com with ESMTPSA id l13sm8887520pgq.33.2020.08.31.18.40.52 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 31 Aug 2020 18:40:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=bXIVfvpKQPtNAPe1+jrOvtUCUMq1RSWy8OrFZDkFxw4=; b=iPJkyfhdQwX3FFf1dOlgZxktgJeD4n9YrP4S45fbu9DGh22JhRCyvUbozZrbTpJx2I DJsIHVzrVR3AnitcffvxNu12ySoaFs2Tk6U0pkOI+d+RJ87EP3DfC3JjyYr6W04SPiGa ih0Lv4v29IvRWbR+rHAP1Lq7Qx6X8mIDA+WShx4hwLimRhR5gySBxjaW7F7IGj0WlmB5 V3zMS42P2TqjUkq6WCeB6M6FX7kXfpj0/UedlofpvhWsBYvnwvrk61mNKHbmKmoeVz5z xL9w3jZ4Ryrg1k7r/Kot7UKbXJ9GbDDTHbbp5NzYGqtLo03OS85KMhtvYpfcaAKvFOoa y83A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=bXIVfvpKQPtNAPe1+jrOvtUCUMq1RSWy8OrFZDkFxw4=; b=IdGsUmCgd/L4/JTHqqKJtUGWF91RQ0renHTO9mvEGH6vs74eDIHML6uf1/Wk1BCRBO Me0IF9+lAN6z+Z6Sy492094GAgZvhC8VQ1R0kZGObNNz07/iMqKE0JExF2nSp6MMAQBh Dx24BBNg4brA7meAs52B3Hofi08gd3+olf7zZzIv6cKlaIuCdcsMCxOda/Z0gMSnv2Sn 6tAgeDMbOKFk2bHPXzZBsNfQ9LfwteGR92obGiDoo8+kvNXFP2+/MH1aDmai9Cs6o2Vr Jgh/HlQY4JEkLNHmHir/xoCFUEO7GpF7uNA8ea9e4QFUgnLmuL9q8e2EpWC50+lUjibC Ifdw== X-Gm-Message-State: AOAM53371VMdLwZTfgXuSGeskI+nTqu+6XDKu14z6ojkRbQJwyPiJh2N RPGal8f/XAE8oQ/t5WczITM= X-Google-Smtp-Source: ABdhPJyyjcnY2Znhx75j5+ddbnlUOirn4tQYx6Vd60/o3ijulw2zvNmOUd2zN3zkL4hMbfehq4D1ZA== X-Received: by 2002:a63:4664:: with SMTP id v36mr3377362pgk.194.1598924455654; Mon, 31 Aug 2020 18:40:55 -0700 (PDT) From: Bin Meng To: Alistair Francis , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Palmer Dabbelt , qemu-devel@nongnu.org, qemu-riscv@nongnu.org Subject: [PATCH v3 09/16] hw/dma: Add SiFive platform DMA controller emulation Date: Tue, 1 Sep 2020 09:39:04 +0800 Message-Id: <1598924352-89526-10-git-send-email-bmeng.cn@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1598924352-89526-1-git-send-email-bmeng.cn@gmail.com> References: <1598924352-89526-1-git-send-email-bmeng.cn@gmail.com> 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=2607:f8b0:4864:20::442; envelope-from=bmeng.cn@gmail.com; helo=mail-pf1-x442.google.com X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Bin Meng , Paolo Bonzini Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Bin Meng Microchip PolarFire SoC integrates a DMA engine that supports: * Independent concurrent DMA transfers using 4 DMA channels * Generation of interrupts on various conditions during execution which is actually an IP reused from the SiFive FU540 chip. This creates a model to support both polling and interrupt modes. Signed-off-by: Bin Meng Acked-by: Alistair Francis --- Changes in v3: - change MCHP_PFSOC_DMA to SIFIVE_PDMA Changes in v2: - change to update hw/dma/meson.build - rename the file names to sifive_pdma.[c|h] - update irq number to 8 per the SiFive FU540 manual - fix the register offset for channel 1/2/3 in the read/write ops include/hw/dma/sifive_pdma.h | 57 ++++++++ hw/dma/sifive_pdma.c | 313 +++++++++++++++++++++++++++++++++++++++= ++++ hw/dma/Kconfig | 3 + hw/dma/meson.build | 1 + 4 files changed, 374 insertions(+) create mode 100644 include/hw/dma/sifive_pdma.h create mode 100644 hw/dma/sifive_pdma.c diff --git a/include/hw/dma/sifive_pdma.h b/include/hw/dma/sifive_pdma.h new file mode 100644 index 0000000..e319bbd --- /dev/null +++ b/include/hw/dma/sifive_pdma.h @@ -0,0 +1,57 @@ +/* + * SiFive Platform DMA emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#ifndef SIFIVE_PDMA_H +#define SIFIVE_PDMA_H + +struct sifive_pdma_chan { + uint32_t control; + uint32_t next_config; + uint64_t next_bytes; + uint64_t next_dst; + uint64_t next_src; + uint32_t exec_config; + uint64_t exec_bytes; + uint64_t exec_dst; + uint64_t exec_src; + int state; +}; + +#define SIFIVE_PDMA_CHANS 4 +#define SIFIVE_PDMA_IRQS (SIFIVE_PDMA_CHANS * 2) +#define SIFIVE_PDMA_REG_SIZE 0x100000 +#define SIFIVE_PDMA_CHAN_NO(reg) ((reg & (SIFIVE_PDMA_REG_SIZE - 1)) >>= 12) + +typedef struct SiFivePDMAState { + SysBusDevice parent; + MemoryRegion iomem; + qemu_irq irq[SIFIVE_PDMA_IRQS]; + + struct sifive_pdma_chan chan[SIFIVE_PDMA_CHANS]; +} SiFivePDMAState; + +#define TYPE_SIFIVE_PDMA "sifive.pdma" + +#define SIFIVE_PDMA(obj) \ + OBJECT_CHECK(SiFivePDMAState, (obj), TYPE_SIFIVE_PDMA) + +#endif /* SIFIVE_PDMA_H */ diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c new file mode 100644 index 0000000..e1f6fed --- /dev/null +++ b/hw/dma/sifive_pdma.c @@ -0,0 +1,313 @@ +/* + * SiFive Platform DMA emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "sysemu/dma.h" +#include "hw/dma/sifive_pdma.h" + +#define DMA_CONTROL 0x000 +#define CONTROL_CLAIM BIT(0) +#define CONTROL_RUN BIT(1) +#define CONTROL_DONE_IE BIT(14) +#define CONTROL_ERR_IE BIT(15) +#define CONTROL_DONE BIT(30) +#define CONTROL_ERR BIT(31) + +#define DMA_NEXT_CONFIG 0x004 +#define CONFIG_REPEAT BIT(2) +#define CONFIG_ORDER BIT(3) +#define CONFIG_WRSZ_SHIFT 24 +#define CONFIG_RDSZ_SHIFT 28 +#define CONFIG_SZ_MASK 0xf + +#define DMA_NEXT_BYTES 0x008 +#define DMA_NEXT_DST 0x010 +#define DMA_NEXT_SRC 0x018 +#define DMA_EXEC_CONFIG 0x104 +#define DMA_EXEC_BYTES 0x108 +#define DMA_EXEC_DST 0x110 +#define DMA_EXEC_SRC 0x118 + +enum dma_chan_state { + DMA_CHAN_STATE_IDLE, + DMA_CHAN_STATE_STARTED, + DMA_CHAN_STATE_ERROR, + DMA_CHAN_STATE_DONE +}; + +static void sifive_pdma_run(SiFivePDMAState *s, int ch) +{ + uint64_t bytes =3D s->chan[ch].next_bytes; + uint64_t dst =3D s->chan[ch].next_dst; + uint64_t src =3D s->chan[ch].next_src; + uint32_t config =3D s->chan[ch].next_config; + int wsize, rsize, size; + uint8_t buf[64]; + int n; + + /* do nothing if bytes to transfer is zero */ + if (!bytes) { + goto error; + } + + /* + * The manual does not describe how the hardware behaviors when + * config.wsize and config.rsize are given different values. + * A common case is memory to memory DMA, and in this case they + * are normally the same. Abort if this expectation fails. + */ + wsize =3D (config >> CONFIG_WRSZ_SHIFT) & CONFIG_SZ_MASK; + rsize =3D (config >> CONFIG_RDSZ_SHIFT) & CONFIG_SZ_MASK; + if (wsize !=3D rsize) { + goto error; + } + + /* + * Calculate the transaction size + * + * size field is base 2 logarithm of DMA transaction size, + * but there is an upper limit of 64 bytes per transaction. + */ + size =3D wsize; + if (size > 6) { + size =3D 6; + } + size =3D 1 << size; + + /* the bytes to transfer should be multiple of transaction size */ + if (bytes % size) { + goto error; + } + + /* indicate a DMA transfer is started */ + s->chan[ch].state =3D DMA_CHAN_STATE_STARTED; + s->chan[ch].control &=3D ~CONTROL_DONE; + s->chan[ch].control &=3D ~CONTROL_ERR; + + /* load the next_ registers into their exec_ counterparts */ + s->chan[ch].exec_config =3D config; + s->chan[ch].exec_bytes =3D bytes; + s->chan[ch].exec_dst =3D dst; + s->chan[ch].exec_src =3D src; + + for (n =3D 0; n < bytes / size; n++) { + cpu_physical_memory_read(s->chan[ch].exec_src, buf, size); + cpu_physical_memory_write(s->chan[ch].exec_dst, buf, size); + s->chan[ch].exec_src +=3D size; + s->chan[ch].exec_dst +=3D size; + s->chan[ch].exec_bytes -=3D size; + } + + /* indicate a DMA transfer is done */ + s->chan[ch].state =3D DMA_CHAN_STATE_DONE; + s->chan[ch].control &=3D ~CONTROL_RUN; + s->chan[ch].control |=3D CONTROL_DONE; + + /* reload exec_ registers if repeat is required */ + if (s->chan[ch].next_config & CONFIG_REPEAT) { + s->chan[ch].exec_bytes =3D bytes; + s->chan[ch].exec_dst =3D dst; + s->chan[ch].exec_src =3D src; + } + + return; + +error: + s->chan[ch].state =3D DMA_CHAN_STATE_ERROR; + s->chan[ch].control |=3D CONTROL_ERR; + return; +} + +static inline void sifive_pdma_update_irq(SiFivePDMAState *s, int ch) +{ + bool done_ie, err_ie; + + done_ie =3D !!(s->chan[ch].control & CONTROL_DONE_IE); + err_ie =3D !!(s->chan[ch].control & CONTROL_ERR_IE); + + if (done_ie && (s->chan[ch].control & CONTROL_DONE)) { + qemu_irq_raise(s->irq[ch * 2]); + } else { + qemu_irq_lower(s->irq[ch * 2]); + } + + if (err_ie && (s->chan[ch].control & CONTROL_ERR)) { + qemu_irq_raise(s->irq[ch * 2 + 1]); + } else { + qemu_irq_lower(s->irq[ch * 2 + 1]); + } + + s->chan[ch].state =3D DMA_CHAN_STATE_IDLE; +} + +static uint64_t sifive_pdma_read(void *opaque, hwaddr offset, unsigned siz= e) +{ + SiFivePDMAState *s =3D opaque; + int ch =3D SIFIVE_PDMA_CHAN_NO(offset); + uint64_t val =3D 0; + + if (ch >=3D SIFIVE_PDMA_CHANS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n", + __func__, ch); + return 0; + } + + offset &=3D 0xfff; + switch (offset) { + case DMA_CONTROL: + val =3D s->chan[ch].control; + break; + case DMA_NEXT_CONFIG: + val =3D s->chan[ch].next_config; + break; + case DMA_NEXT_BYTES: + val =3D s->chan[ch].next_bytes; + break; + case DMA_NEXT_DST: + val =3D s->chan[ch].next_dst; + break; + case DMA_NEXT_SRC: + val =3D s->chan[ch].next_src; + break; + case DMA_EXEC_CONFIG: + val =3D s->chan[ch].exec_config; + break; + case DMA_EXEC_BYTES: + val =3D s->chan[ch].exec_bytes; + break; + case DMA_EXEC_DST: + val =3D s->chan[ch].exec_dst; + break; + case DMA_EXEC_SRC: + val =3D s->chan[ch].exec_src; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\= n", + __func__, offset); + break; + } + + return val; +} + +static void sifive_pdma_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + SiFivePDMAState *s =3D opaque; + int ch =3D SIFIVE_PDMA_CHAN_NO(offset); + + if (ch >=3D SIFIVE_PDMA_CHANS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n", + __func__, ch); + return; + } + + offset &=3D 0xfff; + switch (offset) { + case DMA_CONTROL: + s->chan[ch].control =3D value; + + if (value & CONTROL_RUN) { + sifive_pdma_run(s, ch); + } + + sifive_pdma_update_irq(s, ch); + break; + case DMA_NEXT_CONFIG: + s->chan[ch].next_config =3D value; + break; + case DMA_NEXT_BYTES: + s->chan[ch].next_bytes =3D value; + break; + case DMA_NEXT_DST: + s->chan[ch].next_dst =3D value; + break; + case DMA_NEXT_SRC: + s->chan[ch].next_src =3D value; + break; + case DMA_EXEC_CONFIG: + case DMA_EXEC_BYTES: + case DMA_EXEC_DST: + case DMA_EXEC_SRC: + /* these are read-only registers */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\= n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps sifive_pdma_ops =3D { + .read =3D sifive_pdma_read, + .write =3D sifive_pdma_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + /* there are 32-bit and 64-bit wide registers */ + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + } +}; + +static void sifive_pdma_realize(DeviceState *dev, Error **errp) +{ + SiFivePDMAState *s =3D SIFIVE_PDMA(dev); + int i; + + memory_region_init_io(&s->iomem, OBJECT(dev), &sifive_pdma_ops, s, + TYPE_SIFIVE_PDMA, SIFIVE_PDMA_REG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + for (i =3D 0; i < SIFIVE_PDMA_IRQS; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); + } +} + +static void sifive_pdma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "SiFive Platform DMA controller"; + dc->realize =3D sifive_pdma_realize; +} + +static const TypeInfo sifive_pdma_info =3D { + .name =3D TYPE_SIFIVE_PDMA, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(SiFivePDMAState), + .class_init =3D sifive_pdma_class_init, +}; + +static void sifive_pdma_register_types(void) +{ + type_register_static(&sifive_pdma_info); +} + +type_init(sifive_pdma_register_types) diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 5c61b67..d67492d 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -20,3 +20,6 @@ config ZYNQ_DEVCFG =20 config STP2000 bool + +config SIFIVE_PDMA + bool diff --git a/hw/dma/meson.build b/hw/dma/meson.build index ff5bb37..b991d76 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -13,3 +13,4 @@ softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: f= iles('xlnx-zdma.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.= c')) softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) --=20 2.7.4