All of lore.kernel.org
 help / color / mirror / Atom feed
From: Francisco Iglesias <frasse.iglesias@gmail.com>
To: qemu-devel@nongnu.org
Cc: edgari@xilinx.com, alistai@xilinx.com, francisco.iglesias@feimtech.se
Subject: [Qemu-devel] [PATCH v2 11/12] xilinx_spips: Add support for the ZynqMP Generic QSPI
Date: Sat, 21 Oct 2017 23:54:19 +0200	[thread overview]
Message-ID: <20171021215420.19787-12-frasse.iglesias@gmail.com> (raw)
In-Reply-To: <20171021215420.19787-1-frasse.iglesias@gmail.com>

Add support for the Zynq Ultrascale MPSoc Generic QSPI.

Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
 default-configs/arm-softmmu.mak |   1 +
 hw/ssi/xilinx_spips.c           | 510 ++++++++++++++++++++++++++++++++++++----
 include/hw/ssi/xilinx_spips.h   |  31 ++-
 3 files changed, 494 insertions(+), 48 deletions(-)

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 5059d13..d09fd34 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -130,3 +130,4 @@ CONFIG_SMBIOS=y
 CONFIG_ASPEED_SOC=y
 CONFIG_GPIO_KEY=y
 CONFIG_MSF2=y
+CONFIG_XILINX_AXI=y
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index c360af7..0aaa5c8 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -31,6 +31,7 @@
 #include "hw/ssi/xilinx_spips.h"
 #include "qapi/error.h"
 #include "hw/register.h"
+#include "sysemu/dma.h"
 #include "migration/blocker.h"
 
 #ifndef XILINX_SPIPS_ERR_DEBUG
@@ -69,13 +70,31 @@
 #define R_INTR_DIS          (0x0C / 4)
 #define R_INTR_MASK         (0x10 / 4)
 #define IXR_TX_FIFO_UNDERFLOW   (1 << 6)
+/* Poll timeout not implemented */
+#define IXR_RX_FIFO_EMPTY       (1 << 11)
+#define IXR_GENERIC_FIFO_FULL   (1 << 10)
+#define IXR_GENERIC_FIFO_NOT_FULL (1 << 9)
+#define IXR_TX_FIFO_EMPTY       (1 << 8)
+#define IXR_GENERIC_FIFO_EMPTY  (1 << 7)
 #define IXR_RX_FIFO_FULL        (1 << 5)
 #define IXR_RX_FIFO_NOT_EMPTY   (1 << 4)
 #define IXR_TX_FIFO_FULL        (1 << 3)
 #define IXR_TX_FIFO_NOT_FULL    (1 << 2)
 #define IXR_TX_FIFO_MODE_FAIL   (1 << 1)
 #define IXR_RX_FIFO_OVERFLOW    (1 << 0)
-#define IXR_ALL                 ((IXR_TX_FIFO_UNDERFLOW<<1)-1)
+#define IXR_ALL                 ((1 << 13) - 1)
+#define GQSPI_IXR_MASK          0xFBE
+
+#define IXR_SELF_CLEAR \
+(IXR_GENERIC_FIFO_EMPTY \
+| IXR_GENERIC_FIFO_FULL  \
+| IXR_GENERIC_FIFO_NOT_FULL \
+| IXR_TX_FIFO_EMPTY \
+| IXR_TX_FIFO_FULL  \
+| IXR_TX_FIFO_NOT_FULL \
+| IXR_RX_FIFO_EMPTY \
+| IXR_RX_FIFO_FULL  \
+| IXR_RX_FIFO_NOT_EMPTY)
 
 #define R_EN                (0x14 / 4)
 #define R_DELAY             (0x18 / 4)
@@ -118,9 +137,63 @@
 
 #define R_MOD_ID            (0xFC / 4)
 
