From patchwork Sun Nov 26 23:16:28 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Francisco Iglesias X-Patchwork-Id: 10075631 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B0F9260353 for ; Sun, 26 Nov 2017 23:25:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A0B4C28AA6 for ; Sun, 26 Nov 2017 23:25:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 927DC28B26; Sun, 26 Nov 2017 23:25:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A0DC328AA6 for ; Sun, 26 Nov 2017 23:25:06 +0000 (UTC) Received: from localhost ([::1]:58466 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eJ6IL-0003dK-TC for patchwork-qemu-devel@patchwork.kernel.org; Sun, 26 Nov 2017 18:25:05 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50241) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eJ6AP-0004Zr-AM for qemu-devel@nongnu.org; Sun, 26 Nov 2017 18:16:54 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eJ6AN-0000tA-KT for qemu-devel@nongnu.org; Sun, 26 Nov 2017 18:16:53 -0500 Received: from mail-lf0-x244.google.com ([2a00:1450:4010:c07::244]:36817) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eJ6AN-0000st-86 for qemu-devel@nongnu.org; Sun, 26 Nov 2017 18:16:51 -0500 Received: by mail-lf0-x244.google.com with SMTP id k66so30675151lfg.3 for ; Sun, 26 Nov 2017 15:16:51 -0800 (PST) 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=5KTvL3yzFhefNBxPweHDGqlsSJG5GWQCqLGGqN9oGLQ=; b=cUE6YN7peiIEfKFcmhvjOgU7835LkNlWx2Cuxd+GBqJkJQYn3xDbjl+FxuI3it9Bj0 I+dIVHVfBfzEzmtaabAug6CmuqzjMMY44CNbSh3mPD+r5LPvAD2UAR/BVSLtiaJZ13WO z7LFX8VWFbLlm/Kv6+l98YanX1BfpW44soCbHK+6NKqPTF5wZbz+nAlxyO5jZqMFQ/Kh 2Kqekm3x07Vwr2/ffw0OVnHL/8ylscvAxsCmpzE4Y2c4/DE5nVqn0nph+t28OrtEpsL/ Vzm5cZkEawhOyKkfyydIsGntcJQWLKIXIXWysNBXVav1zpUWQ8sPIkD2ivG05B+iaclh 7PGQ== 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=5KTvL3yzFhefNBxPweHDGqlsSJG5GWQCqLGGqN9oGLQ=; b=n22K1c8KPRXbXC/47/Xto+cfc7T9xmyyrVFdO5emzLufrb7yapzMvivDtOxuAmhp5+ OrepUc/KnOQ0f03/LOjh0QSkGyjeStvWX8q2wrSSHzOBbRxjZgsHvgP/FAeHTtZqmIEL HLztbsP1p0k5cQ6UXeHgchO1YSLIxZmTu/gI/qnvVwYTemKDUjeDtY2l3G+yLUpV5UCb CnQqx5T3LpHkGwgoLrJsAyD0H0KUc+1hJZ3pcdSVHwgkDp1MjwGLvZUPRlBTgeVeVM0V rNO8ZENVhXsk7dyb69MLnh6go6gXoieTOjLZrMrivvCNKXe8aHSuHwi13KmB0FOdAKr4 S9QA== X-Gm-Message-State: AJaThX5uGFWJ3Y13LtNOEsF0OrADVic/aseS1Gmm0cwEiGlBC3kwDHBd 4wftpaiBa3nkjzZcWrT+i7veEg== X-Google-Smtp-Source: AGs4zMb85eAKgVE+pgRKi9B3xEfF1aAsmfOadaLjUK+wQHhgcKLEtkloGhl3MxiUaaV/ixf9ftdiPA== X-Received: by 10.46.85.18 with SMTP id j18mr11418155ljb.109.1511738209678; Sun, 26 Nov 2017 15:16:49 -0800 (PST) Received: from localhost.localdomain (host-90-232-123-128.mobileonline.telia.com. [90.232.123.128]) by smtp.gmail.com with ESMTPSA id j63sm2070285lfg.95.2017.11.26.15.16.48 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Nov 2017 15:16:48 -0800 (PST) From: Francisco Iglesias To: qemu-devel@nongnu.org Date: Mon, 27 Nov 2017 00:16:28 +0100 Message-Id: <20171126231634.9531-8-frasse.iglesias@gmail.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20171126231634.9531-1-frasse.iglesias@gmail.com> References: <20171126231634.9531-1-frasse.iglesias@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4010:c07::244 Subject: [Qemu-devel] [PATCH v9 07/13] xilinx_spips: Add support for RX discard and RX drain X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, francisco.iglesias@feimtech.se, f4bug@amsat.org, edgari@xilinx.com, alistai@xilinx.com, mar.krzeminski@gmail.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add support for the RX discard and RX drain functionality. Also transmit one byte per dummy cycle (to the flash memories) with commands that require these. Signed-off-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Tested-by: Edgar E. Iglesias --- hw/ssi/xilinx_spips.c | 167 +++++++++++++++++++++++++++++++++++++----- include/hw/ssi/xilinx_spips.h | 6 ++ 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 231aa5b..691d48d 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -30,6 +30,7 @@ #include "qemu/bitops.h" #include "hw/ssi/xilinx_spips.h" #include "qapi/error.h" +#include "hw/register.h" #include "migration/blocker.h" #ifndef XILINX_SPIPS_ERR_DEBUG @@ -100,6 +101,14 @@ #define LQSPI_CFG_DUMMY_SHIFT 8 #define LQSPI_CFG_INST_CODE 0xFF +#define R_CMND (0xc0 / 4) + #define R_CMND_RXFIFO_DRAIN (1 << 19) + FIELD(CMND, PARTIAL_BYTE_LEN, 16, 3) +#define R_CMND_EXT_ADD (1 << 15) + FIELD(CMND, RX_DISCARD, 8, 7) + FIELD(CMND, DUMMY_CYCLES, 2, 6) +#define R_CMND_DMA_EN (1 << 1) +#define R_CMND_PUSH_WAIT (1 << 0) #define R_LQSPI_STS (0xA4 / 4) #define LQSPI_STS_WR_RECVD (1 << 1) @@ -116,7 +125,8 @@ #define LQSPI_ADDRESS_BITS 24 #define SNOOP_CHECKING 0xFF -#define SNOOP_NONE 0xFE +#define SNOOP_ADDR 0xF0 +#define SNOOP_NONE 0xEE #define SNOOP_STRIPING 0 static inline int num_effective_busses(XilinxSPIPS *s) @@ -146,9 +156,14 @@ static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) if (xilinx_spips_cs_is_set(s, i, field) && !found) { DB_PRINT_L(0, "selecting slave %d\n", i); qemu_set_irq(s->cs_lines[cs_to_set], 0); + if (s->cs_lines_state[cs_to_set]) { + s->cs_lines_state[cs_to_set] = false; + s->rx_discard = ARRAY_FIELD_EX32(s->regs, CMND, RX_DISCARD); + } } else { DB_PRINT_L(0, "deselecting slave %d\n", i); qemu_set_irq(s->cs_lines[cs_to_set], 1); + s->cs_lines_state[cs_to_set] = true; } } if (xilinx_spips_cs_is_set(s, i, field)) { @@ -157,6 +172,10 @@ static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) } if (!found) { s->snoop_state = SNOOP_CHECKING; + s->cmd_dummies = 0; + s->link_state = 1; + s->link_state_next = 1; + s->link_state_next_when = 0; DB_PRINT_L(1, "moving to snoop check state\n"); } } @@ -203,7 +222,11 @@ static void xilinx_spips_reset(DeviceState *d) /* FIXME: move magic number definition somewhere sensible */ s->regs[R_MOD_ID] = 0x01090106; s->regs[R_LQSPI_CFG] = R_LQSPI_CFG_RESET; + s->link_state = 1; + s->link_state_next = 1; + s->link_state_next_when = 0; s->snoop_state = SNOOP_CHECKING; + s->cmd_dummies = 0; xilinx_spips_update_ixr(s); xilinx_spips_update_cs_lines(s); } @@ -238,14 +261,69 @@ static inline void stripe8(uint8_t *x, int num, bool dir) memcpy(x, r, sizeof(uint8_t) * num); } +static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command) +{ + if (!qs) { + /* The SPI device is not a QSPI device */ + return -1; + } + + switch (command) { /* check for dummies */ + case READ: /* no dummy bytes/cycles */ + case PP: + case DPP: + case QPP: + case READ_4: + case PP_4: + case QPP_4: + return 0; + case FAST_READ: + case DOR: + case QOR: + case DOR_4: + case QOR_4: + return 1; + case DIOR: + case FAST_READ_4: + case DIOR_4: + return 2; + case QIOR: + case QIOR_4: + return 5; + default: + return -1; + } +} + +static inline uint8_t get_addr_length(XilinxSPIPS *s, uint8_t cmd) +{ + switch (cmd) { + case PP_4: + case QPP_4: + case READ_4: + case QIOR_4: + case FAST_READ_4: + case DOR_4: + case QOR_4: + case DIOR_4: + return 4; + default: + return (s->regs[R_CMND] & R_CMND_EXT_ADD) ? 4 : 3; + } +} + static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) { int debug_level = 0; + XilinxQSPIPS *q = (XilinxQSPIPS *) object_dynamic_cast(OBJECT(s), + TYPE_XILINX_QSPIPS); for (;;) { int i; uint8_t tx = 0; uint8_t tx_rx[num_effective_busses(s)]; + uint8_t dummy_cycles = 0; + uint8_t addr_length; if (fifo8_is_empty(&s->tx_fifo)) { if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) { @@ -258,54 +336,102 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) tx_rx[i] = fifo8_pop(&s->tx_fifo); } stripe8(tx_rx, num_effective_busses(s), false); - } else { + } else if (s->snoop_state >= SNOOP_ADDR) { tx = fifo8_pop(&s->tx_fifo); for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = tx; } + } else { + /* Extract a dummy byte and generate dummy cycles according to the + * link state */ + tx = fifo8_pop(&s->tx_fifo); + dummy_cycles = 8 / s->link_state; } for (i = 0; i < num_effective_busses(s); ++i) { int bus = num_effective_busses(s) - 1 - i; - DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); - tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]); - DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); + if (dummy_cycles) { + int d; + for (d = 0; d < dummy_cycles; ++d) { + tx_rx[0] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[0]); + } + } else { + DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); + tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]); + DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); + } } - if (fifo8_is_full(&s->rx_fifo)) { + if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) { + DB_PRINT_L(debug_level, "dircarding drained rx byte\n"); + /* Do nothing */ + } else if (s->rx_discard) { + DB_PRINT_L(debug_level, "dircarding discarded rx byte\n"); + s->rx_discard -= 8 / s->link_state; + } else if (fifo8_is_full(&s->rx_fifo)) { s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW; DB_PRINT_L(0, "rx FIFO overflow"); } else if (s->snoop_state == SNOOP_STRIPING) { stripe8(tx_rx, num_effective_busses(s), true); for (i = 0; i < num_effective_busses(s); ++i) { fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[i]); + DB_PRINT_L(debug_level, "pushing striped rx byte\n"); } } else { + DB_PRINT_L(debug_level, "pushing unstriped rx byte\n"); fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]); } + if (s->link_state_next_when) { + s->link_state_next_when--; + if (!s->link_state_next_when) { + s->link_state = s->link_state_next; + } + } + DB_PRINT_L(debug_level, "initial snoop state: %x\n", (unsigned)s->snoop_state); switch (s->snoop_state) { case (SNOOP_CHECKING): - switch (tx) { /* new instruction code */ - case READ: /* 3 address bytes, no dummy bytes/cycles */ - case PP: + /* Store the count of dummy bytes in the txfifo */ + s->cmd_dummies = xilinx_spips_num_dummies(q, tx); + addr_length = get_addr_length(s, tx); + if (s->cmd_dummies < 0) { + s->snoop_state = SNOOP_NONE; + } else { + s->snoop_state = SNOOP_ADDR + addr_length - 1; + } + switch (tx) { case DPP: - case QPP: - s->snoop_state = 3; - break; - case FAST_READ: /* 3 address bytes, 1 dummy byte */ case DOR: + case DOR_4: + s->link_state_next = 2; + s->link_state_next_when = addr_length + s->cmd_dummies; + break; + case QPP: + case QPP_4: case QOR: - case DIOR: /* FIXME: these vary between vendor - set to spansion */ - s->snoop_state = 4; + case QOR_4: + s->link_state_next = 4; + s->link_state_next_when = addr_length + s->cmd_dummies; + break; + case DIOR: + case DIOR_4: + s->link_state = 2; break; - case QIOR: /* 3 address bytes, 2 dummy bytes */ - s->snoop_state = 6; + case QIOR: + case QIOR_4: + s->link_state = 4; break; - default: + } + break; + case (SNOOP_ADDR): + /* Address has been transmitted, transmit dummy cycles now if + * needed */ + if (s->cmd_dummies < 0) { s->snoop_state = SNOOP_NONE; + } else { + s->snoop_state = s->cmd_dummies; } break; case (SNOOP_STRIPING): @@ -483,6 +609,7 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { XilinxQSPIPS *q = XILINX_QSPIPS(opaque); + XilinxSPIPS *s = XILINX_SPIPS(opaque); xilinx_spips_write(opaque, addr, value, size); addr >>= 2; @@ -490,6 +617,9 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr, if (addr == R_LQSPI_CFG) { xilinx_qspips_invalidate_mmio_ptr(q); } + if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) { + fifo8_reset(&s->rx_fifo); + } } static const MemoryRegionOps qspips_ops = { @@ -632,6 +762,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp) } s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses); + s->cs_lines_state = g_new0(bool, s->num_cs * s->num_busses); for (i = 0, cs = s->cs_lines; i < s->num_busses; ++i, cs += s->num_cs) { ssi_auto_connect_slaves(DEVICE(s), cs, s->spi[i]); } diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index 7f9e2fc..bac90a5 100644 --- a/include/hw/ssi/xilinx_spips.h +++ b/include/hw/ssi/xilinx_spips.h @@ -61,13 +61,19 @@ struct XilinxSPIPS { uint8_t num_busses; uint8_t snoop_state; + int cmd_dummies; + uint8_t link_state; + uint8_t link_state_next; + uint8_t link_state_next_when; qemu_irq *cs_lines; + bool *cs_lines_state; SSIBus **spi; Fifo8 rx_fifo; Fifo8 tx_fifo; uint8_t num_txrx_bytes; + uint32_t rx_discard; uint32_t regs[XLNX_SPIPS_R_MAX]; };