From patchwork Mon Jun 15 15:15:41 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fred.konrad@greensocs.com X-Patchwork-Id: 484354 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 5546214029E for ; Tue, 16 Jun 2015 01:18:39 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=greensocs.com header.i=@greensocs.com header.b=Hu3ST2kr; dkim=fail reason="signature verification failed" (1024-bit key) header.d=greensocs.com header.i=@greensocs.com header.b=xm9W8FvP; dkim-atps=neutral Received: from localhost ([::1]:34783 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z4W9k-0000n6-U7 for incoming@patchwork.ozlabs.org; Mon, 15 Jun 2015 11:18:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55428) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z4W7W-0004x3-Nn for qemu-devel@nongnu.org; Mon, 15 Jun 2015 11:16:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z4W7T-0004dG-7K for qemu-devel@nongnu.org; Mon, 15 Jun 2015 11:16:18 -0400 Received: from greensocs.com ([193.104.36.180]:49215) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z4W7S-0004cp-OC for qemu-devel@nongnu.org; Mon, 15 Jun 2015 11:16:15 -0400 Received: from localhost (localhost [127.0.0.1]) by greensocs.com (Postfix) with ESMTP id 3B6251CE9BF; Mon, 15 Jun 2015 17:16:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=greensocs.com; s=mail; t=1434381374; bh=yLbr2JukLw5OaiwlzYVSv5lhsKuxqBuxussRZ8KMeIg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Hu3ST2krKA2zxVbFfD3nh+x3MUo8gQuBObgXeYFh1caEHmQagAEUmOEB5r1o8PTD8 Fz6lRjncal3ztP/ZDjPSL6LKn9AgSbczll9f4OKNXfPxYfqMn2xdgD17ey25Yjlil5 U1iYC/rFU3PixnJoDKZX0+KUXp2ugzVEv6/IrnYA= X-Virus-Scanned: amavisd-new at greensocs.com Authentication-Results: gs-01.greensocs.com (amavisd-new); dkim=pass (1024-bit key) header.d=greensocs.com Received: from greensocs.com ([127.0.0.1]) by localhost (gs-01.greensocs.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id URglpgGXREa1; Mon, 15 Jun 2015 17:16:06 +0200 (CEST) Received: from localhost.localdomain (i16-les03-th2-31-36-202-29.sfr.lns.abo.bbox.fr [31.36.202.29]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: fred.konrad@greensocs.com) by greensocs.com (Postfix) with ESMTPSA id 02AC81CE9C6; Mon, 15 Jun 2015 17:16:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=greensocs.com; s=mail; t=1434381363; bh=yLbr2JukLw5OaiwlzYVSv5lhsKuxqBuxussRZ8KMeIg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=xm9W8FvP80ug4D+higYC+KKP7PHqqinAKzXlWo3giJysriYNs6R5SM16ominsIuyT fVJHUOojGR5O4i4q9kHn08RoNhwP/aNiGdmI4iGNMzCTEzV4viart0oCfjS278hVNE siO2tGTUgqTaYh4L/dA0mH+I7YXXL2mezpyMo5JI= From: fred.konrad@greensocs.com To: qemu-devel@nongnu.org Date: Mon, 15 Jun 2015 17:15:41 +0200 Message-Id: <1434381343-7583-6-git-send-email-fred.konrad@greensocs.com> X-Mailer: git-send-email 1.9.0 In-Reply-To: <1434381343-7583-1-git-send-email-fred.konrad@greensocs.com> References: <1434381343-7583-1-git-send-email-fred.konrad@greensocs.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 193.104.36.180 Cc: peter.maydell@linaro.org, peter.crosthwaite@xilinx.com, hyunk@xilinx.com, mark.burton@greensocs.com, guillaume.delbergue@greensocs.com, fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH V2 5/7] Introduce xilinx dpdma. 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: KONRAD Frederic This is the implementation of the DPDMA. Signed-off-by: KONRAD Frederic --- hw/dma/Makefile.objs | 1 + hw/dma/xilinx_dpdma.c | 779 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/dma/xilinx_dpdma.h | 71 +++++ 3 files changed, 851 insertions(+) create mode 100644 hw/dma/xilinx_dpdma.c create mode 100644 hw/dma/xilinx_dpdma.h diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index 0e65ed0..a9934c5 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o +obj-$(CONFIG_XLNX_ZYNQMP) += xilinx_dpdma.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o diff --git a/hw/dma/xilinx_dpdma.c b/hw/dma/xilinx_dpdma.c new file mode 100644 index 0000000..50c5919 --- /dev/null +++ b/hw/dma/xilinx_dpdma.c @@ -0,0 +1,779 @@ +/* + * xilinx_dpdma.c + * + * Copyright (C) 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * 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, see . + * + */ + +#include "xilinx_dpdma.h" + +#ifndef DEBUG_DPDMA +#define DEBUG_DPDMA 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_DPDMA) { \ + qemu_log("xilinx_dpdma: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +/* + * Registers offset for DPDMA. + */ +#define DPDMA_ERR_CTRL (0x0000) +#define DPDMA_ISR (0x0004 >> 2) +#define DPDMA_IMR (0x0008 >> 2) +#define DPDMA_IEN (0x000C >> 2) +#define DPDMA_IDS (0x0010 >> 2) +#define DPDMA_EISR (0x0014 >> 2) +#define DPDMA_EIMR (0x0018 >> 2) +#define DPDMA_EIEN (0x001C >> 2) +#define DPDMA_EIDS (0x0020 >> 2) +#define DPDMA_CNTL (0x0100 >> 2) +#define DPDMA_GBL (0x0104 >> 2) +#define DPDMA_GBL_TRG_CH(n) (1 << n) +#define DPDMA_GBL_RTRG_CH(n) (1 << 6 << n) +#define DPDMA_ALC0_CNTL (0x0108 >> 2) +#define DPDMA_ALC0_STATUS (0x010C >> 2) +#define DPDMA_ALC0_MAX (0x0110 >> 2) +#define DPDMA_ALC0_MIN (0x0114 >> 2) +#define DPDMA_ALC0_ACC (0x0118 >> 2) +#define DPDMA_ALC0_ACC_TRAN (0x011C >> 2) +#define DPDMA_ALC1_CNTL (0x0120 >> 2) +#define DPDMA_ALC1_STATUS (0x0124 >> 2) +#define DPDMA_ALC1_MAX (0x0128 >> 2) +#define DPDMA_ALC1_MIN (0x012C >> 2) +#define DPDMA_ALC1_ACC (0x0130 >> 2) +#define DPDMA_ALC1_ACC_TRAN (0x0134 >> 2) +#define DPDMA_DSCR_STRT_ADDRE_CH(n) ((0x0200 + n * 0x100) >> 2) +#define DPDMA_DSCR_STRT_ADDR_CH(n) ((0x0204 + n * 0x100) >> 2) +#define DPDMA_DSCR_NEXT_ADDRE_CH(n) ((0x0208 + n * 0x100) >> 2) +#define DPDMA_DSCR_NEXT_ADDR_CH(n) ((0x020C + n * 0x100) >> 2) +#define DPDMA_PYLD_CUR_ADDRE_CH(n) ((0x0210 + n * 0x100) >> 2) +#define DPDMA_PYLD_CUR_ADDR_CH(n) ((0x0214 + n * 0x100) >> 2) +#define DPDMA_CNTL_CH(n) ((0x0218 + n * 0x100) >> 2) +#define DPDMA_CNTL_CH_EN (1) +#define DPDMA_CNTL_CH_PAUSED (1 << 1) +#define DPDMA_STATUS_CH(n) ((0x021C + n * 0x100) >> 2) +#define DPDMA_STATUS_BURST_TYPE (1 << 4) +#define DPDMA_STATUS_MODE (1 << 5) +#define DPDMA_STATUS_EN_CRC (1 << 6) +#define DPDMA_STATUS_LAST_DSCR (1 << 7) +#define DPDMA_STATUS_LDSCR_FRAME (1 << 8) +#define DPDMA_STATUS_IGNR_DONE (1 << 9) +#define DPDMA_STATUS_DSCR_DONE (1 << 10) +#define DPDMA_STATUS_EN_DSCR_UP (1 << 11) +#define DPDMA_STATUS_EN_DSCR_INTR (1 << 12) +#define DPDMA_STATUS_PREAMBLE_OFF (13) +#define DPDMA_VDO_CH(n) ((0x0220 + n * 0x100) >> 2) +#define DPDMA_PYLD_SZ_CH(n) ((0x0224 + n * 0x100) >> 2) +#define DPDMA_DSCR_ID_CH(n) ((0x0228 + n * 0x100) >> 2) + +/* + * Descriptor control field. + */ +#define CONTROL_PREAMBLE_VALUE 0xA5 + +#define CONTROL_PREAMBLE 0xFF +#define EN_DSCR_DONE_INTR (1 << 8) +#define EN_DSCR_UPDATE (1 << 9) +#define IGNORE_DONE (1 << 10) +#define AXI_BURST_TYPE (1 << 11) +#define AXCACHE (0x0F << 12) +#define AXPROT (0x2 << 16) +#define DESCRIPTOR_MODE (1 << 18) +#define LAST_DESCRIPTOR (1 << 19) +#define ENABLE_CRC (1 << 20) +#define LAST_DESCRIPTOR_OF_FRAME (1 << 21) + +/* + * Descriptor timestamp field. + */ +#define STATUS_DONE (1 << 31) + +#define DPDMA_FRAG_MAX_SZ (4096) + +typedef enum DPDMABurstType { + DPDMA_INCR = 0, + DPDMA_FIXED = 1 +} DPDMABurstType; + +typedef enum DPDMAMode { + DPDMA_CONTIGOUS = 0, + DPDMA_FRAGMENTED = 1 +} DPDMAMode; + +typedef struct DPDMADescriptor { + uint32_t control; + uint32_t descriptor_id; + /* transfer size in byte. */ + uint32_t xfer_size; + uint32_t line_size_stride; + uint32_t timestamp_lsb; + uint32_t timestamp_msb; + /* contains extension for both descriptor and source. */ + uint32_t address_extension; + uint32_t next_descriptor; + uint32_t source_address; + uint32_t address_extension_23; + uint32_t address_extension_45; + uint32_t source_address2; + uint32_t source_address3; + uint32_t source_address4; + uint32_t source_address5; + uint32_t crc; +} DPDMADescriptor; + +static bool xilinx_dpdma_desc_is_last(DPDMADescriptor *desc) +{ + return ((desc->control & LAST_DESCRIPTOR) != 0); +} + +static bool xilinx_dpdma_desc_is_last_of_frame(DPDMADescriptor *desc) +{ + return ((desc->control & LAST_DESCRIPTOR_OF_FRAME) != 0); +} + +static uint64_t xilinx_dpdma_desc_get_source_address(DPDMADescriptor *desc, + uint8_t frag) +{ + uint64_t addr = 0; + assert(frag < 5); + + switch (frag) { + case 0: + addr = desc->source_address + + (extract32(desc->address_extension, 16, 12) << 20); + break; + case 1: + addr = desc->source_address2 + + (extract32(desc->address_extension_23, 0, 12) << 8); + break; + case 2: + addr = desc->source_address3 + + (extract32(desc->address_extension_23, 16, 12) << 20); + break; + case 3: + addr = desc->source_address4 + + (extract32(desc->address_extension_45, 0, 12) << 8); + break; + case 4: + addr = desc->source_address5 + + (extract32(desc->address_extension_45, 16, 12) << 20); + break; + default: + addr = 0; + break; + } + + return addr; +} + +static uint32_t xilinx_dpdma_desc_get_transfer_size(DPDMADescriptor *desc) +{ + return desc->xfer_size; +} + +static uint32_t xilinx_dpdma_desc_get_line_size(DPDMADescriptor *desc) +{ + return extract32(desc->line_size_stride, 0, 18); +} + +static uint32_t xilinx_dpdma_desc_get_line_stride(DPDMADescriptor *desc) +{ + return extract32(desc->line_size_stride, 18, 14) * 16; +} + +static inline bool xilinx_dpdma_desc_crc_enabled(DPDMADescriptor *desc) +{ + return (desc->control & ENABLE_CRC) != 0; +} + +static inline bool xilinx_dpdma_desc_check_crc(DPDMADescriptor *desc) +{ + uint32_t *p = (uint32_t *)desc; + uint32_t crc = 0; + uint8_t i; + + /* + * CRC is calculated on the whole descriptor except the last 32bits word + * using 32bits addition. + */ + for (i = 0; i < 15; i++) { + crc += p[i]; + } + + return crc == desc->crc; +} + +static inline bool xilinx_dpdma_desc_completion_interrupt(DPDMADescriptor *desc) +{ + return (desc->control & EN_DSCR_DONE_INTR) != 0; +} + +static inline bool xilinx_dpdma_desc_is_valid(DPDMADescriptor *desc) +{ + return (desc->control & CONTROL_PREAMBLE) == CONTROL_PREAMBLE_VALUE; +} + +static inline bool xilinx_dpdma_desc_is_contiguous(DPDMADescriptor *desc) +{ + return (desc->control & DESCRIPTOR_MODE) == 0; +} + +static inline bool xilinx_dpdma_desc_update_enabled(DPDMADescriptor *desc) +{ + return (desc->control & EN_DSCR_UPDATE) != 0; +} + +static inline void xilinx_dpdma_desc_set_done(DPDMADescriptor *desc) +{ + desc->timestamp_msb |= STATUS_DONE; +} + +static inline bool xilinx_dpdma_desc_is_already_done(DPDMADescriptor *desc) +{ + return (desc->timestamp_msb & STATUS_DONE) != 0; +} + +static inline bool xilinx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc) +{ + return (desc->control & IGNORE_DONE) != 0; +} + +static const VMStateDescription vmstate_xilinx_dpdma = { + .name = TYPE_XILINX_DPDMA, + .version_id = 1, + .fields = (VMStateField[]) { + + VMSTATE_END_OF_LIST() + } +}; + +static void xilinx_dpdma_update_irq(XilinxDPDMAState *s) +{ + uint32_t flags; + + flags = ((s->registers[DPDMA_ISR] & (~s->registers[DPDMA_IMR])) + | (s->registers[DPDMA_EISR] & (~s->registers[DPDMA_EIMR]))); + qemu_set_irq(s->irq, flags != 0); +} + +static uint64_t xilinx_dpdma_descriptor_start_address(XilinxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_DSCR_STRT_ADDRE_CH(channel)] << 16) + + s->registers[DPDMA_DSCR_STRT_ADDR_CH(channel)]; +} + +static uint64_t xilinx_dpdma_descriptor_next_address(XilinxDPDMAState *s, + uint8_t channel) +{ + return ((uint64_t)s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] << 32) + + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)]; +} + +static inline void xilinx_dpdma_set_desc_next_address(XilinxDPDMAState *s, + uint8_t channel, + uint64_t addr) +{ + s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] = extract64(addr, 32, 32); + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = extract64(addr, 0, 32); +} + +static bool xilinx_dpdma_is_channel_enabled(XilinxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_EN) != 0; +} + +static bool xilinx_dpdma_is_channel_paused(XilinxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_PAUSED) != 0; +} + +static inline bool xilinx_dpdma_is_channel_retriggered(XilinxDPDMAState *s, + uint8_t channel) +{ + /* Clear the retriggered bit after reading it. */ + bool channel_is_retriggered = s->registers[DPDMA_GBL] + & DPDMA_GBL_RTRG_CH(channel); + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_RTRG_CH(channel); + return channel_is_retriggered; +} + +static inline bool xilinx_dpdma_is_channel_triggered(XilinxDPDMAState *s, + uint8_t channel) +{ + return s->registers[DPDMA_GBL] & DPDMA_GBL_TRG_CH(channel); +} + +static void xilinx_dpdma_update_desc_info(XilinxDPDMAState *s, uint8_t channel, + DPDMADescriptor *desc) +{ + s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] = + extract32(desc->address_extension, 0, 16); + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = desc->next_descriptor; + s->registers[DPDMA_PYLD_CUR_ADDRE_CH(channel)] = + extract32(desc->address_extension, 16, 16); + s->registers[DPDMA_PYLD_CUR_ADDR_CH(channel)] = desc->source_address; + s->registers[DPDMA_VDO_CH(channel)] = + extract32(desc->line_size_stride, 18, 14) + + (extract32(desc->line_size_stride, 0, 18) + << 14); + s->registers[DPDMA_PYLD_SZ_CH(channel)] = desc->xfer_size; + s->registers[DPDMA_DSCR_ID_CH(channel)] = desc->descriptor_id; + + /* Compute the status register with the descriptor information. */ + s->registers[DPDMA_STATUS_CH(channel)] = + extract32(desc->control, 0, 8) << 13; + if ((desc->control & EN_DSCR_DONE_INTR) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_INTR; + } + if ((desc->control & EN_DSCR_UPDATE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_UP; + } + if ((desc->timestamp_msb & STATUS_DONE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_DSCR_DONE; + } + if ((desc->control & IGNORE_DONE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_IGNR_DONE; + } + if ((desc->control & LAST_DESCRIPTOR_OF_FRAME) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LDSCR_FRAME; + } + if ((desc->control & LAST_DESCRIPTOR) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LAST_DSCR; + } + if ((desc->control & ENABLE_CRC) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_CRC; + } + if ((desc->control & DESCRIPTOR_MODE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_MODE; + } + if ((desc->control & AXI_BURST_TYPE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_BURST_TYPE; + } +} + +#ifdef DEBUG_DPDMA +static void xilinx_dpdma_dump_descriptor(DPDMADescriptor *desc) +{ + uint8_t *p = (uint8_t *)desc; + size_t i; + + qemu_log("DUMP DESCRIPTOR:\n"); + for (i = 0; i < 64; i++) { + qemu_log(" %" PRIx8, *p++); + if (((i + 1) % 4) == 0) { + qemu_log("\n"); + } + } +} +#endif + +static uint64_t xilinx_dpdma_read(void *opaque, hwaddr offset, + unsigned size) +{ + XilinxDPDMAState *s = XILINX_DPDMA(opaque); + + DPRINTF("read @%" HWADDR_PRIx "\n", offset); + offset = offset >> 2; + + switch (offset) { + /* + * Trying to read a write only register. + */ + case DPDMA_GBL: + return 0; + default: + assert(offset <= (0xFFC >> 2)); + return s->registers[offset]; + } + return 0; +} + +static void xilinx_dpdma_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XilinxDPDMAState *s = XILINX_DPDMA(opaque); + + DPRINTF("write @%" HWADDR_PRIx " = %" PRIx64 "\n", offset, value); + offset = offset >> 2; + + switch (offset) { + case DPDMA_ISR: + s->registers[DPDMA_ISR] &= ~value; + xilinx_dpdma_update_irq(s); + break; + case DPDMA_IEN: + value = ~value; + s->registers[DPDMA_IMR] &= value; + break; + case DPDMA_IDS: + s->registers[DPDMA_IMR] |= value; + break; + case DPDMA_EISR: + value = ~value; + s->registers[DPDMA_EISR] &= value; + xilinx_dpdma_update_irq(s); + break; + case DPDMA_EIEN: + value = ~value; + s->registers[DPDMA_EIMR] &= value; + break; + case DPDMA_EIDS: + s->registers[DPDMA_EIMR] |= value; + break; + case DPDMA_IMR: + case DPDMA_EIMR: + case DPDMA_DSCR_NEXT_ADDRE_CH(0): + case DPDMA_DSCR_NEXT_ADDRE_CH(1): + case DPDMA_DSCR_NEXT_ADDRE_CH(2): + case DPDMA_DSCR_NEXT_ADDRE_CH(3): + case DPDMA_DSCR_NEXT_ADDRE_CH(4): + case DPDMA_DSCR_NEXT_ADDRE_CH(5): + case DPDMA_DSCR_NEXT_ADDR_CH(0): + case DPDMA_DSCR_NEXT_ADDR_CH(1): + case DPDMA_DSCR_NEXT_ADDR_CH(2): + case DPDMA_DSCR_NEXT_ADDR_CH(3): + case DPDMA_DSCR_NEXT_ADDR_CH(4): + case DPDMA_DSCR_NEXT_ADDR_CH(5): + case DPDMA_PYLD_CUR_ADDRE_CH(0): + case DPDMA_PYLD_CUR_ADDRE_CH(1): + case DPDMA_PYLD_CUR_ADDRE_CH(2): + case DPDMA_PYLD_CUR_ADDRE_CH(3): + case DPDMA_PYLD_CUR_ADDRE_CH(4): + case DPDMA_PYLD_CUR_ADDRE_CH(5): + case DPDMA_PYLD_CUR_ADDR_CH(0): + case DPDMA_PYLD_CUR_ADDR_CH(1): + case DPDMA_PYLD_CUR_ADDR_CH(2): + case DPDMA_PYLD_CUR_ADDR_CH(3): + case DPDMA_PYLD_CUR_ADDR_CH(4): + case DPDMA_PYLD_CUR_ADDR_CH(5): + case DPDMA_STATUS_CH(0): + case DPDMA_STATUS_CH(1): + case DPDMA_STATUS_CH(2): + case DPDMA_STATUS_CH(3): + case DPDMA_STATUS_CH(4): + case DPDMA_STATUS_CH(5): + case DPDMA_VDO_CH(0): + case DPDMA_VDO_CH(1): + case DPDMA_VDO_CH(2): + case DPDMA_VDO_CH(3): + case DPDMA_VDO_CH(4): + case DPDMA_VDO_CH(5): + case DPDMA_PYLD_SZ_CH(0): + case DPDMA_PYLD_SZ_CH(1): + case DPDMA_PYLD_SZ_CH(2): + case DPDMA_PYLD_SZ_CH(3): + case DPDMA_PYLD_SZ_CH(4): + case DPDMA_PYLD_SZ_CH(5): + case DPDMA_DSCR_ID_CH(0): + case DPDMA_DSCR_ID_CH(1): + case DPDMA_DSCR_ID_CH(2): + case DPDMA_DSCR_ID_CH(3): + case DPDMA_DSCR_ID_CH(4): + case DPDMA_DSCR_ID_CH(5): + /* + * Trying to write to a read only register.. + */ + break; + case DPDMA_GBL: + /* + * This is a write only register so it's read as zero in the read + * callback. + * We store the value anyway so we can know if the channel is + * enabled. + */ + s->registers[offset] |= value & 0x00000FFF; + break; + case DPDMA_DSCR_STRT_ADDRE_CH(0): + case DPDMA_DSCR_STRT_ADDRE_CH(1): + case DPDMA_DSCR_STRT_ADDRE_CH(2): + case DPDMA_DSCR_STRT_ADDRE_CH(3): + case DPDMA_DSCR_STRT_ADDRE_CH(4): + case DPDMA_DSCR_STRT_ADDRE_CH(5): + value &= 0x0000FFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(0): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(0); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(1): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(1); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(2): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(2); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(3): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(3); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(4): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(4); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(5): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(5); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + default: + assert(offset <= (0xFFC >> 2)); + s->registers[offset] = value; + break; + } +} + +static const MemoryRegionOps dma_ops = { + .read = xilinx_dpdma_read, + .write = xilinx_dpdma_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void xilinx_dpdma_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XilinxDPDMAState *s = XILINX_DPDMA(obj); + + memory_region_init_io(&s->iomem, obj, &dma_ops, s, + TYPE_XILINX_DPDMA, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void xilinx_dpdma_reset(DeviceState *dev) +{ + XilinxDPDMAState *s = XILINX_DPDMA(dev); + + memset(s->registers, 0, sizeof(s->registers)); + s->registers[DPDMA_IMR] = 0x07FFFFFF; + s->registers[DPDMA_EIMR] = 0xFFFFFFFF; + s->registers[DPDMA_ALC0_MIN] = 0x0000FFFF; + s->registers[DPDMA_ALC1_MIN] = 0x0000FFFF; +} + +static void xilinx_dpdma_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->vmsd = &vmstate_xilinx_dpdma; + dc->reset = xilinx_dpdma_reset; +} + +static const TypeInfo xilinx_dpdma_info = { + .name = TYPE_XILINX_DPDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XilinxDPDMAState), + .instance_init = xilinx_dpdma_init, + .class_init = xilinx_dpdma_class_init, +}; + +static void xilinx_dpdma_register_types(void) +{ + type_register_static(&xilinx_dpdma_info); +} + +size_t xilinx_dpdma_start_operation(XilinxDPDMAState *s, uint8_t channel, + bool one_desc) +{ + uint64_t desc_addr; + uint64_t source_addr[6]; + DPDMADescriptor desc; + bool done = false; + size_t ptr = 0; + + assert(channel <= 5); + + /* Trigger a VSYNC IRQ when the graphic callback asks for data. */ + if (channel == 3) { + s->registers[DPDMA_ISR] |= (1 << 27); + xilinx_dpdma_update_irq(s); + } + + DPRINTF("dpdma_start_channel() on channel %u\n", channel); + + if (!xilinx_dpdma_is_channel_triggered(s, channel)) { + DPRINTF("Channel isn't triggered..\n"); + return 0; + } + + if (!xilinx_dpdma_is_channel_enabled(s, channel)) { + DPRINTF("Channel isn't enabled..\n"); + return 0; + } + + if (xilinx_dpdma_is_channel_paused(s, channel)) { + DPRINTF("Channel is paused..\n"); + return 0; + } + + do { + if ((s->operation_finished[channel]) + || xilinx_dpdma_is_channel_retriggered(s, channel)) { + desc_addr = xilinx_dpdma_descriptor_start_address(s, channel); + s->operation_finished[channel] = false; + } else { + desc_addr = xilinx_dpdma_descriptor_next_address(s, channel); + } + + if (dma_memory_read(&address_space_memory, desc_addr, &desc, + sizeof(DPDMADescriptor))) { + s->registers[DPDMA_EISR] |= ((1 << 1) << channel); + xilinx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Can't get the descriptor.\n"); + break; + } + + xilinx_dpdma_update_desc_info(s, channel, &desc); + +#ifdef DEBUG_DPDMA + xilinx_dpdma_dump_descriptor(&desc); +#endif + + DPRINTF("location of the descriptor: %" PRIx64 "\n", desc_addr); + if (!xilinx_dpdma_desc_is_valid(&desc)) { + s->registers[DPDMA_EISR] |= ((1 << 7) << channel); + xilinx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Invalid descriptor..\n"); + break; + } + + if (xilinx_dpdma_desc_crc_enabled(&desc) + && !xilinx_dpdma_desc_check_crc(&desc)) { + s->registers[DPDMA_EISR] |= ((1 << 13) << channel); + xilinx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Bad CRC for descriptor..\n"); + break; + } + + if (xilinx_dpdma_desc_is_already_done(&desc) + && !xilinx_dpdma_desc_ignore_done_bit(&desc)) { + /* We are trying to process an already processed descriptor. */ + s->registers[DPDMA_EISR] |= ((1 << 25) << channel); + xilinx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Already processed descriptor..\n"); + break; + } + + done = xilinx_dpdma_desc_is_last(&desc) + || xilinx_dpdma_desc_is_last_of_frame(&desc); + + s->operation_finished[channel] = done; + if (s->data[channel]) { + int64_t transfer_len = + xilinx_dpdma_desc_get_transfer_size(&desc); + uint32_t line_size = xilinx_dpdma_desc_get_line_size(&desc); + uint32_t line_stride = xilinx_dpdma_desc_get_line_stride(&desc); + if (xilinx_dpdma_desc_is_contiguous(&desc)) { + source_addr[0] = + xilinx_dpdma_desc_get_source_address(&desc, 0); + while (transfer_len != 0) { + if (dma_memory_read(&address_space_memory, + source_addr[0], + &s->data[channel][ptr], + line_size)) { + s->registers[DPDMA_ISR] |= ((1 << 12) << channel); + xilinx_dpdma_update_irq(s); + DPRINTF("Can't get data.\n"); + break; + } + ptr += line_size; + transfer_len -= line_size; + source_addr[0] += line_stride; + } + } else { + DPRINTF("Source address:\n"); + int frag; + for (frag = 0; frag < 5; frag++) { + source_addr[frag] = + xilinx_dpdma_desc_get_source_address(&desc, frag); + DPRINTF("Fragment %u: %" PRIx64 "\n", frag + 1, + source_addr[frag]); + } + + frag = 0; + while ((transfer_len < 0) && (frag < 5)) { + size_t fragment_len = DPDMA_FRAG_MAX_SZ + - (source_addr[frag] % DPDMA_FRAG_MAX_SZ); + + if (dma_memory_read(&address_space_memory, + source_addr[frag], + &(s->data[channel][ptr]), + fragment_len)) { + s->registers[DPDMA_ISR] |= ((1 << 12) << channel); + xilinx_dpdma_update_irq(s); + DPRINTF("Can't get data.\n"); + break; + } + ptr += fragment_len; + transfer_len -= fragment_len; + frag += 1; + } + } + } + + if (xilinx_dpdma_desc_update_enabled(&desc)) { + /* The descriptor need to be updated when it's completed. */ + DPRINTF("update the descriptor with the done flag set.\n"); + xilinx_dpdma_desc_set_done(&desc); + dma_memory_write(&address_space_memory, desc_addr, &desc, + sizeof(DPDMADescriptor)); + } + + if (xilinx_dpdma_desc_completion_interrupt(&desc)) { + DPRINTF("completion interrupt enabled!\n"); + s->registers[DPDMA_ISR] |= (1 << channel); + xilinx_dpdma_update_irq(s); + } + + } while (!done && !one_desc); + + return ptr; +} + +void xilinx_dpdma_set_host_data_location(XilinxDPDMAState *s, uint8_t channel, + void *p) +{ + if (!s) { + qemu_log_mask(LOG_UNIMP, "DPDMA client not attached to valid DPDMA" + " instance\n"); + return; + } + + assert(channel <= 5); + s->data[channel] = p; +} + +type_init(xilinx_dpdma_register_types) diff --git a/hw/dma/xilinx_dpdma.h b/hw/dma/xilinx_dpdma.h new file mode 100644 index 0000000..f92167d --- /dev/null +++ b/hw/dma/xilinx_dpdma.h @@ -0,0 +1,71 @@ +/* + * xilinx_dpdma.h + * + * Copyright (C) 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * 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, see . + * + */ + +#ifndef XILINX_DPDMA_H +#define XILINX_DPDMA_H + +#include "hw/sysbus.h" +#include "ui/console.h" +#include "sysemu/dma.h" + +struct XilinxDPDMAState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t registers[0x1000 >> 2]; + uint8_t *data[6]; + bool operation_finished[6]; + qemu_irq irq; +}; + +typedef struct XilinxDPDMAState XilinxDPDMAState; + +#define TYPE_XILINX_DPDMA "xlnx.dpdma" +#define XILINX_DPDMA(obj) OBJECT_CHECK(XilinxDPDMAState, (obj), \ + TYPE_XILINX_DPDMA) + +/* + * \func xilinx_dpdma_start_operation. + * \brief Start the operation on the specified channel. The DPDMA get the + * current descriptor and retrieve data to the buffer specified by + * dpdma_set_host_data_location. + * \arg s The DPDMA instance. + * \arg channel The channel to start. + * \return the number of byte transfered by the DPDMA or 0 if an error occured. + */ +size_t xilinx_dpdma_start_operation(XilinxDPDMAState *s, uint8_t channel, + bool one_desc); + +/* + * \func xilinx_dpdma_set_host_data_location. + * \brief Set the location in the host memory where to store the data out from + * the dma channel. + * \arg s The DPDMA instance. + * \arg channel The channel associated to the pointer. + * \arg p The buffer where to store the data. + */ +/* XXX: add a maximum size arg and send an interrupt in case of overflow. */ +void xilinx_dpdma_set_host_data_location(XilinxDPDMAState *s, uint8_t channel, + void *p); + +#endif /* !XILINX_DPDMA_H */