qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 13/36] aspeed/smc: handle SPI flash Command mode
Date: Thu, 19 Jan 2017 14:09:32 +0000	[thread overview]
Message-ID: <1484834995-26826-14-git-send-email-peter.maydell@linaro.org> (raw)
In-Reply-To: <1484834995-26826-1-git-send-email-peter.maydell@linaro.org>

From: Cédric Le Goater <clg@kaod.org>

The Aspeed SMC controllers have a mode (Command mode) in which
accesses to the flash content are no different than doing MMIOs. The
controller generates all the necessary commands to load (or store)
data in memory.

However, accesses are restricted to the segment window assigned the
the flash module by the controller. This window is defined by the
Segment Address Register.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1483979087-32663-8-git-send-email-clg@kaod.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 include/hw/ssi/aspeed_smc.h |   2 +-
 hw/ssi/aspeed_smc.c         | 152 ++++++++++++++++++++++++++++++++++++++------
 2 files changed, 132 insertions(+), 22 deletions(-)

diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index e811742..1f55731 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -49,7 +49,7 @@ typedef struct AspeedSMCController {
 } AspeedSMCController;
 
 typedef struct AspeedSMCFlash {
-    const struct AspeedSMCState *controller;
+    struct AspeedSMCState *controller;
 
     uint8_t id;
     uint32_t size;
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 8d8a62e..a0a8164 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -69,6 +69,7 @@
 #define R_CTRL0           (0x10 / 4)
 #define   CTRL_CMD_SHIFT           16
 #define   CTRL_CMD_MASK            0xff
+#define   CTRL_AST2400_SPI_4BYTE   (1 << 13)
 #define   CTRL_CE_STOP_ACTIVE      (1 << 2)
 #define   CTRL_CMD_MODE_MASK       0x3
 #define     CTRL_READMODE          0x0
@@ -138,6 +139,9 @@
 #define ASPEED_SOC_SPI_FLASH_BASE   0x30000000
 #define ASPEED_SOC_SPI2_FLASH_BASE  0x38000000
 
+/* Flash opcodes. */
+#define SPI_OP_READ       0x03    /* Read data bytes (low frequency) */
+
 /*
  * Default segments mapping addresses and size for each slave per
  * controller. These can be changed when board is initialized with the
@@ -414,21 +418,123 @@ static inline bool aspeed_smc_is_writable(const AspeedSMCFlash *fl)
     return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + fl->id));
 }
 
+static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl)
+{
+    const AspeedSMCState *s = fl->controller;
+    int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK;
+
+    /* In read mode, the default SPI command is READ (0x3). In other
+     * modes, the command should necessarily be defined */
+    if (aspeed_smc_flash_mode(fl) == CTRL_READMODE) {
+        cmd = SPI_OP_READ;
+    }
+
+    if (!cmd) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: no command defined for mode %d\n",
+                      __func__, aspeed_smc_flash_mode(fl));
+    }
+
+    return cmd;
+}
+
+static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl)
+{
+    const AspeedSMCState *s = fl->controller;
+
+    if (s->ctrl->segments == aspeed_segments_spi) {
+        return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE;
+    } else {
+        return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->id));
+    }
+}
+
+static inline bool aspeed_smc_is_ce_stop_active(const AspeedSMCFlash *fl)
+{
+    const AspeedSMCState *s = fl->controller;
+
+    return s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE;
+}
+
+static void aspeed_smc_flash_select(AspeedSMCFlash *fl)
+{
+    AspeedSMCState *s = fl->controller;
+
+    s->regs[s->r_ctrl0 + fl->id] &= ~CTRL_CE_STOP_ACTIVE;
+    qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
+}
+
+static void aspeed_smc_flash_unselect(AspeedSMCFlash *fl)
+{
+    AspeedSMCState *s = fl->controller;
+
+    s->regs[s->r_ctrl0 + fl->id] |= CTRL_CE_STOP_ACTIVE;
+    qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
+}
+
+static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl,
+                                              uint32_t addr)
+{
+    const AspeedSMCState *s = fl->controller;
+    AspeedSegments seg;
+
+    aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + fl->id], &seg);
+    if ((addr & (seg.size - 1)) != addr) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid address 0x%08x for CS%d segment : "
+                      "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n",
+                      s->ctrl->name, addr, fl->id, seg.addr,
+                      seg.addr + seg.size);
+    }
+
+    addr &= seg.size - 1;
+    return addr;
+}
+
+static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr)
+{
+    const AspeedSMCState *s = fl->controller;
+    uint8_t cmd = aspeed_smc_flash_cmd(fl);
+
+    /* Flash access can not exceed CS segment */
+    addr = aspeed_smc_check_segment_addr(fl, addr);
+
+    ssi_transfer(s->spi, cmd);
+
+    if (aspeed_smc_flash_is_4byte(fl)) {
+        ssi_transfer(s->spi, (addr >> 24) & 0xff);
+    }
+    ssi_transfer(s->spi, (addr >> 16) & 0xff);
+    ssi_transfer(s->spi, (addr >> 8) & 0xff);
+    ssi_transfer(s->spi, (addr & 0xff));
+}
+
 static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
 {
     AspeedSMCFlash *fl = opaque;
-    const AspeedSMCState *s = fl->controller;
+    AspeedSMCState *s = fl->controller;
     uint64_t ret = 0;
     int i;
 
-    if (aspeed_smc_is_usermode(fl)) {
+    switch (aspeed_smc_flash_mode(fl)) {
+    case CTRL_USERMODE:
         for (i = 0; i < size; i++) {
             ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
         }
-    } else {
-        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
-                      __func__);
-        ret = -1;
+        break;
+    case CTRL_READMODE:
+    case CTRL_FREADMODE:
+        aspeed_smc_flash_select(fl);
+        aspeed_smc_flash_send_addr(fl, addr);
+
+        for (i = 0; i < size; i++) {
+            ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
+        }
+
+        aspeed_smc_flash_unselect(fl);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n",
+                      __func__, aspeed_smc_flash_mode(fl));
     }
 
     return ret;