+#define R_GQSPI_SELECT          (0x144 / 4)
+    FIELD(GQSPI_SELECT, GENERIC_QSPI_EN, 0, 1)
+#define R_GQSPI_ISR         (0x104 / 4)
+#define R_GQSPI_IER         (0x108 / 4)
+#define R_GQSPI_IDR         (0x10c / 4)
+#define R_GQSPI_IMR         (0x110 / 4)
+#define R_GQSPI_TX_THRESH   (0x128 / 4)
+#define R_GQSPI_RX_THRESH   (0x12c / 4)
+
+#define R_GQSPI_CNFG        (0x100 / 4)
+    FIELD(GQSPI_CNFG, MODE_EN, 30, 2)
+    FIELD(GQSPI_CNFG, GEN_FIFO_START_MODE, 29, 1)
+    FIELD(GQSPI_CNFG, GEN_FIFO_START, 28, 1)
+    FIELD(GQSPI_CNFG, ENDIAN, 26, 1)
+    /* Poll timeout not implemented */
+    FIELD(GQSPI_CNFG, EN_POLL_TIMEOUT, 20, 1)
+    /* QEMU doesnt care about any of these last three */
+    FIELD(GQSPI_CNFG, BR, 3, 3)
+    FIELD(GQSPI_CNFG, CPH, 2, 1)
+    FIELD(GQSPI_CNFG, CPL, 1, 1)
+
+#define R_GQSPI_GEN_FIFO        (0x140 / 4)
+
+#define R_GQSPI_TXD             (0x11c / 4)
+#define R_GQSPI_RXD             (0x120 / 4)
+
+#define R_GQSPI_FIFO_CTRL       (0x14c / 4)
+    FIELD(GQSPI_FIFO_CTRL, RX_FIFO_RESET, 2, 1)
+    FIELD(GQSPI_FIFO_CTRL, TX_FIFO_RESET, 1, 1)
+    FIELD(GQSPI_FIFO_CTRL, GENERIC_FIFO_RESET, 0, 1)
+
+#define R_GQSPI_GFIFO_THRESH    (0x150 / 4)
+
+#define R_GQSPI_DATA_STS (0x15c / 4)
+
+/* We use the snapshot register to hold the core state for the currently
+ * or most recently executed command. So the generic fifo format is defined
+ * for the snapshot register
+ */
+#define R_GQSPI_GF_SNAPSHOT (0x160 / 4)
+    FIELD(GQSPI_GF_SNAPSHOT, POLL, 19, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, STRIPE, 18, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, RECIEVE, 17, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, TRANSMIT, 16, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT, 14, 2)
+    FIELD(GQSPI_GF_SNAPSHOT, CHIP_SELECT, 12, 2)
+    FIELD(GQSPI_GF_SNAPSHOT, SPI_MODE, 10, 2)
+    FIELD(GQSPI_GF_SNAPSHOT, EXPONENT, 9, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, DATA_XFER, 8, 1)
+    FIELD(GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA, 0, 8)
+
+#define R_GQSPI_MOD_ID        (0x168 / 4)
+#define R_GQSPI_MOD_ID_VALUE  0x010A0000
+
 /* size of TXRX FIFOs */
-#define RXFF_A          32
-#define TXFF_A          32
+#define RXFF_A          (128)
+#define TXFF_A          (128)
 
 #define RXFF_A_Q          (64 * 4)
 #define TXFF_A_Q          (64 * 4)
@@ -133,6 +206,7 @@
 #define SNOOP_NONE 0xEE
 #define SNOOP_STRIPING 0
 
+#define ZYNQMP_ONLY(a) ((zynqmp) ? (a) : (0))
 
 static inline int num_effective_busses(XilinxSPIPS *s)
 {
@@ -140,37 +214,61 @@ static inline int num_effective_busses(XilinxSPIPS *s)
             s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM) ? s->num_busses : 1;
 }
 