@@ -438,7 +544,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
                            unsigned size)
 {
     AspeedSMCFlash *fl = opaque;
-    const AspeedSMCState *s = fl->controller;
+    AspeedSMCState *s = fl->controller;
     int i;
 
     if (!aspeed_smc_is_writable(fl)) {
@@ -447,14 +553,25 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
         return;
     }
 
-    if (!aspeed_smc_is_usermode(fl)) {
-        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
-                      __func__);
-        return;
-    }
+    switch (aspeed_smc_flash_mode(fl)) {
+    case CTRL_USERMODE:
+        for (i = 0; i < size; i++) {
+            ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
+        }
+        break;
+    case CTRL_WRITEMODE:
+        aspeed_smc_flash_select(fl);
+        aspeed_smc_flash_send_addr(fl, addr);
+
+        for (i = 0; i < size; i++) {
+            ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
+        }
 
-    for (i = 0; i < size; i++) {
-        ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
+        aspeed_smc_flash_unselect(fl);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n",
+                      __func__, aspeed_smc_flash_mode(fl));
     }
 }
 
@@ -468,13 +585,6 @@ static const MemoryRegionOps aspeed_smc_flash_ops = {
     },
 };
 
-static inline bool aspeed_smc_is_ce_stop_active(const AspeedSMCFlash *fl)
-{
-    const AspeedSMCState *s = fl->controller;
-
-    return s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE;
-}
-
 static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl)
 {
     const AspeedSMCState *s = fl->controller;
-- 
2.7.4

  parent reply	other threads:[~2017-01-19 14:10 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-19 14:09 [Qemu-devel] [PULL 00/36] target-arm queue Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 01/36] arm: Uniquely name imx25 I2C buses Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 02/36] block: m25p80: Add Quad Page Program 4byte Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 03/36] block: m25p80: Introduce die erase command Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 04/36] block: m25p80: Improve 1GiB Micron flash definition Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 05/36] target/arm: Handle VIRQ and VFIQ in arm_cpu_do_interrupt_aarch32() Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 06/36] target/arm: Implement DBGVCR32_EL2 system register Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 07/36] aspeed/smc: remove call to reset in realize function Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 08/36] aspeed/smc: remove call to aspeed_smc_update_cs() in reset function Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 09/36] aspeed/smc: rework the prototype of the AspeedSMCFlash helper routines Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 10/36] aspeed/smc: autostrap CE0/1 configuration Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 11/36] aspeed/smc: unfold the AspeedSMCController array Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 12/36] aspeed/smc: adjust the size of the register region Peter Maydell
2017-01-19 14:09 ` Peter Maydell [this message]
2017-01-19 14:09 ` [Qemu-devel] [PULL 14/36] aspeed/smc: reset flash after each test Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 15/36] aspeed/smc: extend tests for Command mode Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 16/36] aspeed: use first FMC flash as a boot ROM Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 17/36] arm: virt: Fix segmentation fault when specifying an unsupported CPU Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 18/36] hw/arm/virt-acpi - reserve ECAM space as PNP0C02 device Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 19/36] hw/intc/arm_gicv3: Add external IRQ lines for VIRQ and VFIQ Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 20/36] hw/intc/arm_gic: " Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 21/36] target-arm: Expose output GPIO line for VCPU maintenance interrupt Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 22/36] hw/arm/virt: Wire VIRQ, VFIQ, maintenance irq lines from GIC to CPU Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 23/36] target-arm: Add ARMCPU fields for GIC CPU i/f config Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 24/36] hw/intc/gicv3: Add defines for ICH system register fields Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 25/36] hw/intc/gicv3: Add data fields for virtualization support Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 26/36] hw/intc/arm_gicv3: Add accessors for ICH_ system registers Peter Maydell
2017-01-26  9:35   ` Paolo Bonzini
2017-01-26  9:42     ` Thomas Huth
2017-01-19 14:09 ` [Qemu-devel] [PULL 27/36] hw/intc/arm_gicv3: Implement ICV_ registers which are just accessors Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 28/36] hw/intc/arm_gicv3: Implement ICV_ HPPIR, DIR and RPR registers Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 29/36] hw/intc/arm_gicv3: Implement ICV_ registers EOIR and IAR Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 30/36] hw/intc/arm_gicv3: Implement gicv3_cpuif_virt_update() Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 31/36] hw/intc/arm_gicv3: Implement EL2 traps for CPU i/f regs Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 32/36] hw/arm/virt: Support using SMC for PSCI Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 33/36] hw/arm/virt-acpi-build: use SMC if booting in EL2 Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 34/36] target/arm/psci.c: If EL2 implemented, start CPUs " Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 35/36] target-arm: Enable EL2 feature bit on A53 and A57 Peter Maydell
2017-01-19 14:09 ` [Qemu-devel] [PULL 36/36] hw/arm/virt: Add board property to enable EL2 Peter Maydell

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=1484834995-26826-14-git-send-email-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).