-static inline bool xilinx_spips_cs_is_set(XilinxSPIPS *s, int i, int field)
-{
-    return ~field & (1 << i) && (s->regs[R_CONFIG] & MANUAL_CS
-                    || !fifo8_is_empty(&s->tx_fifo));
+static void xilinx_spips_update_cs_lines_legacy_mangle(XilinxSPIPS *s,
+                                                       int *field) {
+    *field = ~((s->regs[R_CONFIG] & CS) >> CS_SHIFT);
+    /* In dual parallel, mirror low CS to both */
+    if (num_effective_busses(s) == 2) {
+        /* Single bit chip-select for qspi */
+        *field &= 0x1;
+        *field |= *field << 1;
+    /* Dual stack U-Page */
+    } else if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM &&
+               s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE) {
+        /* Single bit chip-select for qspi */
+        *field &= 0x1;
+        /* change from CS0 to CS1 */
+        *field <<= 1;
+    }
+    /* Auto CS */
+    if (!(s->regs[R_CONFIG] & MANUAL_CS) &&
+        fifo8_is_empty(&s->tx_fifo)) {
+        *field = 0;
+    }
+}
+
+static void xilinx_spips_update_cs_lines_generic_mangle(XilinxSPIPS *s,
+                                                        int *field) {
+    *field = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, CHIP_SELECT);
 }
 
 static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
 {
-    int i, j;
-    bool found = false;
-    int field = s->regs[R_CONFIG] >> CS_SHIFT;
+    int i;
+    int field = 0;
 
-    for (i = 0; i < s->num_cs; i++) {
-        for (j = 0; j < num_effective_busses(s); j++) {
-            int upage = !!(s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE);
-            int cs_to_set = (j * s->num_cs + i + upage) %
-                                (s->num_cs * s->num_busses);
-
-            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);
-            } else {
-                DB_PRINT_L(0, "deselecting slave %d\n", i);
-                qemu_set_irq(s->cs_lines[cs_to_set], 1);
-            }
+    if (!ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+        xilinx_spips_update_cs_lines_legacy_mangle(s, &field);
+    } else {
+        if (!s->regs[R_GQSPI_GF_SNAPSHOT]) {
+            return;
         }
-        if (xilinx_spips_cs_is_set(s, i, field)) {
-            found = true;
+        xilinx_spips_update_cs_lines_generic_mangle(s, &field);
+    }
+
+    for (i = 0; i < s->num_cs; i++) {
+        bool old_state = s->cs_lines_state[i];
+        bool new_state = field & (1 << i);
+
+        if (old_state != new_state) {
+            s->cs_lines_state[i] = new_state;
+            s->rx_discard = ARRAY_FIELD_EX32(s->regs, CMND, RX_DISCARD);
+            DB_PRINT_L(0, "%sselecting slave %d\n", new_state ? "" : "de", i);
         }
+        qemu_set_irq(s->cs_lines[i], !new_state);
     }
-    if (!found) {
+
+    if (!(field & ((1 << s->num_cs) - 1))) {
         s->snoop_state = SNOOP_CHECKING;
         s->cmd_dummies = 0;
         s->link_state = 1;
@@ -182,22 +280,53 @@ static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
 
 static void xilinx_spips_update_ixr(XilinxSPIPS *s)
 {
-    if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE) {
-        return;
+    int new_irqline;
+    uint32_t qspi_int, gqspi_int;
+    bool zynqmp = false;
+
+    if (object_dynamic_cast(OBJECT(s), TYPE_XLNX_ZYNQMP_QSPIPS)) {
+        zynqmp = true;
     }
-    /* These are set/cleared as they occur */
-    s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW |
-                                IXR_TX_FIFO_MODE_FAIL);
+
     /* these are pure functions of fifo state, set them here */
-    s->regs[R_INTR_STATUS] |=
-        (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
-        (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) |
-        (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
-        (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+    s->regs[R_GQSPI_ISR] &= ~IXR_SELF_CLEAR;
+    s->regs[R_GQSPI_ISR] |=
+        (fifo32_is_empty(&s->fifo_g) ? IXR_GENERIC_FIFO_EMPTY : 0) |
+        (fifo32_is_full(&s->fifo_g) ? IXR_GENERIC_FIFO_FULL : 0) |
+        (s->fifo_g.fifo.num < s->regs[R_GQSPI_GFIFO_THRESH] ?
+                                    IXR_GENERIC_FIFO_NOT_FULL : 0) |
+
+        (fifo8_is_empty(&s->rx_fifo_g) ? IXR_RX_FIFO_EMPTY : 0) |
+        (fifo8_is_full(&s->rx_fifo_g) ? IXR_RX_FIFO_FULL : 0) |
+        (s->rx_fifo_g.num >= s->regs[R_GQSPI_RX_THRESH] ?
+                                    IXR_RX_FIFO_NOT_EMPTY : 0) |
+
+        (fifo8_is_empty(&s->tx_fifo_g) ? IXR_TX_FIFO_EMPTY : 0) |
+        (fifo8_is_full(&s->tx_fifo_g) ? IXR_TX_FIFO_FULL : 0) |
+        (s->tx_fifo_g.num < s->regs[R_GQSPI_TX_THRESH] ?
+                                    IXR_TX_FIFO_NOT_FULL : 0);
+
+    if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
+        s->regs[R_INTR_STATUS] &= ~IXR_SELF_CLEAR;
+        s->regs[R_INTR_STATUS] |=
+            (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
+            (s->rx_fifo.num >= s->regs[R_RX_THRES] ?
+                                    IXR_RX_FIFO_NOT_EMPTY : 0) |
+            ZYNQMP_ONLY(fifo8_is_empty(&s->tx_fifo) ? IXR_TX_FIFO_EMPTY : 0) |
+            (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
+            (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+    }
+
+    /* QSPI/SPI Interrupt Trigger Status */
+    qspi_int = s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS];
+    /* GQSPI Interrupt Trigger Status */
+    gqspi_int = (~s->regs[R_GQSPI_IMR]) & s->regs[R_GQSPI_ISR] &
+                   GQSPI_IXR_MASK;
     /* drive external interrupt pin */
-    int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
-                                                                IXR_ALL);
+    new_irqline = !!((qspi_int | gqspi_int) & IXR_ALL);
     if (new_irqline != s->irqline) {
+        DB_PRINT_L(0, "IRQ state is changing %" PRIx32 " -> %" PRIx32 "\n",
+                   s->irqline, new_irqline);
         s->irqline = new_irqline;
         qemu_set_irq(s->irq, s->irqline);
     }
@@ -214,11 +343,18 @@ static void xilinx_spips_reset(DeviceState *d)
 
     fifo8_reset(&s->rx_fifo);
     fifo8_reset(&s->rx_fifo);
+    fifo8_reset(&s->rx_fifo_g);
+    fifo8_reset(&s->rx_fifo_g);
+    fifo32_reset(&s->fifo_g);
     /* non zero resets */
     s->regs[R_CONFIG] |= MODEFAIL_GEN_EN;
     s->regs[R_SLAVE_IDLE_COUNT] = 0xFF;
     s->regs[R_TX_THRES] = 1;
     s->regs[R_RX_THRES] = 1;
+    s->regs[R_GQSPI_TX_THRESH] = 1;
+    s->regs[R_GQSPI_RX_THRESH] = 1;
+    s->regs[R_GQSPI_GFIFO_THRESH] = 1;
+    s->regs[R_GQSPI_IMR] = GQSPI_IXR_MASK;
     /* FIXME: move magic number definition somewhere sensible */
     s->regs[R_MOD_ID] = 0x01090106;
     s->regs[R_LQSPI_CFG] = R_LQSPI_CFG_RESET;
@@ -228,6 +364,7 @@ static void xilinx_spips_reset(DeviceState *d)
     s->snoop_state = SNOOP_CHECKING;
     s->cmd_dummies = 0;
     s->man_start_com = false;
+    s->man_start_com_g = false;
     xilinx_spips_update_ixr(s);
     xilinx_spips_update_cs_lines(s);
 }
@@ -262,6 +399,141 @@ static inline void stripe8(uint8_t *x, int num, bool dir)
     memcpy(x, r, sizeof(uint8_t) * num);
 }
 
+static void xilinx_spips_flush_fifo_g(XilinxSPIPS *s)
+{
+    while (s->regs[R_GQSPI_DATA_STS] || !fifo32_is_empty(&s->fifo_g)) {
+        uint8_t tx_rx[2];
+        int num_stripes;
+        int i;
+        uint8_t busses;
+
+        /* memset() get's optimised out and results in the kernel seeing bogus
+         * data and complaining. So until memset_s() is supported let's just do
+         * it like this.
+         */
+        memset(tx_rx, 0, sizeof(tx_rx));
+        i = tx_rx[0];
+        i += tx_rx[1];
+
+        if (!s->regs[R_GQSPI_DATA_STS]) {
+            uint8_t imm;
+
+            s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g);
+            DB_PRINT_L(0, "Popped GQSPI command %" PRIx32 "\n",
+                       s->regs[R_GQSPI_GF_SNAPSHOT]);
+            if (!s->regs[R_GQSPI_GF_SNAPSHOT]) {
+                DB_PRINT_L(0, "Dummy GQSPI Delay Command Entry, Do nothing");
+                continue;
+            }
+            xilinx_spips_update_cs_lines(s);
+
+            imm = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
+            if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
+                /* immedate transfer */
+                if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) ||
+                    ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+                    s->regs[R_GQSPI_DATA_STS] = 1;
+                /* CS setup/hold - do nothing */
+                } else {
+                    s->regs[R_GQSPI_DATA_STS] = 0;
+                }
+            /* exponential transfer */
+            } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, EXPONENT)) {
+                if (imm > 31) {
+                    qemu_log_mask(LOG_UNIMP, "QSPI exponential transfer too"
+                                  " long - 2 ^ %" PRId8 " requested\n", imm);
+                }
+                s->regs[R_GQSPI_DATA_STS] = 1ul << imm;
+            /* non-exponential data transfer */
+            } else {
+                s->regs[R_GQSPI_DATA_STS] = imm;
+            }
+        }
+
+        /* Zero length transfer? no thanks! */
+        if (!s->regs[R_GQSPI_DATA_STS]) {
+            continue;
+        }
+
+        if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE) &&
+            fifo8_is_full(&s->rx_fifo_g)) {
+            /* No space in RX fifo for transfer - try again later */
+            return;
+        }
+
+        num_stripes = ARRAY_FIELD_EX32(s->regs,
+                                       GQSPI_GF_SNAPSHOT, STRIPE) ? 2 : 1;
+        if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) &&
+            !ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+            num_stripes = 1;
+        }
+
+        if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
+            tx_rx[0] = ARRAY_FIELD_EX32(s->regs,
+                                        GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
+        } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT)) {
+            for (i = 0; i < num_stripes; ++i) {
+                if (!fifo8_is_empty(&s->tx_fifo_g)) {
+                    tx_rx[i] = fifo8_pop(&s->tx_fifo_g);
+                    s->tx_fifo_g_align++;
+                } else {
+                    return;
+                }
+            }
+        }
+        if (num_stripes == 1) {
+            /* mirror */
+            for (i = 1; i < 2; ++i) {
+                tx_rx[i] = tx_rx[0];
+            }
+        }
+
+        busses = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT);
+        for (i = 0; i < 2; ++i) {
+            if (busses & (1 << i)) {
+                DB_PRINT_L(1, "bus %d tx = %02x\n", i, tx_rx[i]);
+            }
+            tx_rx[i] = ssi_transfer(s->spi[i], tx_rx[i]);
+            if (busses & (1 << i)) {
+                DB_PRINT_L(1, "bus %d rx = %02x\n", i, tx_rx[i]);
+            }
+        }
+
+        switch (busses) {
+        case 0x3:
+            if (num_stripes == 2) {
+                s->regs[R_GQSPI_DATA_STS]--;
+            }
+            /* fallthrough */
+        default:
+            if (s->regs[R_GQSPI_DATA_STS] != 0) {
+                /* Don't let this wrap around */
+                s->regs[R_GQSPI_DATA_STS]--;
+            }
+        }
+
+        if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+            for (i = 0; i < 2; ++i) {
+                if (busses & (1 << i)) {
+                    DB_PRINT_L(1, "bus %d push_byte = %02x\n",
+                               i, tx_rx[i]);
+                   fifo8_push(&s->rx_fifo_g, tx_rx[i]);
+                   s->rx_fifo_g_align++;
+                }
+            }
+        }
+
+        if (!s->regs[R_GQSPI_DATA_STS]) {
+            for (; s->tx_fifo_g_align % 4; s->tx_fifo_g_align++) {
+                fifo8_pop(&s->tx_fifo_g);
+            }
+            for (; s->rx_fifo_g_align % 4; s->rx_fifo_g_align++) {
+                fifo8_push(&s->rx_fifo_g, 0);
+            }
+        }
+    }
+}
+
 static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command)
 {
     if (!qs) {
@@ -327,9 +599,6 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
         uint8_t addr_length;
 
         if (fifo8_is_empty(&s->tx_fifo)) {
-            if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
-                s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
-            }
             xilinx_spips_update_ixr(s);
             return;
         } else if (s->snoop_state == SNOOP_STRIPING) {
@@ -490,16 +759,31 @@ static void xilinx_spips_check_zero_pump(XilinxSPIPS *s)
 
 static void xilinx_spips_check_flush(XilinxSPIPS *s)
 {
-    if (s->man_start_com ||
-        (!fifo8_is_empty(&s->tx_fifo) &&
-         !(s->regs[R_CONFIG] & MAN_START_EN))) {
-        xilinx_spips_check_zero_pump(s);
-        xilinx_spips_flush_txfifo(s);
+    bool gqspi_has_work = s->regs[R_GQSPI_DATA_STS] ||
+                          !fifo32_is_empty(&s->fifo_g);
+
+    if (ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+        if (s->man_start_com_g ||
+            (gqspi_has_work &&
+             !ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, GEN_FIFO_START_MODE))) {
+            xilinx_spips_flush_fifo_g(s);
+        }
+    } else {
+        if (s->man_start_com ||
+            (!fifo8_is_empty(&s->tx_fifo) &&
+             !(s->regs[R_CONFIG] & MAN_START_EN))) {
+            xilinx_spips_check_zero_pump(s);
+            xilinx_spips_flush_txfifo(s);
+        }
     }
 
     if (fifo8_is_empty(&s->tx_fifo) && !s->regs[R_TRANSFER_SIZE]) {
         s->man_start_com = false;
     }
+
+    if (!gqspi_has_work) {
+        s->man_start_com_g = false;
+    }
     xilinx_spips_update_ixr(s);
 }
 
@@ -514,6 +798,54 @@ static inline int rx_data_bytes(XilinxSPIPS *s, uint8_t *value, int max)
     return max - i;
 }
 
+static const void *pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
+{
+    void *ret;
+
+    if (max == 0 || max > fifo->num) {
+        abort();
+    }
+    *num = MIN(fifo->capacity - fifo->head, max);
+    ret = &fifo->data[fifo->head];
+    fifo->head += *num;
+    fifo->head %= fifo->capacity;
+    fifo->num -= *num;
+    return ret;
+}
+
+static void xlnx_zynqmp_qspips_notify(void *opaque)
+{
+    XlnxZynqMPQSPIPS *rq = XLNX_ZYNQMP_QSPIPS(opaque);
+    XilinxSPIPS *s = XILINX_SPIPS(rq);
+    Fifo8 *recv_fifo;
+
+    if (ARRAY_FIELD_EX32(s->regs, GQSPI_SELECT, GENERIC_QSPI_EN)) {
+        if (!(ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, MODE_EN) == 2)) {
+            return;
+        }
+        recv_fifo = &s->rx_fifo_g;
+    } else {
+        if (!(s->regs[R_CMND] & R_CMND_DMA_EN)) {
+            return;
+        }
+        recv_fifo = &s->rx_fifo;
+    }
+
+    while (recv_fifo->num >= 4
+           && stream_can_push(rq->dma, xlnx_zynqmp_qspips_notify, rq))
+    {
+        size_t ret;
+        uint32_t num;
+        const void *rxd = pop_buf(recv_fifo, 4, &num);
+
+        memcpy(rq->dma_buf, rxd, num);
+
+        ret = stream_push(rq->dma, rq->dma_buf, 4);
+        assert(ret == 4);
+        xilinx_spips_check_flush(s);
+    }
+}
+
 static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
                                                         unsigned size)
 {
@@ -522,6 +854,8 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
     uint32_t ret;
     uint8_t rx_buf[4];
     int shortfall;
+    const void *rxd;
+    uint32_t rx_num;
 
     memset(rx_buf, 0, sizeof(rx_buf));
 
@@ -563,6 +897,21 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr,
             ret <<= 8 * shortfall;
         }
         DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
+        xilinx_spips_check_flush(s);
+        xilinx_spips_update_ixr(s);
+        return ret;
+    case R_GQSPI_RXD:
+        if (fifo8_is_empty(&s->rx_fifo_g)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Read from empty GQSPI RX FIFO\n");
+            return 0;
+        }
+        rxd = pop_buf(&s->rx_fifo_g, 4, &rx_num);
+        assert(!(rx_num % 4));
+        memcpy(rx_buf, rxd, rx_num);
+        ret = ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, ENDIAN) ?
+              cpu_to_be32(*(uint32_t *)rx_buf) :
+              cpu_to_le32(*(uint32_t *)rx_buf);
+        xilinx_spips_check_flush(s);
         xilinx_spips_update_ixr(s);
         return ret;
     }
@@ -624,6 +973,49 @@ static void xilinx_spips_write(void *opaque, hwaddr addr,
         tx_data_bytes(&s->tx_fifo, (uint32_t)value, tx_btt,
                       s->regs[R_CONFIG] & R_CONFIG_ENDIAN);
         goto no_reg_update;
+    case R_GQSPI_CNFG:
+        mask = ~(R_GQSPI_CNFG_GEN_FIFO_START_MASK);
+        if (FIELD_EX32(value, GQSPI_CNFG, GEN_FIFO_START) &&
+            ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, GEN_FIFO_START_MODE)) {
+            s->man_start_com_g = true;
+        }
+        break;
+    case R_GQSPI_GEN_FIFO:
+        if (!fifo32_is_full(&s->fifo_g)) {
+            fifo32_push(&s->fifo_g, value);
+        }
+        goto no_reg_update;
+    case R_GQSPI_TXD:
+        tx_data_bytes(&s->tx_fifo_g, (uint32_t)value, 4,
+                      ARRAY_FIELD_EX32(s->regs, GQSPI_CNFG, ENDIAN));
+        goto no_reg_update;
+    case R_GQSPI_FIFO_CTRL:
+        mask = 0;
+        if (FIELD_EX32(value, GQSPI_FIFO_CTRL, GENERIC_FIFO_RESET)) {
+            fifo32_reset(&s->fifo_g);
+        }
+        if (FIELD_EX32(value, GQSPI_FIFO_CTRL, TX_FIFO_RESET)) {
+            fifo8_reset(&s->tx_fifo_g);
+        }
+        if (FIELD_EX32(value, GQSPI_FIFO_CTRL, RX_FIFO_RESET)) {
+            fifo8_reset(&s->rx_fifo_g);
+        }
+        break;
+    case R_GQSPI_IDR:
+        s->regs[R_GQSPI_IMR] |= value;
+        goto no_reg_update;
+    case R_GQSPI_IER:
+        s->regs[R_GQSPI_IMR] &= ~value;
+        goto no_reg_update;
+    case R_GQSPI_ISR:
+        s->regs[R_GQSPI_ISR] &= ~value;
+        goto no_reg_update;
+    case R_GQSPI_IMR:
+    case R_GQSPI_RXD:
+    case R_GQSPI_GF_SNAPSHOT:
+    case R_GQSPI_MOD_ID:
+        mask = 0;
+        break;
     }
     s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
 no_reg_update:
@@ -667,6 +1059,9 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr,
     if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) {
         fifo8_reset(&s->rx_fifo);
     }
+    if (object_dynamic_cast(OBJECT(s), TYPE_XLNX_ZYNQMP_QSPIPS)) {
+        xlnx_zynqmp_qspips_notify(s);
+    }
 }
 
 static const MemoryRegionOps qspips_ops = {
@@ -812,6 +1207,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]);
     }
@@ -829,6 +1225,9 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
 
     fifo8_create(&s->rx_fifo, xsc->rx_fifo_size);
     fifo8_create(&s->tx_fifo, xsc->tx_fifo_size);
+    fifo8_create(&s->rx_fifo_g, xsc->rx_fifo_size);
+    fifo8_create(&s->tx_fifo_g, xsc->tx_fifo_size);
+    fifo32_create(&s->fifo_g, 32);
 }
 
 static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
@@ -860,6 +1259,17 @@ static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
     }
 }
 
+static void xlnx_zynqmp_qspips_init(Object *obj)
+{
+    XlnxZynqMPQSPIPS *rq = XLNX_ZYNQMP_QSPIPS(obj);
+
+    object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE,
+                             (Object **)&rq->dma,
+                             object_property_allow_set_link,
+                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
+                             NULL);
+}
+
 static int xilinx_spips_post_load(void *opaque, int version_id)
 {
     xilinx_spips_update_ixr((XilinxSPIPS *)opaque);
@@ -940,10 +1350,18 @@ static const TypeInfo xilinx_qspips_info = {
     .class_init = xilinx_qspips_class_init,
 };
 
+static const TypeInfo xlnx_zynqmp_qspips_info = {
+    .name  = TYPE_XLNX_ZYNQMP_QSPIPS,
+    .parent = TYPE_XILINX_QSPIPS,
+    .instance_size  = sizeof(XlnxZynqMPQSPIPS),
+    .instance_init = xlnx_zynqmp_qspips_init,
+};
+
 static void xilinx_spips_register_types(void)
 {
     type_register_static(&xilinx_spips_info);
     type_register_static(&xilinx_qspips_info);
+    type_register_static(&xlnx_zynqmp_qspips_info);
 }
 
 type_init(xilinx_spips_register_types)
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index df6e245..ecfeb28 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -26,11 +26,12 @@
 #define XILINX_SPIPS_H
 
 #include "hw/ssi/ssi.h"
-#include "qemu/fifo8.h"
+#include "qemu/fifo32.h"
+#include "hw/stream.h"
 
 typedef struct XilinxSPIPS XilinxSPIPS;
 
-#define XLNX_SPIPS_R_MAX        (0x100 / 4)
+#define XLNX_SPIPS_R_MAX        0x200
 
 /* Bite off 4k chunks at a time */
 #define LQSPI_CACHE_SIZE 1024
@@ -66,10 +67,24 @@ struct XilinxSPIPS {
     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;
+    /* GQSPI has seperate tx/rx fifos */
+    Fifo8 rx_fifo_g;
+    Fifo8 tx_fifo_g;
+    /*
+     * at the end of each generic command, misaligned extra bytes are discard
+     * or padded to tx and rx respectively to round it out (and avoid need for
+     * individual byte access. Since we use byte fifos, keep track of the
+     * alignment WRT to word access.
+     */
+    uint8_t rx_fifo_g_align;
+    uint8_t tx_fifo_g_align;
+
+    Fifo32 fifo_g;
 
     uint8_t num_txrx_bytes;
     uint32_t rx_discard;
@@ -77,6 +92,7 @@ struct XilinxSPIPS {
     uint32_t regs[XLNX_SPIPS_R_MAX];
 
     bool man_start_com;
+    bool man_start_com_g;
 };
 
 typedef struct {
@@ -88,6 +104,13 @@ typedef struct {
     bool mmio_execution_enabled;
 } XilinxQSPIPS;
 
+typedef struct {
+    XilinxQSPIPS parent_obj;
+
+    StreamSlave *dma;
+    uint8_t dma_buf[4];
+} XlnxZynqMPQSPIPS;
+
 typedef struct XilinxSPIPSClass {
     SysBusDeviceClass parent_class;
 
@@ -99,6 +122,7 @@ typedef struct XilinxSPIPSClass {
 
 #define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
 #define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
+#define TYPE_XLNX_ZYNQMP_QSPIPS "xlnx.usmp-gqspi"
 
 #define XILINX_SPIPS(obj) \
      OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS)
@@ -110,4 +134,7 @@ typedef struct XilinxSPIPSClass {
 #define XILINX_QSPIPS(obj) \
      OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS)
 
+#define XLNX_ZYNQMP_QSPIPS(obj) \
+     OBJECT_CHECK(XlnxZynqMPQSPIPS, (obj), TYPE_XLNX_ZYNQMP_QSPIPS)
+
 #endif /* XILINX_SPIPS_H */
-- 
2.9.3

  parent reply	other threads:[~2017-10-21 21:54 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-21 21:54 [Qemu-devel] [PATCH v2 00/12] Add support for the ZynqMP Generic QSPI Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 01/12] m25p80: Add support for continuous read out of RDSR and READ_FSR Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 02/12] m25p80: Add support for SST READ ID 0x90/0xAB commands Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 03/12] m25p80: Add support for BRRD/BRWR and BULK_ERASE (0x60) Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 04/12] m25p80: Add support for n25q512a11 and n25q512a13 Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 05/12] xilinx_spips: Move FlashCMD, XilinxQSPIPS and XilinxSPIPSClass Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 06/12] xilinx_spips: Update striping to be big-endian bit order Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 08/12] xilinx_spips: Support configured endiannes of TX/RX registers Francisco Iglesias
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 10/12] xilinx_spips: Add support for 4 byte addresses in the LQSPI Francisco Iglesias
2017-10-21 21:54 ` Francisco Iglesias [this message]
2017-10-21 21:54 ` [Qemu-devel] [PATCH v2 12/12] xlnx-zcu102: Add support for the ZynqMP QSPI Francisco Iglesias

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171021215420.19787-12-frasse.iglesias@gmail.com \
    --to=frasse.iglesias@gmail.com \
    --cc=alistai@xilinx.com \
    --cc=edgari@xilinx.com \
    --cc=francisco.iglesias@feimtech.se \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.