All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support
@ 2016-03-19 20:59 Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

This patch series adds support for the Freescale i.MX6 processor.

For now we only support the following devices:
* up to 4 Cortex A9 cores
* A9 MPCORE (SCU, GIC, TWD)
* 5 i.MX UARTs
* 2 EPIT timers
* 1 GPT timer
* 7 GPIO controllers
* 6 SDHC controllers
* 5 SPI controllers
* 1 CCM device
* 1 SRC device
* 3 I2C devices
* various ROM/RAM areas.

This also adds the sabrelite board as a an actual platform for i.MX6.

This series was tested by booting a 4.4 linux kernel (using the
imx_v6_v7_defconfig file as kernel configuration).

Note1: as sabrelite uses uart2 as console, you have to redirect the second
QEMU serial port to stdout.
    qemu-system-arm -M sabrelite -display none ... -serial null -serial stdio

Note2: You need to disable the GPIO section related to physical push buttons
in the Linux DTS tree in order to avoid excecive interrupt triggering on
GPIO devices for non existant buttons.

Jean-Christophe Dubois (6):
  ARM: Factor out ARM on/off PSCI control functions
  i.MX: Add i.MX6 System Reset Controller device.
  FIFO: Add a FIFO32 implementation
  i.MX: Add the Freescale SPI Controller
  i.MX: Add i.MX6 SOC implementation.
  i.MX: Add sabrelite i.MX6 emulation.

 default-configs/arm-softmmu.mak |   1 +
 hw/arm/Makefile.objs            |   1 +
 hw/arm/fsl-imx6.c               | 449 +++++++++++++++++++++++++++++++++++++++
 hw/arm/sabrelite.c              | 117 +++++++++++
 hw/misc/Makefile.objs           |   1 +
 hw/misc/imx6_src.c              | 264 +++++++++++++++++++++++
 hw/ssi/Makefile.objs            |   1 +
 hw/ssi/imx_spi.c                | 454 ++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx6.h       | 450 +++++++++++++++++++++++++++++++++++++++
 include/hw/misc/imx6_src.h      |  73 +++++++
 include/hw/ssi/imx_spi.h        | 103 +++++++++
 include/qemu/fifo32.h           | 191 +++++++++++++++++
 target-arm/Makefile.objs        |   1 +
 target-arm/arm-powerctl.c       | 224 ++++++++++++++++++++
 target-arm/arm-powerctl.h       |  44 ++++
 target-arm/psci.c               |  69 +-----
 16 files changed, 2380 insertions(+), 63 deletions(-)
 create mode 100644 hw/arm/fsl-imx6.c
 create mode 100644 hw/arm/sabrelite.c
 create mode 100644 hw/misc/imx6_src.c
 create mode 100644 hw/ssi/imx_spi.c
 create mode 100644 include/hw/arm/fsl-imx6.h
 create mode 100644 include/hw/misc/imx6_src.h
 create mode 100644 include/hw/ssi/imx_spi.h
 create mode 100644 include/qemu/fifo32.h
 create mode 100644 target-arm/arm-powerctl.c
 create mode 100644 target-arm/arm-powerctl.h

-- 
2.5.0

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-23 15:24   ` Peter Maydell
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 2/6] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

Split ARM on/off function from PSCI support code.

This will allow to reuse these functions in other code.

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since V1:
 * Not present on V1

Changes since V2: 
 * Not present on V2

Changes since V3:
 * Move to a more generic/usefull API
 * Manage CPU level/mode change at startup
 * Allow PSCI to cope with EL2/HYP level.
 * Keep distinct errors for different causes.

 target-arm/Makefile.objs  |   1 +
 target-arm/arm-powerctl.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++
 target-arm/arm-powerctl.h |  44 +++++++++
 target-arm/psci.c         |  69 ++------------
 4 files changed, 275 insertions(+), 63 deletions(-)
 create mode 100644 target-arm/arm-powerctl.c
 create mode 100644 target-arm/arm-powerctl.h

diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index a80eb39..60fd1dd 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -9,3 +9,4 @@ obj-y += neon_helper.o iwmmxt_helper.o
 obj-y += gdbstub.o
 obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
 obj-y += crypto_helper.o
+obj-y += arm-powerctl.o
diff --git a/target-arm/arm-powerctl.c b/target-arm/arm-powerctl.c
new file mode 100644
index 0000000..8ebb3d8
--- /dev/null
+++ b/target-arm/arm-powerctl.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU support -- ARM Power Control specific functions.
+ *
+ * Copyright (c) 2016 Jean-Christophe Dubois
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include <cpu.h>
+#include <cpu-qom.h>
+#include "internals.h"
+#include "arm-powerctl.h"
+
+#ifndef DEBUG_ARM_POWERCTL
+#define DEBUG_ARM_POWERCTL 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_ARM_POWERCTL) { \
+            fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
+        } \
+    } while (0)
+
+CPUState *arm_get_cpu_by_id(uint64_t id)
+{
+    CPUState *cpu;
+
+    DPRINTF("cpu %" PRId64 "\n", id);
+
+    CPU_FOREACH(cpu) {
+        ARMCPU *armcpu = ARM_CPU(cpu);
+
+        if (armcpu->mp_affinity == id) {
+            return cpu;
+        }
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
+                  __func__, id);
+
+    return NULL;
+}
+
+int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
+                   int target_el, bool target_aa64)
+{
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+    CPUClass *target_cpu_class;
+
+    DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
+            cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
+            context_id);
+
+    /* requested EL level need to be above 0 */
+    assert(target_el >= 1 && target_el <= 3);
+
+    /* change to the cpu we are powering up */
+    target_cpu_state = arm_get_cpu_by_id(cpuid);
+    if (!target_cpu_state) {
+        /* The cpu was not found */
+        return QEMU_ARM_POWERCTL_INVALID_PARAM;
+    }
+    target_cpu = ARM_CPU(target_cpu_state);
+    if (!target_cpu->powered_off) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is already running\n",
+                      __func__, cpuid);
+        return QEMU_ARM_POWERCTL_ALREADY_ON;
+    }
+    target_cpu_class = CPU_GET_CLASS(target_cpu);
+
+    /* Initialize the cpu we are turning on */
+    cpu_reset(target_cpu_state);
+    target_cpu->powered_off = false;
+    target_cpu_state->halted = 0;
+
+    /*
+     * The newly brought CPU is requested to enter the exception level
+     * "target_el" and be in the requested mode (aarch64 ou aarch32).
+     */
+
+    /*
+     * Check that the CPU is supporting the requested level
+     */
+    switch (target_el) {
+    case 3:
+        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
+            if (is_a64(&target_cpu->env)) {
+                /* Lower EL3 exception is aarch64 */
+                target_cpu->env.cp15.scr_el3 |= SCR_RW;
+                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
+                    /* Lower EL2 exception is aarch64 */
+                    target_cpu->env.cp15.hcr_el2 |= HCR_RW;
+                }
+                target_cpu->env.pstate = PSTATE_MODE_EL3h;
+            } else {
+                switch_mode(&target_cpu->env, ARM_CPU_MODE_MON);
+            }
+            /* Processor is in secure mode */
+            target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
+        } else {
+            return QEMU_ARM_POWERCTL_INVALID_PARAM;
+        }
+        break;
+    case 2:
+        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
+            if (is_a64(&target_cpu->env)) {
+                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
+                    /* Lower EL3 exception is aarch64 */
+                    target_cpu->env.cp15.scr_el3 |= SCR_RW;
+                }
+                /* Lower EL2 exception is aarch64 */
+                target_cpu->env.cp15.hcr_el2 |= HCR_RW;
+                target_cpu->env.pstate = PSTATE_MODE_EL2h;
+            } else {
+                switch_mode(&target_cpu->env, ARM_CPU_MODE_HYP);
+            }
+            /* Processor is not in secure mode */
+            target_cpu->env.cp15.scr_el3 |= SCR_NS;
+        } else {
+            return QEMU_ARM_POWERCTL_INVALID_PARAM;
+        }
+        break;
+    case 1:
+    default:
+        if (is_a64(&target_cpu->env)) {
+            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
+                /* Lower EL3 exception is aarch64 */
+                target_cpu->env.cp15.scr_el3 |= SCR_RW;
+            }
+            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
+                /* Lower EL2 exception is aarch64 */
+                target_cpu->env.cp15.hcr_el2 |= HCR_RW;
+            }
+            target_cpu->env.pstate = PSTATE_MODE_EL1h;
+        } else {
+            switch_mode(&target_cpu->env, ARM_CPU_MODE_SVC);
+        }
+        /* Processor is not in secure mode */
+        target_cpu->env.cp15.scr_el3 |= SCR_NS;
+        break;
+    }
+
+    /* We assert if the request cannot be fulfilled */
+    assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));
+
+    /* We check if the started CPU is now in the correct level */
+    assert(target_el == arm_current_el(&target_cpu->env));
+
+    if (target_aa64) {
+        /* Thumb32 is not supported on AARCH64 */
+        if (entry & 1) {
+            return QEMU_ARM_POWERCTL_INVALID_PARAM;
+        }
+        target_cpu->env.xregs[0] = context_id;
+    } else {
+        target_cpu->env.regs[0] = context_id;
+        target_cpu->env.thumb = entry & 1;
+    }
+
+    /* Start the new CPU at the requested address */
+    target_cpu_class->set_pc(target_cpu_state, entry);
+
+    /* We are good to go */
+    return QEMU_ARM_POWERCTL_RET_SUCCESS;
+}
+
+void arm_set_cpu_off(uint64_t cpuid)
+{
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+
+    DPRINTF("cpu %" PRId64 "\n", cpuid);
+
+    /* change to the cpu we are powering up */
+    target_cpu_state = arm_get_cpu_by_id(cpuid);
+    if (!target_cpu_state) {
+        return;
+    }
+    target_cpu = ARM_CPU(target_cpu_state);
+    if (target_cpu->powered_off) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is already off\n",
+                      __func__, cpuid);
+        return;
+    }
+
+    target_cpu->powered_off = true;
+    target_cpu_state->halted = 1;
+    target_cpu_state->exception_index = EXCP_HLT;
+    cpu_loop_exit(target_cpu_state);
+}
+
+int arm_reset_cpu(uint64_t cpuid)
+{
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+
+    DPRINTF("cpu %" PRId64 "\n", cpuid);
+
+    /* change to the cpu we are resetting */
+    target_cpu_state = arm_get_cpu_by_id(cpuid);
+    if (!target_cpu_state) {
+        return QEMU_ARM_POWERCTL_INVALID_PARAM;
+    }
+    target_cpu = ARM_CPU(target_cpu_state);
+    if (target_cpu->powered_off) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is off\n",
+                      __func__, cpuid);
+        return -1;
+    }
+
+    /* Reset the cpu */
+    cpu_reset(target_cpu_state);
+
+    return QEMU_ARM_POWERCTL_RET_SUCCESS;
+}
diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
new file mode 100644
index 0000000..2195483
--- /dev/null
+++ b/target-arm/arm-powerctl.h
@@ -0,0 +1,44 @@
+/*
+ * QEMU support -- ARM Power Control specific functions.
+ *
+ * Copyright (c) 2016 Jean-Christophe Dubois
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_ARM_POWERCTL_H
+#define QEMU_ARM_POWERCTL_H
+
+#include "kvm-consts.h"
+
+#define QEMU_ARM_POWERCTL_RET_SUCCESS QEMU_PSCI_RET_SUCCESS
+#define QEMU_ARM_POWERCTL_INVALID_PARAM QEMU_PSCI_RET_INVALID_PARAMS
+#define QEMU_ARM_POWERCTL_ALREADY_ON QEMU_PSCI_RET_ALREADY_ON
+
+/*
+ * Retreive a CPUState object from its CPU ID
+ */
+CPUState *arm_get_cpu_by_id(uint64_t id);
+
+/*
+ * Start a give CPU. The CPU will start running at address "entry" with
+ * "context_id" in r0/x0. The CPU should start at exception level "target_el"
+ * and in mode aa64 or aa32 depending on "target_aa64"
+ */
+int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
+                   int target_el, bool target_aa64);
+
+/*
+ * Stop/Power off a given CPU
+ */
+
+void arm_set_cpu_off(uint64_t cpuid);
+
+/*
+ * Reset a given CPU
+ */
+int arm_reset_cpu(uint64_t cpuid);
+
+#endif
diff --git a/target-arm/psci.c b/target-arm/psci.c
index c55487f..f1c8eb2 100644
--- a/target-arm/psci.c
+++ b/target-arm/psci.c
@@ -22,6 +22,7 @@
 #include <kvm-consts.h>
 #include <sysemu/sysemu.h>
 #include "internals.h"
+#include "arm-powerctl.h"
 
 bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
 {
@@ -73,21 +74,6 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
     }
 }
 
-static CPUState *get_cpu_by_id(uint64_t id)
-{
-    CPUState *cpu;
-
-    CPU_FOREACH(cpu) {
-        ARMCPU *armcpu = ARM_CPU(cpu);
-
-        if (armcpu->mp_affinity == id) {
-            return cpu;
-        }
-    }
-
-    return NULL;
-}
-
 void arm_handle_psci_call(ARMCPU *cpu)
 {
     /*
@@ -98,7 +84,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
      * Additional information about the calling convention used is available in
      * the document 'SMC Calling Convention' (ARM DEN 0028)
      */
-    CPUState *cs = CPU(cpu);
     CPUARMState *env = &cpu->env;
     uint64_t param[4];
     uint64_t context_id, mpidr;
@@ -123,7 +108,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
     switch (param[0]) {
         CPUState *target_cpu_state;
         ARMCPU *target_cpu;
-        CPUClass *target_cpu_class;
 
     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
         ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
@@ -137,7 +121,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
 
         switch (param[2]) {
         case 0:
-            target_cpu_state = get_cpu_by_id(mpidr);
+            target_cpu_state = arm_get_cpu_by_id(mpidr);
             if (!target_cpu_state) {
                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
                 break;
@@ -167,52 +151,14 @@ void arm_handle_psci_call(ARMCPU *cpu)
         mpidr = param[1];
         entry = param[2];
         context_id = param[3];
-
-        /* change to the cpu we are powering up */
-        target_cpu_state = get_cpu_by_id(mpidr);
-        if (!target_cpu_state) {
-            ret = QEMU_PSCI_RET_INVALID_PARAMS;
-            break;
-        }
-        target_cpu = ARM_CPU(target_cpu_state);
-        if (!target_cpu->powered_off) {
-            ret = QEMU_PSCI_RET_ALREADY_ON;
-            break;
-        }
-        target_cpu_class = CPU_GET_CLASS(target_cpu);
-
-        /* Initialize the cpu we are turning on */
-        cpu_reset(target_cpu_state);
-        target_cpu->powered_off = false;
-        target_cpu_state->halted = 0;
-
         /*
          * The PSCI spec mandates that newly brought up CPUs enter the
          * exception level of the caller in the same execution mode as
          * the caller, with context_id in x0/r0, respectively.
-         *
-         * For now, it is sufficient to assert() that CPUs come out of
-         * reset in the same mode as the calling CPU, since we only
-         * implement EL1, which means that
-         * (a) there is no EL2 for the calling CPU to trap into to change
-         *     its state
-         * (b) the newly brought up CPU enters EL1 immediately after coming
-         *     out of reset in the default state
          */
-        assert(is_a64(env) == is_a64(&target_cpu->env));
-        if (is_a64(env)) {
-            if (entry & 1) {
-                ret = QEMU_PSCI_RET_INVALID_PARAMS;
-                break;
-            }
-            target_cpu->env.xregs[0] = context_id;
-        } else {
-            target_cpu->env.regs[0] = context_id;
-            target_cpu->env.thumb = entry & 1;
-        }
-        target_cpu_class->set_pc(target_cpu_state, entry);
-
-        ret = 0;
+        ret = arm_set_cpu_on(mpidr, entry, context_id,
+                             arm_current_el(&ARM_CPU(current_cpu)->env),
+                             is_a64(&ARM_CPU(current_cpu)->env));
         break;
     case QEMU_PSCI_0_1_FN_CPU_OFF:
     case QEMU_PSCI_0_2_FN_CPU_OFF:
@@ -250,9 +196,6 @@ err:
     return;
 
 cpu_off:
-    cpu->powered_off = true;
-    cs->halted = 1;
-    cs->exception_index = EXCP_HLT;
-    cpu_loop_exit(cs);
+    arm_set_cpu_off(cpu->mp_affinity);
     /* notreached */
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 2/6] i.MX: Add i.MX6 System Reset Controller device.
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 3/6] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

This controller is also present in i.MX5X devices but they are not
yet emulated by QEMU.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since V1:
 * Change "reset" sematic to mean full power cyvle.

Changes since V2:
 * use arm-powerctl API
 * Added #include "qemu/osdep.h"

Changes since V3:
 * Minor fixes.

 hw/misc/Makefile.objs      |   1 +
 hw/misc/imx6_src.c         | 264 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/imx6_src.h |  73 +++++++++++++
 3 files changed, 338 insertions(+)
 create mode 100644 hw/misc/imx6_src.c
 create mode 100644 include/hw/misc/imx6_src.h

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 44ac2e1..2562fa2 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,6 +29,7 @@ obj-$(CONFIG_IMX) += imx_ccm.o
 obj-$(CONFIG_IMX) += imx31_ccm.o
 obj-$(CONFIG_IMX) += imx25_ccm.o
 obj-$(CONFIG_IMX) += imx6_ccm.o
+obj-$(CONFIG_IMX) += imx6_src.o
 obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c
new file mode 100644
index 0000000..6b026b4
--- /dev/null
+++ b/hw/misc/imx6_src.c
@@ -0,0 +1,264 @@
+/*
+ * IMX6 System Reset Controller
+ *
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx6_src.h"
+#include "sysemu/sysemu.h"
+#include "qemu/bitops.h"
+#include "arm-powerctl.h"
+
+#ifndef DEBUG_IMX6_SRC
+#define DEBUG_IMX6_SRC 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_IMX6_SRC) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+static char const *imx6_src_reg_name(uint32_t reg)
+{
+    static char unknown[20];
+
+    switch (reg) {
+    case SRC_SCR:
+        return "SRC_SCR";
+    case SRC_SBMR1:
+        return "SRC_SBMR1";
+    case SRC_SRSR:
+        return "SRC_SRSR";
+    case SRC_SISR:
+        return "SRC_SISR";
+    case SRC_SIMR:
+        return "SRC_SIMR";
+    case SRC_SBMR2:
+        return "SRC_SBMR2";
+    case SRC_GPR1:
+        return "SRC_GPR1";
+    case SRC_GPR2:
+        return "SRC_GPR2";
+    case SRC_GPR3:
+        return "SRC_GPR3";
+    case SRC_GPR4:
+        return "SRC_GPR4";
+    case SRC_GPR5:
+        return "SRC_GPR5";
+    case SRC_GPR6:
+        return "SRC_GPR6";
+    case SRC_GPR7:
+        return "SRC_GPR7";
+    case SRC_GPR8:
+        return "SRC_GPR8";
+    case SRC_GPR9:
+        return "SRC_GPR9";
+    case SRC_GPR10:
+        return "SRC_GPR10";
+    default:
+        sprintf(unknown, "%d ?", reg);
+        return unknown;
+    }
+}
+
+static const VMStateDescription vmstate_imx6_src = {
+    .name = TYPE_IMX6_SRC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, IMX6SRCState, SRC_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void imx6_src_reset(DeviceState *dev)
+{
+    IMX6SRCState *s = IMX6_SRC(dev);
+
+    DPRINTF("\n");
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    /* Set reset values */
+    s->regs[SRC_SCR] = 0x521;
+    s->regs[SRC_SRSR] = 0x1;
+    s->regs[SRC_SIMR] = 0x1F;
+}
+
+static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t value = 0;
+    IMX6SRCState *s = (IMX6SRCState *)opaque;
+    uint32_t index = offset >> 2;
+
+    if (index < SRC_MAX) {
+        value = s->regs[index];
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset);
+
+    }
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value);
+
+    return value;
+}
+
+static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
+{
+    IMX6SRCState *s = (IMX6SRCState *)opaque;
+    uint32_t index = offset >> 2;
+    unsigned long change_mask;
+    unsigned long current_value = value;
+
+    if (index >=  SRC_MAX) {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset);
+        return;
+    }
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index),
+            (uint32_t)current_value);
+
+    change_mask = s->regs[index] ^ (uint32_t)current_value;
+
+    switch (index) {
+    case SRC_SCR:
+        /*
+         * On real hardware when the system reset controller starts a
+         * secondary CPU it runs through some boot ROM code which reads
+         * the SRC_GPRX registers controlling the start address and branches
+         * to it.
+         * Here we are taking a short cut and branching directly to the
+         * requested address (we don't want to run the boot ROM code inside
+         * QEMU)
+         */
+        if (EXTRACT(change_mask, CORE3_ENABLE)) {
+            if (EXTRACT(current_value, CORE3_ENABLE)) {
+                /* CORE 3 is brought up */
+                arm_set_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8],
+                               3, false);
+            } else {
+                /* CORE 3 is shut down */
+                arm_set_cpu_off(3);
+            }
+            /* We clear the reset bits as the processor changed state */
+            clear_bit(CORE3_RST_SHIFT, &current_value);
+            clear_bit(CORE3_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE2_ENABLE)) {
+            if (EXTRACT(current_value, CORE2_ENABLE)) {
+                /* CORE 2 is brought up */
+                arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6],
+                               3, false);
+            } else {
+                /* CORE 3 is shut down */
+                arm_set_cpu_off(2);
+            }
+            /* We clear the reset bits as the processor changed state */
+            clear_bit(CORE2_RST_SHIFT, &current_value);
+            clear_bit(CORE2_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE1_ENABLE)) {
+            if (EXTRACT(current_value, CORE1_ENABLE)) {
+                /* CORE 1 is brought up */
+                arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4],
+                               3, false);
+            } else {
+                /* CORE 3 is shut down */
+                arm_set_cpu_off(1);
+            }
+            /* We clear the reset bits as the processor changed state */
+            clear_bit(CORE1_RST_SHIFT, &current_value);
+            clear_bit(CORE1_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE0_RST)) {
+            arm_reset_cpu(0);
+            clear_bit(CORE0_RST_SHIFT, &current_value);
+        }
+        if (EXTRACT(change_mask, CORE1_RST)) {
+            arm_reset_cpu(1);
+            clear_bit(CORE1_RST_SHIFT, &current_value);
+        }
+        if (EXTRACT(change_mask, CORE2_RST)) {
+            arm_reset_cpu(2);
+            clear_bit(CORE2_RST_SHIFT, &current_value);
+        }
+        if (EXTRACT(change_mask, CORE3_RST)) {
+            arm_reset_cpu(3);
+            clear_bit(CORE3_RST_SHIFT, &current_value);
+        }
+        if (EXTRACT(change_mask, SW_IPU2_RST)) {
+            /* We pretend the IPU2 is reset */
+            clear_bit(SW_IPU2_RST_SHIFT, &current_value);
+        }
+        if (EXTRACT(change_mask, SW_IPU1_RST)) {
+            /* We pretend the IPU1 is reset */
+            clear_bit(SW_IPU1_RST_SHIFT, &current_value);
+        }
+        s->regs[index] = current_value;
+        break;
+    default:
+        s->regs[index] = current_value;
+        break;
+    }
+}
+
+static const struct MemoryRegionOps imx6_src_ops = {
+    .read = imx6_src_read,
+    .write = imx6_src_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        /*
+         * Our device would not work correctly if the guest was doing
+         * unaligned access. This might not be a limitation on the real
+         * device but in practice there is no reason for a guest to access
+         * this device unaligned.
+         */
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void imx6_src_realize(DeviceState *dev, Error **errp)
+{
+    IMX6SRCState *s = IMX6_SRC(dev);
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s,
+                          TYPE_IMX6_SRC, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+}
+
+static void imx6_src_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = imx6_src_realize;
+    dc->reset = imx6_src_reset;
+    dc->vmsd = &vmstate_imx6_src;
+    dc->desc = "i.MX6 System Reset Controller";
+}
+
+static const TypeInfo imx6_src_info = {
+    .name          = TYPE_IMX6_SRC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(IMX6SRCState),
+    .class_init    = imx6_src_class_init,
+};
+
+static void imx6_src_register_types(void)
+{
+    type_register_static(&imx6_src_info);
+}
+
+type_init(imx6_src_register_types)
diff --git a/include/hw/misc/imx6_src.h b/include/hw/misc/imx6_src.h
new file mode 100644
index 0000000..eb36407
--- /dev/null
+++ b/include/hw/misc/imx6_src.h
@@ -0,0 +1,73 @@
+/*
+ * IMX6 System Reset Controller
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX6_SRC_H
+#define IMX6_SRC_H
+
+#include "hw/sysbus.h"
+#include "qemu/bitops.h"
+
+#define SRC_SCR 0
+#define SRC_SBMR1 1
+#define SRC_SRSR 2
+#define SRC_SISR 5
+#define SRC_SIMR 6
+#define SRC_SBMR2 7
+#define SRC_GPR1 8
+#define SRC_GPR2 9
+#define SRC_GPR3 10
+#define SRC_GPR4 11
+#define SRC_GPR5 12
+#define SRC_GPR6 13
+#define SRC_GPR7 14
+#define SRC_GPR8 15
+#define SRC_GPR9 16
+#define SRC_GPR10 17
+#define SRC_MAX 18
+
+/* SRC_SCR */
+#define CORE3_ENABLE_SHIFT     24
+#define CORE3_ENABLE_LENGTH    1
+#define CORE2_ENABLE_SHIFT     23
+#define CORE2_ENABLE_LENGTH    1
+#define CORE1_ENABLE_SHIFT     22
+#define CORE1_ENABLE_LENGTH    1
+#define CORE3_RST_SHIFT        16
+#define CORE3_RST_LENGTH       1
+#define CORE2_RST_SHIFT        15
+#define CORE2_RST_LENGTH       1
+#define CORE1_RST_SHIFT        14
+#define CORE1_RST_LENGTH       1
+#define CORE0_RST_SHIFT        13
+#define CORE0_RST_LENGTH       1
+#define SW_IPU1_RST_SHIFT      3
+#define SW_IPU1_RST_LENGTH     1
+#define SW_IPU2_RST_SHIFT      12
+#define SW_IPU2_RST_LENGTH     1
+#define WARM_RST_ENABLE_SHIFT  0
+#define WARM_RST_ENABLE_LENGTH 1
+
+#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH)
+
+#define TYPE_IMX6_SRC "imx6.src"
+#define IMX6_SRC(obj) OBJECT_CHECK(IMX6SRCState, (obj), TYPE_IMX6_SRC)
+
+typedef struct IMX6SRCState {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion iomem;
+
+    uint32_t regs[SRC_MAX];
+
+} IMX6SRCState;
+
+#endif /* IMX6_SRC_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 3/6] FIFO: Add a FIFO32 implementation
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 2/6] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 4/6] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

This one is build on top of the existing FIFO8

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since v1: 
 * None

Changes since v2:
 * Added copyright
 * define Fifo32 as a struct containing Fifo8
 * remove fifo32_pop_buf()

Changes since v3:
 * Added comment on unsupported fifo32_pop_buf()

 include/qemu/fifo32.h | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 191 insertions(+)
 create mode 100644 include/qemu/fifo32.h

diff --git a/include/qemu/fifo32.h b/include/qemu/fifo32.h
new file mode 100644
index 0000000..2e5a0cc
--- /dev/null
+++ b/include/qemu/fifo32.h
@@ -0,0 +1,191 @@
+/*
+ * Generic FIFO32 component, based on FIFO8.
+ *
+ * Copyright (c) 2016 Jean-Christophe Dubois
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FIFO32_H
+#define FIFO32_H
+
+#include "qemu/osdep.h"
+#include "qemu/fifo8.h"
+
+typedef struct {
+    Fifo8 fifo;
+} Fifo32;
+
+/**
+ * fifo32_create:
+ * @fifo: struct Fifo32 to initialise with new FIFO
+ * @capacity: capacity of the newly created FIFO expressed in 32 bit words
+ *
+ * Create a FIFO of the specified size. Clients should call fifo32_destroy()
+ * when finished using the fifo. The FIFO is initially empty.
+ */
+
+static inline void fifo32_create(Fifo32 *fifo, uint32_t capacity)
+{
+    fifo8_create(&fifo->fifo, capacity * sizeof(uint32_t));
+}
+
+/**
+ * fifo32_destroy:
+ * @fifo: FIFO to cleanup
+ *
+ * Cleanup a FIFO created with fifo32_create(). Frees memory created for FIFO
+ * storage. The FIFO is no longer usable after this has been called.
+ */
+
+static inline void fifo32_destroy(Fifo32 *fifo)
+{
+    fifo8_destroy(&fifo->fifo);
+}
+
+/**
+ * fifo32_num_free:
+ * @fifo: FIFO to check
+ *
+ * Return the number of free uint32_t slots in the FIFO.
+ *
+ * Returns: Number of free 32 bit words.
+ */
+
+static inline uint32_t fifo32_num_free(Fifo32 *fifo)
+{
+    return DIV_ROUND_UP(fifo8_num_free(&fifo->fifo), sizeof(uint32_t));
+}
+
+/**
+ * fifo32_num_used:
+ * @fifo: FIFO to check
+ *
+ * Return the number of used uint32_t slots in the FIFO.
+ *
+ * Returns: Number of used 32 bit words.
+ */
+
+static inline uint32_t fifo32_num_used(Fifo32 *fifo)
+{
+    return DIV_ROUND_UP(fifo8_num_used(&fifo->fifo), sizeof(uint32_t));
+}
+
+/**
+ * fifo32_push:
+ * @fifo: FIFO to push to
+ * @data: 32 bits data word to push
+ *
+ * Push a 32 bits data word to the FIFO. Behaviour is undefined if the FIFO
+ * is full. Clients are responsible for checking for fullness using
+ * fifo32_is_full().
+ */
+
+static inline void fifo32_push(Fifo32 *fifo, uint32_t data)
+{
+    int i;
+
+    for (i = 0; i < sizeof(data); i++) {
+        fifo8_push(&fifo->fifo, data & 0xff);
+        data >>= 8;
+    }
+}
+
+/**
+ * fifo32_push_all:
+ * @fifo: FIFO to push to
+ * @data: data to push
+ * @size: number of 32 bit words to push
+ *
+ * Push a 32 bit word array to the FIFO. Behaviour is undefined if the FIFO
+ * is full. Clients are responsible for checking the space left in the FIFO
+ * using fifo32_num_free().
+ */
+
+static inline void fifo32_push_all(Fifo32 *fifo, const uint32_t *data,
+                                   uint32_t num)
+{
+    int i;
+
+    for (i = 0; i < num; i++) {
+        fifo32_push(fifo, data[i]);
+    }
+}
+
+/**
+ * fifo32_pop:
+ * @fifo: fifo to pop from
+ *
+ * Pop a 32 bits data word from the FIFO. Behaviour is undefined if the FIFO
+ * is empty. Clients are responsible for checking for emptiness using
+ * fifo32_is_empty().
+ *
+ * Returns: The popped 32 bits data word.
+ */
+
+static inline uint32_t fifo32_pop(Fifo32 *fifo)
+{
+    uint32_t ret = 0;
+    int i;
+
+    for (i = 0; i < sizeof(uint32_t); i++) {
+        ret |= (fifo8_pop(&fifo->fifo) << (i * 8));
+    }
+
+    return ret;
+}
+
+/**
+ * There is no fifo32_pop_buf() because the data is not stored in the buffer
+ * as a set of native-order words.
+ */
+
+/**
+ * fifo32_reset:
+ * @fifo: FIFO to reset
+ *
+ * Reset a FIFO. All data is discarded and the FIFO is emptied.
+ */
+
+static inline void fifo32_reset(Fifo32 *fifo)
+{
+    fifo8_reset(&fifo->fifo);
+}
+
+/**
+ * fifo32_is_empty:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is empty.
+ *
+ * Returns: True if the fifo is empty, false otherwise.
+ */
+
+static inline bool fifo32_is_empty(Fifo32 *fifo)
+{
+    return fifo8_is_empty(&fifo->fifo);
+}
+
+/**
+ * fifo32_is_full:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is full.
+ *
+ * Returns: True if the fifo is full, false otherwise.
+ */
+
+static inline bool fifo32_is_full(Fifo32 *fifo)
+{
+    return fifo8_num_free(&fifo->fifo) < sizeof(uint32_t);
+}
+
+#define VMSTATE_FIFO32(_field, _state) VMSTATE_FIFO8(_field.fifo, _state)
+
+#endif /* FIFO32_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 4/6] i.MX: Add the Freescale SPI Controller
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (2 preceding siblings ...)
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 3/6] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 5/6] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
  5 siblings, 0 replies; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since v1:
 * Access SPI slave only at a byte level.
 * rework the CS activation to avoid to reset access to SPI slaves.

Changes since v2:
 * Added #include "qemu/osdep.h"
 * remove previous_level from state struct
 * save burst_length in VMSTATE

Changes since v3:
 * Don't call qemu_set_irq() in reset function

 hw/ssi/Makefile.objs     |   1 +
 hw/ssi/imx_spi.c         | 454 +++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ssi/imx_spi.h | 103 +++++++++++
 3 files changed, 558 insertions(+)
 create mode 100644 hw/ssi/imx_spi.c
 create mode 100644 include/hw/ssi/imx_spi.h

diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index 9555825..fcbb79e 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -4,3 +4,4 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
 
 obj-$(CONFIG_OMAP) += omap_spi.o
+obj-$(CONFIG_IMX) += imx_spi.o
diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
new file mode 100644
index 0000000..d5dd42a
--- /dev/null
+++ b/hw/ssi/imx_spi.c
@@ -0,0 +1,454 @@
+/*
+ * IMX SPI Controller
+ *
+ * Copyright (c) 2016 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ssi/imx_spi.h"
+#include "sysemu/sysemu.h"
+
+#ifndef DEBUG_IMX_SPI
+#define DEBUG_IMX_SPI 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_IMX_SPI) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SPI, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+static char const *imx_spi_reg_name(uint32_t reg)
+{
+    static char unknown[20];
+
+    switch (reg) {
+    case ECSPI_RXDATA:
+        return  "ECSPI_RXDATA";
+    case ECSPI_TXDATA:
+        return  "ECSPI_TXDATA";
+    case ECSPI_CONREG:
+        return  "ECSPI_CONREG";
+    case ECSPI_CONFIGREG:
+        return  "ECSPI_CONFIGREG";
+    case ECSPI_INTREG:
+        return  "ECSPI_INTREG";
+    case ECSPI_DMAREG:
+        return  "ECSPI_DMAREG";
+    case ECSPI_STATREG:
+        return  "ECSPI_STATREG";
+    case ECSPI_PERIODREG:
+        return  "ECSPI_PERIODREG";
+    case ECSPI_TESTREG:
+        return  "ECSPI_TESTREG";
+    case ECSPI_MSGDATA:
+        return  "ECSPI_MSGDATA";
+    default:
+        sprintf(unknown, "%d ?", reg);
+        return unknown;
+    }
+}
+
+static const VMStateDescription vmstate_imx_spi = {
+    .name = TYPE_IMX_SPI,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_FIFO32(tx_fifo, IMXSPIState),
+        VMSTATE_FIFO32(rx_fifo, IMXSPIState),
+        VMSTATE_INT16(burst_length, IMXSPIState),
+        VMSTATE_UINT32_ARRAY(regs, IMXSPIState, ECSPI_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void imx_spi_txfifo_reset(IMXSPIState *s)
+{
+    fifo32_reset(&s->tx_fifo);
+    s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE;
+    s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF;
+}
+
+static void imx_spi_rxfifo_reset(IMXSPIState *s)
+{
+    fifo32_reset(&s->rx_fifo);
+    s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR;
+    s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF;
+    s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RO;
+}
+
+static void imx_spi_update_irq(IMXSPIState *s)
+{
+    int level;
+
+    if (fifo32_is_empty(&s->rx_fifo)) {
+        s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR;
+    } else {
+        s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RR;
+    }
+
+    if (fifo32_is_full(&s->rx_fifo)) {
+        s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RF;
+    } else {
+        s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF;
+    }
+
+    if (fifo32_is_empty(&s->tx_fifo)) {
+        s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE;
+    } else {
+        s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TE;
+    }
+
+    if (fifo32_is_full(&s->tx_fifo)) {
+        s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TF;
+    } else {
+        s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF;
+    }
+
+    level = s->regs[ECSPI_STATREG] & s->regs[ECSPI_INTREG] ? 1 : 0;
+
+    qemu_set_irq(s->irq, level);
+
+    DPRINTF("IRQ level is %d\n", level);
+}
+
+static uint8_t imx_spi_selected_channel(IMXSPIState *s)
+{
+    return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_SELECT);
+}
+
+static uint32_t imx_spi_burst_length(IMXSPIState *s)
+{
+    return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1;
+}
+
+static bool imx_spi_is_enabled(IMXSPIState *s)
+{
+    return s->regs[ECSPI_CONREG] & ECSPI_CONREG_EN;
+}
+
+static bool imx_spi_channel_is_master(IMXSPIState *s)
+{
+    uint8_t mode = EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_MODE);
+
+    return (mode & (1 << imx_spi_selected_channel(s))) ? true : false;
+}
+
+static bool imx_spi_is_multiple_master_burst(IMXSPIState *s)
+{
+    uint8_t wave = EXTRACT(s->regs[ECSPI_CONFIGREG], ECSPI_CONFIGREG_SS_CTL);
+
+    return imx_spi_channel_is_master(s) &&
+           !(s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC) &&
+           ((wave & (1 << imx_spi_selected_channel(s))) ? true : false);
+}
+
+static void imx_spi_flush_txfifo(IMXSPIState *s)
+{
+    uint32_t tx;
+    uint32_t rx;
+
+    DPRINTF("Begin: TX Fifo Size = %d, RX Fifo Size = %d\n",
+            fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo));
+
+    while (!fifo32_is_empty(&s->tx_fifo)) {
+        int tx_burst = 0;
+        int index = 0;
+
+        if (s->burst_length <= 0) {
+            s->burst_length = imx_spi_burst_length(s);
+
+            DPRINTF("Burst length = %d\n", s->burst_length);
+
+            if (imx_spi_is_multiple_master_burst(s)) {
+                s->regs[ECSPI_CONREG] |= ECSPI_CONREG_XCH;
+            }
+        }
+
+        tx = fifo32_pop(&s->tx_fifo);
+
+        DPRINTF("data tx:0x%08x\n", tx);
+
+        tx_burst = MIN(s->burst_length, 32);
+
+        rx = 0;
+
+        while (tx_burst) {
+            uint8_t byte = tx & 0xff;
+
+            DPRINTF("writing 0x%02x\n", (uint32_t)byte);
+
+            /* We need to write one byte at a time */
+            byte = ssi_transfer(s->bus, byte);
+
+            DPRINTF("0x%02x read\n", (uint32_t)byte);
+
+            tx = tx >> 8;
+            rx |= (byte << (index * 8));
+
+            /* Remove 8 bits from the actual burst */
+            tx_burst -= 8;
+            s->burst_length -= 8;
+            index++;
+        }
+
+        DPRINTF("data rx:0x%08x\n", rx);
+
+        if (fifo32_is_full(&s->rx_fifo)) {
+            s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RO;
+        } else {
+            fifo32_push(&s->rx_fifo, (uint8_t)rx);
+        }
+
+        if (s->burst_length <= 0) {
+            s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH;
+
+            if (!imx_spi_is_multiple_master_burst(s)) {
+                s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC;
+                break;
+            }
+        }
+    }
+
+    if (fifo32_is_empty(&s->tx_fifo)) {
+        s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC;
+    }
+
+    /* TODO: We should also use TDR and RDR bits */
+
+    DPRINTF("End: TX Fifo Size = %d, RX Fifo Size = %d\n",
+            fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo));
+}
+
+static void imx_spi_reset(DeviceState *dev)
+{
+    IMXSPIState *s = IMX_SPI(dev);
+
+    DPRINTF("\n");
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    s->regs[ECSPI_STATREG] = 0x00000003;
+
+    imx_spi_rxfifo_reset(s);
+    imx_spi_txfifo_reset(s);
+
+    imx_spi_update_irq(s);
+
+    s->burst_length = 0;
+}
+
+static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t value = 0;
+    IMXSPIState *s = opaque;
+    uint32_t index = offset >> 2;
+
+    if (index >=  ECSPI_MAX) {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset);
+        return 0;
+    }
+
+    switch (index) {
+    case ECSPI_RXDATA:
+        if (!imx_spi_is_enabled(s)) {
+            value = 0;
+        } else if (fifo32_is_empty(&s->rx_fifo)) {
+            /* value is undefined */
+            value = 0xdeadbeef;
+        } else {
+            /* read from the RX FIFO */
+            value = fifo32_pop(&s->rx_fifo);
+        }
+
+        break;
+    case ECSPI_TXDATA:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from TX FIFO\n",
+                      TYPE_IMX_SPI, __func__);
+
+        /* Reading from TXDATA gives 0 */
+
+        break;
+    case ECSPI_MSGDATA:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from MSG FIFO\n",
+                      TYPE_IMX_SPI, __func__);
+
+        /* Reading from MSGDATA gives 0 */
+
+        break;
+    default:
+        value = s->regs[index];
+        break;
+    }
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_spi_reg_name(index), value);
+
+    imx_spi_update_irq(s);
+
+    return (uint64_t)value;
+}
+
+static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
+{
+    IMXSPIState *s = opaque;
+    uint32_t index = offset >> 2;
+    uint32_t change_mask;
+
+    if (index >=  ECSPI_MAX) {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset);
+        return;
+    }
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index),
+            (uint32_t)value);
+
+    change_mask = s->regs[index] ^ value;
+
+    switch (index) {
+    case ECSPI_RXDATA:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n",
+                      TYPE_IMX_SPI, __func__);
+        break;
+    case ECSPI_TXDATA:
+    case ECSPI_MSGDATA:
+        /* Is there any difference between TXDATA and MSGDATA ? */
+        /* I'll have to look in the linux driver */
+        if (!imx_spi_is_enabled(s)) {
+            /* Ignore writes if device is disabled */
+            break;
+        } else if (fifo32_is_full(&s->tx_fifo)) {
+            /* Ignore writes if queue is full */
+            break;
+        }
+
+        fifo32_push(&s->tx_fifo, (uint32_t)value);
+
+        if (imx_spi_channel_is_master(s) &&
+            (s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC)) {
+            /*
+             * Start emitting if current channel is master and SMC bit is
+             * set.
+             */
+            imx_spi_flush_txfifo(s);
+        }
+
+        break;
+    case ECSPI_STATREG:
+        /* the RO and TC bits are write-one-to-clear */
+        value &= ECSPI_STATREG_RO | ECSPI_STATREG_TC;
+        s->regs[ECSPI_STATREG] &= ~value;
+
+        break;
+    case ECSPI_CONREG:
+        s->regs[ECSPI_CONREG] = value;
+
+        if (!imx_spi_is_enabled(s)) {
+            /* device is disabled, so this is a reset */
+            imx_spi_reset(DEVICE(s));
+            return;
+        }
+
+        if (imx_spi_channel_is_master(s)) {
+            int i;
+
+            /* We are in master mode */
+
+            for (i = 0; i < 4; i++) {
+                qemu_set_irq(s->cs_lines[i],
+                             i == imx_spi_selected_channel(s) ? 0 : 1);
+            }
+
+            if ((value & change_mask & ECSPI_CONREG_SMC) &&
+                !fifo32_is_empty(&s->tx_fifo)) {
+                /* SMC bit is set and TX FIFO has some slots filled in */
+                imx_spi_flush_txfifo(s);
+            } else if ((value & change_mask & ECSPI_CONREG_XCH) &&
+                !(value & ECSPI_CONREG_SMC)) {
+                /* This is a request to start emitting */
+                imx_spi_flush_txfifo(s);
+            }
+        }
+
+        break;
+    default:
+        s->regs[index] = value;
+
+        break;
+    }
+
+    imx_spi_update_irq(s);
+}
+
+static const struct MemoryRegionOps imx_spi_ops = {
+    .read = imx_spi_read,
+    .write = imx_spi_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        /*
+         * Our device would not work correctly if the guest was doing
+         * unaligned access. This might not be a limitation on the real
+         * device but in practice there is no reason for a guest to access
+         * this device unaligned.
+         */
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void imx_spi_realize(DeviceState *dev, Error **errp)
+{
+    IMXSPIState *s = IMX_SPI(dev);
+    int i;
+
+    s->bus = ssi_create_bus(dev, "spi");
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_spi_ops, s,
+                          TYPE_IMX_SPI, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+    ssi_auto_connect_slaves(dev, s->cs_lines, s->bus);
+
+    for (i = 0; i < 4; ++i) {
+        sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
+    }
+
+    s->burst_length = 0;
+
+    fifo32_create(&s->tx_fifo, ECSPI_FIFO_SIZE);
+    fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE);
+}
+
+static void imx_spi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = imx_spi_realize;
+    dc->vmsd = &vmstate_imx_spi;
+    dc->reset = imx_spi_reset;
+    dc->desc = "i.MX SPI Controller";
+}
+
+static const TypeInfo imx_spi_info = {
+    .name          = TYPE_IMX_SPI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(IMXSPIState),
+    .class_init    = imx_spi_class_init,
+};
+
+static void imx_spi_register_types(void)
+{
+    type_register_static(&imx_spi_info);
+}
+
+type_init(imx_spi_register_types)
diff --git a/include/hw/ssi/imx_spi.h b/include/hw/ssi/imx_spi.h
new file mode 100644
index 0000000..7103953
--- /dev/null
+++ b/include/hw/ssi/imx_spi.h
@@ -0,0 +1,103 @@
+/*
+ * IMX SPI Controller
+ *
+ * Copyright 2016 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX_SPI_H
+#define IMX_SPI_H
+
+#include "hw/sysbus.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/bitops.h"
+#include "qemu/fifo32.h"
+
+#define ECSPI_FIFO_SIZE 64
+
+#define ECSPI_RXDATA 0
+#define ECSPI_TXDATA 1
+#define ECSPI_CONREG 2
+#define ECSPI_CONFIGREG 3
+#define ECSPI_INTREG 4
+#define ECSPI_DMAREG 5
+#define ECSPI_STATREG 6
+#define ECSPI_PERIODREG 7
+#define ECSPI_TESTREG 8
+#define ECSPI_MSGDATA 16
+#define ECSPI_MAX 17
+
+/* ECSPI_CONREG */
+#define ECSPI_CONREG_EN (1 << 0)
+#define ECSPI_CONREG_HT (1 << 1)
+#define ECSPI_CONREG_XCH (1 << 2)
+#define ECSPI_CONREG_SMC (1 << 3)
+#define ECSPI_CONREG_CHANNEL_MODE_SHIFT 4
+#define ECSPI_CONREG_CHANNEL_MODE_LENGTH 4
+#define ECSPI_CONREG_DRCTL_SHIFT 16
+#define ECSPI_CONREG_DRCTL_LENGTH 2
+#define ECSPI_CONREG_CHANNEL_SELECT_SHIFT 18
+#define ECSPI_CONREG_CHANNEL_SELECT_LENGTH 2
+#define ECSPI_CONREG_BURST_LENGTH_SHIFT 20
+#define ECSPI_CONREG_BURST_LENGTH_LENGTH 12
+
+/* ECSPI_CONFIGREG */
+#define ECSPI_CONFIGREG_SS_CTL_SHIFT 8
+#define ECSPI_CONFIGREG_SS_CTL_LENGTH 4
+
+/* ECSPI_INTREG */
+#define ECSPI_INTREG_TEEN (1 << 0)
+#define ECSPI_INTREG_TDREN (1 << 1)
+#define ECSPI_INTREG_TFEN (1 << 2)
+#define ECSPI_INTREG_RREN (1 << 3)
+#define ECSPI_INTREG_RDREN (1 << 4)
+#define ECSPI_INTREG_RFEN (1 << 5)
+#define ECSPI_INTREG_ROEN (1 << 6)
+#define ECSPI_INTREG_TCEN (1 << 7)
+
+/* ECSPI_DMAREG */
+#define ECSPI_DMAREG_RXTDEN (1 << 31)
+#define ECSPI_DMAREG_RXDEN (1 << 23)
+#define ECSPI_DMAREG_TEDEN (1 << 7)
+#define ECSPI_DMAREG_RX_THRESHOLD_SHIFT 16
+#define ECSPI_DMAREG_RX_THRESHOLD_LENGTH 6
+
+/* ECSPI_STATREG */
+#define ECSPI_STATREG_TE (1 << 0)
+#define ECSPI_STATREG_TDR (1 << 1)
+#define ECSPI_STATREG_TF (1 << 2)
+#define ECSPI_STATREG_RR (1 << 3)
+#define ECSPI_STATREG_RDR (1 << 4)
+#define ECSPI_STATREG_RF (1 << 5)
+#define ECSPI_STATREG_RO (1 << 6)
+#define ECSPI_STATREG_TC (1 << 7)
+
+#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH)
+
+#define TYPE_IMX_SPI "imx.spi"
+#define IMX_SPI(obj) OBJECT_CHECK(IMXSPIState, (obj), TYPE_IMX_SPI)
+
+typedef struct IMXSPIState {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion iomem;
+
+    qemu_irq irq;
+
+    qemu_irq cs_lines[4];
+
+    SSIBus *bus;
+
+    uint32_t regs[ECSPI_MAX];
+
+    Fifo32 rx_fifo;
+    Fifo32 tx_fifo;
+
+    int16_t burst_length;
+} IMXSPIState;
+
+#endif /* IMX_SPI_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 5/6] i.MX: Add i.MX6 SOC implementation.
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (3 preceding siblings ...)
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 4/6] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
  5 siblings, 0 replies; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

For now we only support the following devices:
* up to 4 Cortex A9 cores
* A9 MPCORE (SCU, GIC, TWD)
* 5 i.MX UARTs
* 2 EPIT timers
* 1 GPT timer
* 3 I2C controllers
* 7 GPIO controllers
* 6 SDHC controllers
* 5 SPI controllers
* 1 CCM device
* 1 SRC device
* various ROM/RAM areas.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since V1:
 * use g_strdup_printf/g_free instead of local char array.
 * output a message on exit for unsupported number of cores.

Changes since V2:
 * Added Include "osdep/qemu.h"
 * Added support for EL3
 * Added SPI controllers
 * Added a property for each controller.
 
Changes since V3:
 * None.

 default-configs/arm-softmmu.mak |   1 +
 hw/arm/Makefile.objs            |   1 +
 hw/arm/fsl-imx6.c               | 449 +++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx6.h       | 450 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 901 insertions(+)
 create mode 100644 hw/arm/fsl-imx6.c
 create mode 100644 include/hw/arm/fsl-imx6.h

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 2bcd236..d4bfca2 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -100,6 +100,7 @@ CONFIG_ALLWINNER_A10_PIT=y
 CONFIG_ALLWINNER_A10_PIC=y
 CONFIG_ALLWINNER_A10=y
 
+CONFIG_FSL_IMX6=y
 CONFIG_FSL_IMX31=y
 CONFIG_FSL_IMX25=y
 
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 954c9fe..2b20e49 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,4 +16,5 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
 obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
+obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
 obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
new file mode 100644
index 0000000..b8e93a6
--- /dev/null
+++ b/hw/arm/fsl-imx6.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * i.MX6 SOC emulation.
+ *
+ * Based on hw/arm/fsl-imx31.c
+ *
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/arm/fsl-imx6.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "sysemu/char.h"
+#include "qemu/error-report.h"
+
+#define NAME_SIZE 20
+
+static void fsl_imx6_init(Object *obj)
+{
+    FslIMX6State *s = FSL_IMX6(obj);
+    char name[NAME_SIZE];
+    int i;
+
+    if (smp_cpus > FSL_IMX6_NUM_CPUS) {
+        error_report("%s: Only %d CPUs are supported (%d requested)",
+                     TYPE_FSL_IMX6, FSL_IMX6_NUM_CPUS, smp_cpus);
+        exit(1);
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
+                          "cortex-a9-" TYPE_ARM_CPU);
+        snprintf(name, NAME_SIZE, "cpu%d", i);
+        object_property_add_child(obj, name, OBJECT(&s->cpu[i]), NULL);
+    }
+
+    object_initialize(&s->a9mpcore, sizeof(s->a9mpcore), TYPE_A9MPCORE_PRIV);
+    qdev_set_parent_bus(DEVICE(&s->a9mpcore), sysbus_get_default());
+    object_property_add_child(obj, "a9mpcore", OBJECT(&s->a9mpcore), NULL);
+
+    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM);
+    qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
+    object_property_add_child(obj, "ccm", OBJECT(&s->ccm), NULL);
+
+    object_initialize(&s->src, sizeof(s->src), TYPE_IMX6_SRC);
+    qdev_set_parent_bus(DEVICE(&s->src), sysbus_get_default());
+    object_property_add_child(obj, "src", OBJECT(&s->src), NULL);
+
+    for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) {
+        object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+        qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "uart%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL);
+    }
+
+    object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT);
+    qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default());
+    object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL);
+
+    for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) {
+        object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT);
+        qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "epit%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->epit[i]), NULL);
+    }
+
+    for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) {
+        object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+        qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "i2c%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
+    }
+
+    for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) {
+        object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO);
+        qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "gpio%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->gpio[i]), NULL);
+    }
+
+    for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) {
+        object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI);
+        qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "sdhc%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL);
+    }
+
+    for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) {
+        object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
+        qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "spi%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
+    }
+}
+
+static void fsl_imx6_realize(DeviceState *dev, Error **errp)
+{
+    FslIMX6State *s = FSL_IMX6(dev);
+    uint16_t i;
+    Error *err = NULL;
+
+    for (i = 0; i < smp_cpus; i++) {
+
+        /* On uniprocessor, the CBAR is set to 0 */
+        if (smp_cpus > 1) {
+            object_property_set_int(OBJECT(&s->cpu[i]), FSL_IMX6_A9MPCORE_ADDR,
+                                    "reset-cbar", &error_abort);
+        }
+
+        /* All CPU but CPU 0 start in power off mode */
+        if (i) {
+            object_property_set_bool(OBJECT(&s->cpu[i]), true,
+                                     "start-powered-off", &error_abort);
+        }
+
+        object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+
+    object_property_set_int(OBJECT(&s->a9mpcore), smp_cpus, "num-cpu",
+                            &error_abort);
+
+    object_property_set_int(OBJECT(&s->a9mpcore),
+                            FSL_IMX6_MAX_IRQ + GIC_INTERNAL, "num-irq",
+                            &error_abort);
+
+    object_property_set_bool(OBJECT(&s->a9mpcore), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, FSL_IMX6_A9MPCORE_ADDR);
+
+    for (i = 0; i < smp_cpus; i++) {
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + smp_cpus,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
+    }
+
+    object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX6_CCM_ADDR);
+
+    object_property_set_bool(OBJECT(&s->src), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0, FSL_IMX6_SRC_ADDR);
+
+    /* Initialize all UARTs */
+    for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq;
+        } serial_table[FSL_IMX6_NUM_UARTS] = {
+            { FSL_IMX6_UART1_ADDR, FSL_IMX6_UART1_IRQ },
+            { FSL_IMX6_UART2_ADDR, FSL_IMX6_UART2_IRQ },
+            { FSL_IMX6_UART3_ADDR, FSL_IMX6_UART3_IRQ },
+            { FSL_IMX6_UART4_ADDR, FSL_IMX6_UART4_IRQ },
+            { FSL_IMX6_UART5_ADDR, FSL_IMX6_UART5_IRQ },
+        };
+
+        if (i < MAX_SERIAL_PORTS) {
+            CharDriverState *chr;
+
+            chr = serial_hds[i];
+
+            if (!chr) {
+                char *label = g_strdup_printf("imx6.uart%d", i + 1);
+                chr = qemu_chr_new(label, "null", NULL);
+                g_free(label);
+                serial_hds[i] = chr;
+            }
+
+            qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
+        }
+
+        object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            serial_table[i].irq));
+    }
+
+    s->gpt.ccm = IMX_CCM(&s->ccm);
+
+    object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX6_GPT_ADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0,
+                       qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                        FSL_IMX6_GPT_IRQ));
+
+    /* Initialize all EPIT timers */
+    for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq;
+        } epit_table[FSL_IMX6_NUM_EPITS] = {
+            { FSL_IMX6_EPIT1_ADDR, FSL_IMX6_EPIT1_IRQ },
+            { FSL_IMX6_EPIT2_ADDR, FSL_IMX6_EPIT2_IRQ },
+        };
+
+        s->epit[i].ccm = IMX_CCM(&s->ccm);
+
+        object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            epit_table[i].irq));
+    }
+
+    /* Initialize all I2C */
+    for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq;
+        } i2c_table[FSL_IMX6_NUM_I2CS] = {
+            { FSL_IMX6_I2C1_ADDR, FSL_IMX6_I2C1_IRQ },
+            { FSL_IMX6_I2C2_ADDR, FSL_IMX6_I2C2_IRQ },
+            { FSL_IMX6_I2C3_ADDR, FSL_IMX6_I2C3_IRQ }
+        };
+
+        object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            i2c_table[i].irq));
+    }
+
+    /* Initialize all GPIOs */
+    for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq_low;
+            unsigned int irq_high;
+        } gpio_table[FSL_IMX6_NUM_GPIOS] = {
+            {
+                FSL_IMX6_GPIO1_ADDR,
+                FSL_IMX6_GPIO1_LOW_IRQ,
+                FSL_IMX6_GPIO1_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO2_ADDR,
+                FSL_IMX6_GPIO2_LOW_IRQ,
+                FSL_IMX6_GPIO2_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO3_ADDR,
+                FSL_IMX6_GPIO3_LOW_IRQ,
+                FSL_IMX6_GPIO3_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO4_ADDR,
+                FSL_IMX6_GPIO4_LOW_IRQ,
+                FSL_IMX6_GPIO4_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO5_ADDR,
+                FSL_IMX6_GPIO5_LOW_IRQ,
+                FSL_IMX6_GPIO5_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO6_ADDR,
+                FSL_IMX6_GPIO6_LOW_IRQ,
+                FSL_IMX6_GPIO6_HIGH_IRQ
+            },
+            {
+                FSL_IMX6_GPIO7_ADDR,
+                FSL_IMX6_GPIO7_LOW_IRQ,
+                FSL_IMX6_GPIO7_HIGH_IRQ
+            },
+        };
+
+        object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-edge-sel",
+                                 &error_abort);
+        object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-upper-pin-irq",
+                                 &error_abort);
+        object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            gpio_table[i].irq_low));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            gpio_table[i].irq_high));
+    }
+
+    /* Initialize all SDHC */
+    for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq;
+        } esdhc_table[FSL_IMX6_NUM_ESDHCS] = {
+            { FSL_IMX6_uSDHC1_ADDR, FSL_IMX6_uSDHC1_IRQ },
+            { FSL_IMX6_uSDHC2_ADDR, FSL_IMX6_uSDHC2_IRQ },
+            { FSL_IMX6_uSDHC3_ADDR, FSL_IMX6_uSDHC3_IRQ },
+            { FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ },
+        };
+
+        object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            esdhc_table[i].irq));
+    }
+
+    /* Initialize all ECSPI */
+    for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) {
+        static const struct {
+            hwaddr addr;
+            unsigned int irq;
+        } spi_table[FSL_IMX6_NUM_ECSPIS] = {
+            { FSL_IMX6_eCSPI1_ADDR, FSL_IMX6_ECSPI1_IRQ },
+            { FSL_IMX6_eCSPI2_ADDR, FSL_IMX6_ECSPI2_IRQ },
+            { FSL_IMX6_eCSPI3_ADDR, FSL_IMX6_ECSPI3_IRQ },
+            { FSL_IMX6_eCSPI4_ADDR, FSL_IMX6_ECSPI4_IRQ },
+            { FSL_IMX6_eCSPI5_ADDR, FSL_IMX6_ECSPI5_IRQ },
+        };
+
+        /* Initialize the SPI */
+        object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a9mpcore),
+                                            spi_table[i].irq));
+    }
+
+    /* ROM memory */
+    memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom",
+                                  FSL_IMX6_ROM_SIZE, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(get_system_memory(), FSL_IMX6_ROM_ADDR,
+                                &s->rom);
+
+    /* CAAM memory */
+    memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam",
+                                  FSL_IMX6_CAAM_MEM_SIZE, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(get_system_memory(), FSL_IMX6_CAAM_MEM_ADDR,
+                                &s->caam);
+
+    /* OCRAM memory */
+    memory_region_init_ram(&s->ocram, NULL, "imx6.ocram", FSL_IMX6_OCRAM_SIZE,
+                           &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ADDR,
+                                &s->ocram);
+    vmstate_register_ram_global(&s->ocram);
+
+    /* internal OCRAM (256 KB) is aliased over 1 MB */
+    memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias",
+                             &s->ocram, 0, FSL_IMX6_OCRAM_ALIAS_SIZE);
+    memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ALIAS_ADDR,
+                                &s->ocram_alias);
+}
+
+static void fsl_imx6_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = fsl_imx6_realize;
+
+    /*
+     * Reason: creates an ARM CPU, thus use after free(), see
+     * arm_cpu_class_init()
+     */
+    dc->cannot_destroy_with_object_finalize_yet = true;
+    dc->desc = "i.MX6 SOC";
+}
+
+static const TypeInfo fsl_imx6_type_info = {
+    .name = TYPE_FSL_IMX6,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(FslIMX6State),
+    .instance_init = fsl_imx6_init,
+    .class_init = fsl_imx6_class_init,
+};
+
+static void fsl_imx6_register_types(void)
+{
+    type_register_static(&fsl_imx6_type_info);
+}
+
+type_init(fsl_imx6_register_types)
diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h
new file mode 100644
index 0000000..d24aaee
--- /dev/null
+++ b/include/hw/arm/fsl-imx6.h
@@ -0,0 +1,450 @@
+/*
+ * Freescale i.MX31 SoC emulation
+ *
+ * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * 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.
+ */
+
+#ifndef FSL_IMX6_H
+#define FSL_IMX6_H
+
+#include "hw/arm/arm.h"
+#include "hw/cpu/a9mpcore.h"
+#include "hw/misc/imx6_ccm.h"
+#include "hw/misc/imx6_src.h"
+#include "hw/char/imx_serial.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/sd/sdhci.h"
+#include "hw/ssi/imx_spi.h"
+#include "exec/memory.h"
+
+#define TYPE_FSL_IMX6 "fsl,imx6"
+#define FSL_IMX6(obj) OBJECT_CHECK(FslIMX6State, (obj), TYPE_FSL_IMX6)
+
+#define FSL_IMX6_NUM_CPUS 4
+#define FSL_IMX6_NUM_UARTS 5
+#define FSL_IMX6_NUM_EPITS 2
+#define FSL_IMX6_NUM_I2CS 3
+#define FSL_IMX6_NUM_GPIOS 7
+#define FSL_IMX6_NUM_ESDHCS 4
+#define FSL_IMX6_NUM_ECSPIS 5
+
+typedef struct FslIMX6State {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    ARMCPU         cpu[FSL_IMX6_NUM_CPUS];
+    A9MPPrivState  a9mpcore;
+    IMX6CCMState   ccm;
+    IMX6SRCState   src;
+    IMXSerialState uart[FSL_IMX6_NUM_UARTS];
+    IMXGPTState    gpt;
+    IMXEPITState   epit[FSL_IMX6_NUM_EPITS];
+    IMXI2CState    i2c[FSL_IMX6_NUM_I2CS];
+    IMXGPIOState   gpio[FSL_IMX6_NUM_GPIOS];
+    SDHCIState     esdhc[FSL_IMX6_NUM_ESDHCS];
+    IMXSPIState    spi[FSL_IMX6_NUM_ECSPIS];
+    MemoryRegion   rom;
+    MemoryRegion   caam;
+    MemoryRegion   ocram;
+    MemoryRegion   ocram_alias;
+} FslIMX6State;
+
+
+#define FSL_IMX6_MMDC_ADDR 0x10000000
+#define FSL_IMX6_MMDC_SIZE 0xF0000000
+#define FSL_IMX6_EIM_MEM_ADDR 0x08000000
+#define FSL_IMX6_EIM_MEM_SIZE 0x8000000
+#define FSL_IMX6_IPU_2_ADDR 0x02800000
+#define FSL_IMX6_IPU_2_SIZE 0x400000
+#define FSL_IMX6_IPU_1_ADDR 0x02400000
+#define FSL_IMX6_IPU_1_SIZE 0x400000
+#define FSL_IMX6_MIPI_HSI_ADDR 0x02208000
+#define FSL_IMX6_MIPI_HSI_SIZE 0x4000
+#define FSL_IMX6_OPENVG_ADDR 0x02204000
+#define FSL_IMX6_OPENVG_SIZE 0x4000
+#define FSL_IMX6_SATA_ADDR 0x02200000
+#define FSL_IMX6_SATA_SIZE 0x4000
+#define FSL_IMX6_AIPS_2_ADDR 0x02100000
+#define FSL_IMX6_AIPS_2_SIZE 0x100000
+/* AIPS2 */
+#define FSL_IMX6_UART5_ADDR 0x021F4000
+#define FSL_IMX6_UART5_SIZE 0x4000
+#define FSL_IMX6_UART4_ADDR 0x021F0000
+#define FSL_IMX6_UART4_SIZE 0x4000
+#define FSL_IMX6_UART3_ADDR 0x021EC000
+#define FSL_IMX6_UART3_SIZE 0x4000
+#define FSL_IMX6_UART2_ADDR 0x021E8000
+#define FSL_IMX6_UART2_SIZE 0x4000
+#define FSL_IMX6_VDOA_ADDR 0x021E4000
+#define FSL_IMX6_VDOA_SIZE 0x4000
+#define FSL_IMX6_MIPI_DSI_ADDR 0x021E0000
+#define FSL_IMX6_MIPI_DSI_SIZE 0x4000
+#define FSL_IMX6_MIPI_CSI_ADDR 0x021DC000
+#define FSL_IMX6_MIPI_CSI_SIZE 0x4000
+#define FSL_IMX6_AUDMUX_ADDR 0x021D8000
+#define FSL_IMX6_AUDMUX_SIZE 0x4000
+#define FSL_IMX6_TZASC2_ADDR 0x021D4000
+#define FSL_IMX6_TZASC2_SIZE 0x4000
+#define FSL_IMX6_TZASC1_ADDR 0x021D0000
+#define FSL_IMX6_TZASC1_SIZE 0x4000
+#define FSL_IMX6_CSU_ADDR 0x021C0000
+#define FSL_IMX6_CSU_SIZE 0x4000
+#define FSL_IMX6_OCOTPCTRL_ADDR 0x021BC000
+#define FSL_IMX6_OCOTPCTRL_SIZE 0x4000
+#define FSL_IMX6_EIM_ADDR 0x021B8000
+#define FSL_IMX6_EIM_SIZE 0x4000
+#define FSL_IMX6_MMDC1_ADDR 0x021B4000
+#define FSL_IMX6_MMDC1_SIZE 0x4000
+#define FSL_IMX6_MMDC0_ADDR 0x021B0000
+#define FSL_IMX6_MMDC0_SIZE 0x4000
+#define FSL_IMX6_ROMCP_ADDR 0x021AC000
+#define FSL_IMX6_ROMCP_SIZE 0x4000
+#define FSL_IMX6_I2C3_ADDR 0x021A8000
+#define FSL_IMX6_I2C3_SIZE 0x4000
+#define FSL_IMX6_I2C2_ADDR 0x021A4000
+#define FSL_IMX6_I2C2_SIZE 0x4000
+#define FSL_IMX6_I2C1_ADDR 0x021A0000
+#define FSL_IMX6_I2C1_SIZE 0x4000
+#define FSL_IMX6_uSDHC4_ADDR 0x0219C000
+#define FSL_IMX6_uSDHC4_SIZE 0x4000
+#define FSL_IMX6_uSDHC3_ADDR 0x02198000
+#define FSL_IMX6_uSDHC3_SIZE 0x4000
+#define FSL_IMX6_uSDHC2_ADDR 0x02194000
+#define FSL_IMX6_uSDHC2_SIZE 0x4000
+#define FSL_IMX6_uSDHC1_ADDR 0x02190000
+#define FSL_IMX6_uSDHC1_SIZE 0x4000
+#define FSL_IMX6_MLB150_ADDR 0x0218C000
+#define FSL_IMX6_MLB150_SIZE 0x4000
+#define FSL_IMX6_ENET_ADDR 0x02188000
+#define FSL_IMX6_ENET_SIZE 0x4000
+#define FSL_IMX6_USBOH3_USB_ADDR 0x02184000
+#define FSL_IMX6_USBOH3_USB_SIZE 0x4000
+#define FSL_IMX6_AIPS2_CFG_ADDR 0x0217C000
+#define FSL_IMX6_AIPS2_CFG_SIZE 0x4000
+/* DAP */
+#define FSL_IMX6_PTF_CTRL_ADDR 0x02160000
+#define FSL_IMX6_PTF_CTRL_SIZE 0x1000
+#define FSL_IMX6_PTM3_ADDR 0x0215F000
+#define FSL_IMX6_PTM3_SIZE 0x1000
+#define FSL_IMX6_PTM2_ADDR 0x0215E000
+#define FSL_IMX6_PTM2_SIZE 0x1000
+#define FSL_IMX6_PTM1_ADDR 0x0215D000
+#define FSL_IMX6_PTM1_SIZE 0x1000
+#define FSL_IMX6_PTM0_ADDR 0x0215C000
+#define FSL_IMX6_PTM0_SIZE 0x1000
+#define FSL_IMX6_CTI3_ADDR 0x0215B000
+#define FSL_IMX6_CTI3_SIZE 0x1000
+#define FSL_IMX6_CTI2_ADDR 0x0215A000
+#define FSL_IMX6_CTI2_SIZE 0x1000
+#define FSL_IMX6_CTI1_ADDR 0x02159000
+#define FSL_IMX6_CTI1_SIZE 0x1000
+#define FSL_IMX6_CTI0_ADDR 0x02158000
+#define FSL_IMX6_CTI0_SIZE 0x1000
+#define FSL_IMX6_CPU3_PMU_ADDR 0x02157000
+#define FSL_IMX6_CPU3_PMU_SIZE 0x1000
+#define FSL_IMX6_CPU3_DEBUG_IF_ADDR 0x02156000
+#define FSL_IMX6_CPU3_DEBUG_IF_SIZE 0x1000
+#define FSL_IMX6_CPU2_PMU_ADDR 0x02155000
+#define FSL_IMX6_CPU2_PMU_SIZE 0x1000
+#define FSL_IMX6_CPU2_DEBUG_IF_ADDR 0x02154000
+#define FSL_IMX6_CPU2_DEBUG_IF_SIZE 0x1000
+#define FSL_IMX6_CPU1_PMU_ADDR 0x02153000
+#define FSL_IMX6_CPU1_PMU_SIZE 0x1000
+#define FSL_IMX6_CPU1_DEBUG_IF_ADDR 0x02152000
+#define FSL_IMX6_CPU1_DEBUG_IF_SIZE 0x1000
+#define FSL_IMX6_CPU0_PMU_ADDR 0x02151000
+#define FSL_IMX6_CPU0_PMU_SIZE 0x1000
+#define FSL_IMX6_CPU0_DEBUG_IF_ADDR 0x02150000
+#define FSL_IMX6_CPU0_DEBUG_IF_SIZE 0x1000
+#define FSL_IMX6_CA9_INTEG_ADDR 0x0214F000
+#define FSL_IMX6_CA9_INTEG_SIZE 0x1000
+#define FSL_IMX6_FUNNEL_ADDR 0x02144000
+#define FSL_IMX6_FUNNEL_SIZE 0x1000
+#define FSL_IMX6_TPIU_ADDR 0x02143000
+#define FSL_IMX6_TPIU_SIZE 0x1000
+#define FSL_IMX6_EXT_CTI_ADDR 0x02142000
+#define FSL_IMX6_EXT_CTI_SIZE 0x1000
+#define FSL_IMX6_ETB_ADDR 0x02141000
+#define FSL_IMX6_ETB_SIZE 0x1000
+#define FSL_IMX6_DAP_ROM_TABLE_ADDR 0x02140000
+#define FSL_IMX6_DAP_ROM_TABLE_SIZE 0x1000
+/* DAP end */
+#define FSL_IMX6_CAAM_ADDR 0x02100000
+#define FSL_IMX6_CAAM_SIZE 0x10000
+/* AIPS2 end */
+#define FSL_IMX6_AIPS_1_ADDR 0x02000000
+#define FSL_IMX6_AIPS_1_SIZE 0x100000
+/* AIPS1 */
+#define FSL_IMX6_SDMA_ADDR 0x020EC000
+#define FSL_IMX6_SDMA_SIZE 0x4000
+#define FSL_IMX6_DCIC2_ADDR 0x020E8000
+#define FSL_IMX6_DCIC2_SIZE 0x4000
+#define FSL_IMX6_DCIC1_ADDR 0x020E4000
+#define FSL_IMX6_DCIC1_SIZE 0x4000
+#define FSL_IMX6_IOMUXC_ADDR 0x020E0000
+#define FSL_IMX6_IOMUXC_SIZE 0x4000
+#define FSL_IMX6_PGCARM_ADDR 0x020DCA00
+#define FSL_IMX6_PGCARM_SIZE 0x20
+#define FSL_IMX6_PGCPU_ADDR 0x020DC260
+#define FSL_IMX6_PGCPU_SIZE 0x20
+#define FSL_IMX6_GPC_ADDR 0x020DC000
+#define FSL_IMX6_GPC_SIZE 0x4000
+#define FSL_IMX6_SRC_ADDR 0x020D8000
+#define FSL_IMX6_SRC_SIZE 0x4000
+#define FSL_IMX6_EPIT2_ADDR 0x020D4000
+#define FSL_IMX6_EPIT2_SIZE 0x4000
+#define FSL_IMX6_EPIT1_ADDR 0x020D0000
+#define FSL_IMX6_EPIT1_SIZE 0x4000
+#define FSL_IMX6_SNVSHP_ADDR 0x020CC000
+#define FSL_IMX6_SNVSHP_SIZE 0x4000
+#define FSL_IMX6_USBPHY2_ADDR 0x020CA000
+#define FSL_IMX6_USBPHY2_SIZE 0x1000
+#define FSL_IMX6_USBPHY1_ADDR 0x020C9000
+#define FSL_IMX6_USBPHY1_SIZE 0x1000
+#define FSL_IMX6_ANALOG_ADDR 0x020C8000
+#define FSL_IMX6_ANALOG_SIZE 0x1000
+#define FSL_IMX6_CCM_ADDR 0x020C4000
+#define FSL_IMX6_CCM_SIZE 0x4000
+#define FSL_IMX6_WDOG2_ADDR 0x020C0000
+#define FSL_IMX6_WDOG2_SIZE 0x4000
+#define FSL_IMX6_WDOG1_ADDR 0x020BC000
+#define FSL_IMX6_WDOG1_SIZE 0x4000
+#define FSL_IMX6_KPP_ADDR 0x020B8000
+#define FSL_IMX6_KPP_SIZE 0x4000
+#define FSL_IMX6_GPIO7_ADDR 0x020B4000
+#define FSL_IMX6_GPIO7_SIZE 0x4000
+#define FSL_IMX6_GPIO6_ADDR 0x020B0000
+#define FSL_IMX6_GPIO6_SIZE 0x4000
+#define FSL_IMX6_GPIO5_ADDR 0x020AC000
+#define FSL_IMX6_GPIO5_SIZE 0x4000
+#define FSL_IMX6_GPIO4_ADDR 0x020A8000
+#define FSL_IMX6_GPIO4_SIZE 0x4000
+#define FSL_IMX6_GPIO3_ADDR 0x020A4000
+#define FSL_IMX6_GPIO3_SIZE 0x4000
+#define FSL_IMX6_GPIO2_ADDR 0x020A0000
+#define FSL_IMX6_GPIO2_SIZE 0x4000
+#define FSL_IMX6_GPIO1_ADDR 0x0209C000
+#define FSL_IMX6_GPIO1_SIZE 0x4000
+#define FSL_IMX6_GPT_ADDR 0x02098000
+#define FSL_IMX6_GPT_SIZE 0x4000
+#define FSL_IMX6_CAN2_ADDR 0x02094000
+#define FSL_IMX6_CAN2_SIZE 0x4000
+#define FSL_IMX6_CAN1_ADDR 0x02090000
+#define FSL_IMX6_CAN1_SIZE 0x4000
+#define FSL_IMX6_PWM4_ADDR 0x0208C000
+#define FSL_IMX6_PWM4_SIZE 0x4000
+#define FSL_IMX6_PWM3_ADDR 0x02088000
+#define FSL_IMX6_PWM3_SIZE 0x4000
+#define FSL_IMX6_PWM2_ADDR 0x02084000
+#define FSL_IMX6_PWM2_SIZE 0x4000
+#define FSL_IMX6_PWM1_ADDR 0x02080000
+#define FSL_IMX6_PWM1_SIZE 0x4000
+#define FSL_IMX6_AIPS1_CFG_ADDR 0x0207C000
+#define FSL_IMX6_AIPS1_CFG_SIZE 0x4000
+#define FSL_IMX6_VPU_ADDR 0x02040000
+#define FSL_IMX6_VPU_SIZE 0x3C000
+#define FSL_IMX6_AIPS1_SPBA_ADDR 0x0203C000
+#define FSL_IMX6_AIPS1_SPBA_SIZE 0x4000
+#define FSL_IMX6_ASRC_ADDR 0x02034000
+#define FSL_IMX6_ASRC_SIZE 0x4000
+#define FSL_IMX6_SSI3_ADDR 0x02030000
+#define FSL_IMX6_SSI3_SIZE 0x4000
+#define FSL_IMX6_SSI2_ADDR 0x0202C000
+#define FSL_IMX6_SSI2_SIZE 0x4000
+#define FSL_IMX6_SSI1_ADDR 0x02028000
+#define FSL_IMX6_SSI1_SIZE 0x4000
+#define FSL_IMX6_ESAI_ADDR 0x02024000
+#define FSL_IMX6_ESAI_SIZE 0x4000
+#define FSL_IMX6_UART1_ADDR 0x02020000
+#define FSL_IMX6_UART1_SIZE 0x4000
+#define FSL_IMX6_eCSPI5_ADDR 0x02018000
+#define FSL_IMX6_eCSPI5_SIZE 0x4000
+#define FSL_IMX6_eCSPI4_ADDR 0x02014000
+#define FSL_IMX6_eCSPI4_SIZE 0x4000
+#define FSL_IMX6_eCSPI3_ADDR 0x02010000
+#define FSL_IMX6_eCSPI3_SIZE 0x4000
+#define FSL_IMX6_eCSPI2_ADDR 0x0200C000
+#define FSL_IMX6_eCSPI2_SIZE 0x4000
+#define FSL_IMX6_eCSPI1_ADDR 0x02008000
+#define FSL_IMX6_eCSPI1_SIZE 0x4000
+#define FSL_IMX6_SPDIF_ADDR 0x02004000
+#define FSL_IMX6_SPDIF_SIZE 0x4000
+/* AIPS1 end */
+#define FSL_IMX6_PCIe_REG_ADDR 0x01FFC000
+#define FSL_IMX6_PCIe_REG_SIZE 0x4000
+#define FSL_IMX6_PCIe_ADDR 0x01000000
+#define FSL_IMX6_PCIe_SIZE 0xFFC000
+#define FSL_IMX6_GPV_1_PL301_CFG_ADDR 0x00C00000
+#define FSL_IMX6_GPV_1_PL301_CFG_SIZE 0x100000
+#define FSL_IMX6_GPV_0_PL301_CFG_ADDR 0x00B00000
+#define FSL_IMX6_GPV_0_PL301_CFG_SIZE 0x100000
+#define FSL_IMX6_PL310_ADDR 0x00A02000
+#define FSL_IMX6_PL310_SIZE 0x1000
+#define FSL_IMX6_A9MPCORE_ADDR 0x00A00000
+#define FSL_IMX6_A9MPCORE_SIZE 0x2000
+#define FSL_IMX6_OCRAM_ALIAS_ADDR 0x00940000
+#define FSL_IMX6_OCRAM_ALIAS_SIZE 0xC0000
+#define FSL_IMX6_OCRAM_ADDR 0x00900000
+#define FSL_IMX6_OCRAM_SIZE 0x40000
+#define FSL_IMX6_GPV_4_PL301_CFG_ADDR 0x00800000
+#define FSL_IMX6_GPV_4_PL301_CFG_SIZE 0x100000
+#define FSL_IMX6_GPV_3_PL301_CFG_ADDR 0x00300000
+#define FSL_IMX6_GPV_3_PL301_CFG_SIZE 0x100000
+#define FSL_IMX6_GPV_2_PL301_CFG_ADDR 0x00200000
+#define FSL_IMX6_GPV_2_PL301_CFG_SIZE 0x100000
+#define FSL_IMX6_DTCP_ADDR 0x00138000
+#define FSL_IMX6_DTCP_SIZE 0x4000
+#define FSL_IMX6_GPU_2D_ADDR 0x00134000
+#define FSL_IMX6_GPU_2D_SIZE 0x4000
+#define FSL_IMX6_GPU_3D_ADDR 0x00130000
+#define FSL_IMX6_GPU_3D_SIZE 0x4000
+#define FSL_IMX6_HDMI_ADDR 0x00120000
+#define FSL_IMX6_HDMI_SIZE 0x9000
+#define FSL_IMX6_BCH_ADDR 0x00114000
+#define FSL_IMX6_BCH_SIZE 0x4000
+#define FSL_IMX6_GPMI_ADDR 0x00112000
+#define FSL_IMX6_GPMI_SIZE 0x2000
+#define FSL_IMX6_APBH_BRIDGE_DMA_ADDR 0x00110000
+#define FSL_IMX6_APBH_BRIDGE_DMA_SIZE 0x2000
+#define FSL_IMX6_CAAM_MEM_ADDR 0x00100000
+#define FSL_IMX6_CAAM_MEM_SIZE 0x4000
+#define FSL_IMX6_ROM_ADDR 0x00000000
+#define FSL_IMX6_ROM_SIZE 0x18000
+
+#define FSL_IMX6_IOMUXC_IRQ 0
+#define FSL_IMX6_DAP_IRQ 1
+#define FSL_IMX6_SDMA_IRQ 2
+#define FSL_IMX6_VPU_JPEG_IRQ 3
+#define FSL_IMX6_SNVS_PMIC_IRQ 4
+#define FSL_IMX6_IPU1_ERROR_IRQ 5
+#define FSL_IMX6_IPU1_SYNC_IRQ 6
+#define FSL_IMX6_IPU2_ERROR_IRQ 7
+#define FSL_IMX6_IPU2_SYNC_IRQ 8
+#define FSL_IMX6_GPU3D_IRQ 9
+#define FSL_IMX6_R2D_IRQ 10
+#define FSL_IMX6_V2D_IRQ 11
+#define FSL_IMX6_VPU_IRQ 12
+#define FSL_IMX6_APBH_BRIDGE_DMA_IRQ 13
+#define FSL_IMX6_EIM_IRQ 14
+#define FSL_IMX6_BCH_IRQ 15
+#define FSL_IMX6_GPMI_IRQ 16
+#define FSL_IMX6_DTCP_IRQ 17
+#define FSL_IMX6_VDOA_IRQ 18
+#define FSL_IMX6_SNVS_CONS_IRQ 19
+#define FSL_IMX6_SNVS_SEC_IRQ 20
+#define FSL_IMX6_CSU_IRQ 21
+#define FSL_IMX6_uSDHC1_IRQ 22
+#define FSL_IMX6_uSDHC2_IRQ 23
+#define FSL_IMX6_uSDHC3_IRQ 24
+#define FSL_IMX6_uSDHC4_IRQ 25
+#define FSL_IMX6_UART1_IRQ 26
+#define FSL_IMX6_UART2_IRQ 27
+#define FSL_IMX6_UART3_IRQ 28
+#define FSL_IMX6_UART4_IRQ 29
+#define FSL_IMX6_UART5_IRQ 30
+#define FSL_IMX6_ECSPI1_IRQ 31
+#define FSL_IMX6_ECSPI2_IRQ 32
+#define FSL_IMX6_ECSPI3_IRQ 33
+#define FSL_IMX6_ECSPI4_IRQ 34
+#define FSL_IMX6_ECSPI5_IRQ 35
+#define FSL_IMX6_I2C1_IRQ 36
+#define FSL_IMX6_I2C2_IRQ 37
+#define FSL_IMX6_I2C3_IRQ 38
+#define FSL_IMX6_SATA_IRQ 39
+#define FSL_IMX6_USB_HOST1_IRQ 40
+#define FSL_IMX6_USB_HOST2_IRQ 41
+#define FSL_IMX6_USB_HOST3_IRQ 42
+#define FSL_IMX6_USB_OTG_IRQ 43
+#define FSL_IMX6_USB_PHY_UTMI0_IRQ 44
+#define FSL_IMX6_USB_PHY_UTMI1_IRQ 45
+#define FSL_IMX6_SSI1_IRQ 46
+#define FSL_IMX6_SSI2_IRQ 47
+#define FSL_IMX6_SSI3_IRQ 48
+#define FSL_IMX6_TEMP_IRQ 49
+#define FSL_IMX6_ASRC_IRQ 50
+#define FSL_IMX6_ESAI_IRQ 51
+#define FSL_IMX6_SPDIF_IRQ 52
+#define FSL_IMX6_MLB150_IRQ 53
+#define FSL_IMX6_PMU1_IRQ 54
+#define FSL_IMX6_GPT_IRQ 55
+#define FSL_IMX6_EPIT1_IRQ 56
+#define FSL_IMX6_EPIT2_IRQ 57
+#define FSL_IMX6_GPIO1_INT7_IRQ 58
+#define FSL_IMX6_GPIO1_INT6_IRQ 59
+#define FSL_IMX6_GPIO1_INT5_IRQ 60
+#define FSL_IMX6_GPIO1_INT4_IRQ 61
+#define FSL_IMX6_GPIO1_INT3_IRQ 62
+#define FSL_IMX6_GPIO1_INT2_IRQ 63
+#define FSL_IMX6_GPIO1_INT1_IRQ 64
+#define FSL_IMX6_GPIO1_INT0_IRQ 65
+#define FSL_IMX6_GPIO1_LOW_IRQ 66
+#define FSL_IMX6_GPIO1_HIGH_IRQ 67
+#define FSL_IMX6_GPIO2_LOW_IRQ 68
+#define FSL_IMX6_GPIO2_HIGH_IRQ 69
+#define FSL_IMX6_GPIO3_LOW_IRQ 70
+#define FSL_IMX6_GPIO3_HIGH_IRQ 71
+#define FSL_IMX6_GPIO4_LOW_IRQ 72
+#define FSL_IMX6_GPIO4_HIGH_IRQ 73
+#define FSL_IMX6_GPIO5_LOW_IRQ 74
+#define FSL_IMX6_GPIO5_HIGH_IRQ 75
+#define FSL_IMX6_GPIO6_LOW_IRQ 76
+#define FSL_IMX6_GPIO6_HIGH_IRQ 77
+#define FSL_IMX6_GPIO7_LOW_IRQ 78
+#define FSL_IMX6_GPIO7_HIGH_IRQ 79
+#define FSL_IMX6_WDOG1_IRQ 80
+#define FSL_IMX6_WDOG2_IRQ 81
+#define FSL_IMX6_KPP_IRQ 82
+#define FSL_IMX6_PWM1_IRQ 83
+#define FSL_IMX6_PWM2_IRQ 84
+#define FSL_IMX6_PWM3_IRQ 85
+#define FSL_IMX6_PWM4_IRQ 86
+#define FSL_IMX6_CCM1_IRQ 87
+#define FSL_IMX6_CCM2_IRQ 88
+#define FSL_IMX6_GPC_IRQ 89
+#define FSL_IMX6_SRC_IRQ 91
+#define FSL_IMX6_CPU_L2_IRQ 92
+#define FSL_IMX6_CPU_PARITY_IRQ 93
+#define FSL_IMX6_CPU_PERF_IRQ 94
+#define FSL_IMX6_CPU_CTI_IRQ 95
+#define FSL_IMX6_SRC_COMB_IRQ 96
+#define FSL_IMX6_MIPI_CSI1_IRQ 100
+#define FSL_IMX6_MIPI_CSI2_IRQ 101
+#define FSL_IMX6_MIPI_DSI_IRQ 102
+#define FSL_IMX6_MIPI_HSI_IRQ 103
+#define FSL_IMX6_SJC_IRQ 104
+#define FSL_IMX6_CAAM0_IRQ 105
+#define FSL_IMX6_CAAM1_IRQ 106
+#define FSL_IMX6_ASC1_IRQ 108
+#define FSL_IMX6_ASC2_IRQ 109
+#define FSL_IMX6_FLEXCAN1_IRQ 110
+#define FSL_IMX6_FLEXCAN2_IRQ 111
+#define FSL_IMX6_HDMI_MASTER_IRQ 115
+#define FSL_IMX6_HDMI_CEC_IRQ 116
+#define FSL_IMX6_MLB150_LOW_IRQ 117
+#define FSL_IMX6_ENET_MAC_IRQ 118
+#define FSL_IMX6_ENET_MAC_1588_IRQ 119
+#define FSL_IMX6_PCIE1_IRQ 120
+#define FSL_IMX6_PCIE2_IRQ 121
+#define FSL_IMX6_PCIE3_IRQ 122
+#define FSL_IMX6_PCIE4_IRQ 123
+#define FSL_IMX6_DCIC1_IRQ 124
+#define FSL_IMX6_DCIC2_IRQ 125
+#define FSL_IMX6_MLB150_HIGH_IRQ 126
+#define FSL_IMX6_PMU2_IRQ 127
+#define FSL_IMX6_MAX_IRQ 128
+
+#endif /* FSL_IMX6_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (4 preceding siblings ...)
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 5/6] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
@ 2016-03-19 20:59 ` Jean-Christophe Dubois
  2016-03-23 14:48   ` Peter Maydell
  5 siblings, 1 reply; 14+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-19 20:59 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

The sabrelite supports one SPI FLASH memory on SPI1

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since v1:
 * output a message and exit if RAM size is unsupported.

Changes since v2:
 * Added include "qemu/osdep.h"
 * Added access to controllers through properties.

Changes since v3:
 * None

 hw/arm/Makefile.objs |   2 +-
 hw/arm/sabrelite.c   | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 118 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/sabrelite.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2b20e49..12764ef 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,5 +16,5 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
 obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
-obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
+obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
 obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
new file mode 100644
index 0000000..86d79f8
--- /dev/null
+++ b/hw/arm/sabrelite.c
@@ -0,0 +1,117 @@
+/*
+ * SABRELITE Board System emulation.
+ *
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This code is licensed under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a sabrelite board, with a Freescale
+ * i.MX6 SoC
+ */
+
+#include "qemu/osdep.h"
+#include "hw/arm/fsl-imx6.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+#include "net/net.h"
+#include "hw/devices.h"
+#include "hw/char/serial.h"
+#include "sysemu/qtest.h"
+
+typedef struct IMX6Sabrelite {
+    FslIMX6State soc;
+    MemoryRegion ram;
+} IMX6Sabrelite;
+
+static struct arm_boot_info sabrelite_binfo = {
+    /* DDR memory start */
+    .loader_start = FSL_IMX6_MMDC_ADDR,
+    /* No board ID, we boot from DT tree */
+    .board_id = -1,
+};
+
+/* No need to do any particular setup for secondary boot */
+static void sabrelite_write_secondary(ARMCPU *cpu,
+                                      const struct arm_boot_info *info)
+{
+}
+
+/* Secondary cores are reset through SRC device */
+static void sabrelite_reset_secondary(ARMCPU *cpu,
+                                      const struct arm_boot_info *info)
+{
+}
+
+static void sabrelite_init(MachineState *machine)
+{
+    IMX6Sabrelite *s = g_new0(IMX6Sabrelite, 1);
+    Error *err = NULL;
+
+    /* Check the amount of memory is compatible with the SOC */
+    if (machine->ram_size > FSL_IMX6_MMDC_SIZE) {
+        error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
+                     machine->ram_size, FSL_IMX6_MMDC_SIZE);
+        exit(1);
+    }
+
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX6);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+                              &error_abort);
+
+    object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
+    if (err != NULL) {
+        error_report("%s", error_get_pretty(err));
+        exit(1);
+    }
+
+    memory_region_allocate_system_memory(&s->ram, NULL, "sabrelite.ram",
+                                         machine->ram_size);
+    memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR,
+                                &s->ram);
+
+    {
+        /* Add the sst25vf016b NOR FLASH memory to first SPI */
+        Object *spi_dev;
+
+        spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1");
+        if (spi_dev) {
+            SSIBus *spi_bus;
+
+            spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi");
+            if (spi_bus) {
+                DeviceState *flash_dev;
+
+                flash_dev = ssi_create_slave(spi_bus, "sst25vf016b");
+                if (flash_dev) {
+                    qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev,
+                                                              SSI_GPIO_CS, 0);
+                    sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line);
+                }
+            }
+        }
+    }
+
+    sabrelite_binfo.ram_size = machine->ram_size;
+    sabrelite_binfo.kernel_filename = machine->kernel_filename;
+    sabrelite_binfo.kernel_cmdline = machine->kernel_cmdline;
+    sabrelite_binfo.initrd_filename = machine->initrd_filename;
+    sabrelite_binfo.nb_cpus = smp_cpus;
+    sabrelite_binfo.secure_boot = true;
+    sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary;
+    sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary;
+
+    if (!qtest_enabled()) {
+        arm_load_kernel(&s->soc.cpu[0], &sabrelite_binfo);
+    }
+}
+
+static void sabrelite_machine_init(MachineClass *mc)
+{
+    mc->desc = "Freescale i.MX6 Quad SABRE Lite Board (Cortex A9)";
+    mc->init = sabrelite_init;
+    mc->max_cpus = FSL_IMX6_NUM_CPUS;
+}
+
+DEFINE_MACHINE("sabrelite", sabrelite_machine_init)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
@ 2016-03-23 14:48   ` Peter Maydell
  2016-03-24  7:11     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2016-03-23 14:48 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> The sabrelite supports one SPI FLASH memory on SPI1
>
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> +    {
> +        /* Add the sst25vf016b NOR FLASH memory to first SPI */
> +        Object *spi_dev;
> +
> +        spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1");

Can you add a comment to remind us there's a todo here:
 /* Ideally we would expose the chip select and spi bus on the SoC
  * object using alias properties; then we would not need to directly
  * access the underlying spi device object.
  */

Otherwise
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
@ 2016-03-23 15:24   ` Peter Maydell
  2016-03-24  7:10     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2016-03-23 15:24 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> Split ARM on/off function from PSCI support code.
>
> This will allow to reuse these functions in other code.
>
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---
>
> Changes since V1:
>  * Not present on V1
>
> Changes since V2:
>  * Not present on V2
>
> Changes since V3:
>  * Move to a more generic/usefull API
>  * Manage CPU level/mode change at startup
>  * Allow PSCI to cope with EL2/HYP level.
>  * Keep distinct errors for different causes.
>
>  target-arm/Makefile.objs  |   1 +
>  target-arm/arm-powerctl.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++
>  target-arm/arm-powerctl.h |  44 +++++++++
>  target-arm/psci.c         |  69 ++------------
>  4 files changed, 275 insertions(+), 63 deletions(-)
>  create mode 100644 target-arm/arm-powerctl.c
>  create mode 100644 target-arm/arm-powerctl.h
>
> diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
> index a80eb39..60fd1dd 100644
> --- a/target-arm/Makefile.objs
> +++ b/target-arm/Makefile.objs
> @@ -9,3 +9,4 @@ obj-y += neon_helper.o iwmmxt_helper.o
>  obj-y += gdbstub.o
>  obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
>  obj-y += crypto_helper.o
> +obj-y += arm-powerctl.o
> diff --git a/target-arm/arm-powerctl.c b/target-arm/arm-powerctl.c
> new file mode 100644
> index 0000000..8ebb3d8
> --- /dev/null
> +++ b/target-arm/arm-powerctl.c
> @@ -0,0 +1,224 @@
> +/*
> + * QEMU support -- ARM Power Control specific functions.
> + *
> + * Copyright (c) 2016 Jean-Christophe Dubois
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include <cpu.h>
> +#include <cpu-qom.h>
> +#include "internals.h"
> +#include "arm-powerctl.h"
> +
> +#ifndef DEBUG_ARM_POWERCTL
> +#define DEBUG_ARM_POWERCTL 0
> +#endif
> +
> +#define DPRINTF(fmt, args...) \
> +    do { \
> +        if (DEBUG_ARM_POWERCTL) { \
> +            fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
> +        } \
> +    } while (0)
> +
> +CPUState *arm_get_cpu_by_id(uint64_t id)
> +{
> +    CPUState *cpu;
> +
> +    DPRINTF("cpu %" PRId64 "\n", id);
> +
> +    CPU_FOREACH(cpu) {
> +        ARMCPU *armcpu = ARM_CPU(cpu);
> +
> +        if (armcpu->mp_affinity == id) {
> +            return cpu;
> +        }
> +    }
> +
> +    qemu_log_mask(LOG_GUEST_ERROR,
> +                  "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
> +                  __func__, id);
> +
> +    return NULL;
> +}
> +
> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
> +                   int target_el, bool target_aa64)
> +{
> +    CPUState *target_cpu_state;
> +    ARMCPU *target_cpu;
> +    CPUClass *target_cpu_class;
> +
> +    DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
> +            cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
> +            context_id);
> +
> +    /* requested EL level need to be above 0 */
> +    assert(target_el >= 1 && target_el <= 3);
> +
> +    /* change to the cpu we are powering up */
> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
> +    if (!target_cpu_state) {
> +        /* The cpu was not found */
> +        return QEMU_ARM_POWERCTL_INVALID_PARAM;
> +    }
> +    target_cpu = ARM_CPU(target_cpu_state);
> +    if (!target_cpu->powered_off) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "[ARM]%s: CPU %" PRId64 " is already running\n",
> +                      __func__, cpuid);
> +        return QEMU_ARM_POWERCTL_ALREADY_ON;
> +    }
> +    target_cpu_class = CPU_GET_CLASS(target_cpu);
> +
> +    /* Initialize the cpu we are turning on */
> +    cpu_reset(target_cpu_state);
> +    target_cpu->powered_off = false;
> +    target_cpu_state->halted = 0;
> +
> +    /*
> +     * The newly brought CPU is requested to enter the exception level
> +     * "target_el" and be in the requested mode (aarch64 ou aarch32).
> +     */
> +
> +    /*
> +     * Check that the CPU is supporting the requested level
> +     */
> +    switch (target_el) {
> +    case 3:
> +        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
> +            if (is_a64(&target_cpu->env)) {

Why are we looking at the is_a64() state of the CPU as it comes
out of reset? What we care about is target_aa64 here (if we
are putting the CPU into a 64-bit state at a lower exception
level then we must ensure the state is consistent with all
the higher levels being 64-bit).

> +                /* Lower EL3 exception is aarch64 */
> +                target_cpu->env.cp15.scr_el3 |= SCR_RW;
> +                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
> +                    /* Lower EL2 exception is aarch64 */
> +                    target_cpu->env.cp15.hcr_el2 |= HCR_RW;

If you're starting in EL3 you don't need to mess with any of
these registers, because they control register width for
EL2 and EL1. You can let the guest do that itself.

> +                }
> +                target_cpu->env.pstate = PSTATE_MODE_EL3h;

> +            } else {
> +                switch_mode(&target_cpu->env, ARM_CPU_MODE_MON);

switch_mode() is an internal function (and does some register
bank copying we don't need); use
 cpsr_write(&target_cpu->env, ARM_CPU_MODE_MON, CPSR_M, CPSRWriteRaw);


> +            }
> +            /* Processor is in secure mode */
> +            target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
> +        } else {
> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
> +        }
> +        break;
> +    case 2:
> +        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
> +            if (is_a64(&target_cpu->env)) {
> +                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
> +                    /* Lower EL3 exception is aarch64 */
> +                    target_cpu->env.cp15.scr_el3 |= SCR_RW;
> +                }
> +                /* Lower EL2 exception is aarch64 */
> +                target_cpu->env.cp15.hcr_el2 |= HCR_RW;

Again, to go into EL2 you don't need to worry about setting HCR_EL2.RW.

> +                target_cpu->env.pstate = PSTATE_MODE_EL2h;
> +            } else {
> +                switch_mode(&target_cpu->env, ARM_CPU_MODE_HYP);
> +            }
> +            /* Processor is not in secure mode */
> +            target_cpu->env.cp15.scr_el3 |= SCR_NS;
> +        } else {
> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
> +        }
> +        break;
> +    case 1:
> +    default:
> +        if (is_a64(&target_cpu->env)) {
> +            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
> +                /* Lower EL3 exception is aarch64 */
> +                target_cpu->env.cp15.scr_el3 |= SCR_RW;
> +            }
> +            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
> +                /* Lower EL2 exception is aarch64 */
> +                target_cpu->env.cp15.hcr_el2 |= HCR_RW;
> +            }
> +            target_cpu->env.pstate = PSTATE_MODE_EL1h;
> +        } else {
> +            switch_mode(&target_cpu->env, ARM_CPU_MODE_SVC);
> +        }
> +        /* Processor is not in secure mode */
> +        target_cpu->env.cp15.scr_el3 |= SCR_NS;
> +        break;
> +    }
> +
> +    /* We assert if the request cannot be fulfilled */
> +    assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));

So, when can this happen, or is it genuinely "can't happen" ?

> +
> +    /* We check if the started CPU is now in the correct level */
> +    assert(target_el == arm_current_el(&target_cpu->env));
> +
> +    if (target_aa64) {
> +        /* Thumb32 is not supported on AARCH64 */
> +        if (entry & 1) {
> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
> +        }

If you're going to check the PC alignment for 64-bit then you might
as well check that it's 4-aligned.

> +        target_cpu->env.xregs[0] = context_id;
> +    } else {
> +        target_cpu->env.regs[0] = context_id;
> +        target_cpu->env.thumb = entry & 1;
> +    }
> +
> +    /* Start the new CPU at the requested address */
> +    target_cpu_class->set_pc(target_cpu_state, entry);
> +
> +    /* We are good to go */
> +    return QEMU_ARM_POWERCTL_RET_SUCCESS;
> +}
> +
> +void arm_set_cpu_off(uint64_t cpuid)
> +{
> +    CPUState *target_cpu_state;
> +    ARMCPU *target_cpu;
> +
> +    DPRINTF("cpu %" PRId64 "\n", cpuid);
> +
> +    /* change to the cpu we are powering up */
> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
> +    if (!target_cpu_state) {
> +        return;
> +    }
> +    target_cpu = ARM_CPU(target_cpu_state);
> +    if (target_cpu->powered_off) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "[ARM]%s: CPU %" PRId64 " is already off\n",
> +                      __func__, cpuid);
> +        return;
> +    }
> +
> +    target_cpu->powered_off = true;
> +    target_cpu_state->halted = 1;
> +    target_cpu_state->exception_index = EXCP_HLT;
> +    cpu_loop_exit(target_cpu_state);
> +}
> +
> +int arm_reset_cpu(uint64_t cpuid)
> +{
> +    CPUState *target_cpu_state;
> +    ARMCPU *target_cpu;
> +
> +    DPRINTF("cpu %" PRId64 "\n", cpuid);
> +
> +    /* change to the cpu we are resetting */
> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
> +    if (!target_cpu_state) {
> +        return QEMU_ARM_POWERCTL_INVALID_PARAM;
> +    }
> +    target_cpu = ARM_CPU(target_cpu_state);
> +    if (target_cpu->powered_off) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "[ARM]%s: CPU %" PRId64 " is off\n",
> +                      __func__, cpuid);
> +        return -1;
> +    }
> +
> +    /* Reset the cpu */
> +    cpu_reset(target_cpu_state);
> +
> +    return QEMU_ARM_POWERCTL_RET_SUCCESS;
> +}
> diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
> new file mode 100644
> index 0000000..2195483
> --- /dev/null
> +++ b/target-arm/arm-powerctl.h
> @@ -0,0 +1,44 @@
> +/*
> + * QEMU support -- ARM Power Control specific functions.
> + *
> + * Copyright (c) 2016 Jean-Christophe Dubois
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#ifndef QEMU_ARM_POWERCTL_H
> +#define QEMU_ARM_POWERCTL_H
> +
> +#include "kvm-consts.h"
> +
> +#define QEMU_ARM_POWERCTL_RET_SUCCESS QEMU_PSCI_RET_SUCCESS
> +#define QEMU_ARM_POWERCTL_INVALID_PARAM QEMU_PSCI_RET_INVALID_PARAMS
> +#define QEMU_ARM_POWERCTL_ALREADY_ON QEMU_PSCI_RET_ALREADY_ON
> +
> +/*
> + * Retreive a CPUState object from its CPU ID
> + */
> +CPUState *arm_get_cpu_by_id(uint64_t id);

New global functions should all have properly formatted doc comments.

> +
> +/*
> + * Start a give CPU. The CPU will start running at address "entry" with
> + * "context_id" in r0/x0. The CPU should start at exception level "target_el"
> + * and in mode aa64 or aa32 depending on "target_aa64"
> + */
> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
> +                   int target_el, bool target_aa64);

What's the return value? Do we start the CPU secure or nonsecure?
In AArch32, in which mode do we start? We need to either document
these things or let the caller specify.

(For PSCI we want to always start in non-secure, and we want to start
in the same execution state as the calling cpu, which I take to include
the mode. We won't ever try to start a cpu in EL3.)

> +
> +/*
> + * Stop/Power off a given CPU
> + */
> +
> +void arm_set_cpu_off(uint64_t cpuid);
> +
> +/*
> + * Reset a given CPU
> + */
> +int arm_reset_cpu(uint64_t cpuid);
> +
> +#endif
> diff --git a/target-arm/psci.c b/target-arm/psci.c
> index c55487f..f1c8eb2 100644
> --- a/target-arm/psci.c
> +++ b/target-arm/psci.c
> @@ -22,6 +22,7 @@
>  #include <kvm-consts.h>
>  #include <sysemu/sysemu.h>
>  #include "internals.h"
> +#include "arm-powerctl.h"
>
>  bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
>  {
> @@ -73,21 +74,6 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
>      }
>  }
>
> -static CPUState *get_cpu_by_id(uint64_t id)
> -{
> -    CPUState *cpu;
> -
> -    CPU_FOREACH(cpu) {
> -        ARMCPU *armcpu = ARM_CPU(cpu);
> -
> -        if (armcpu->mp_affinity == id) {
> -            return cpu;
> -        }
> -    }
> -
> -    return NULL;
> -}
> -
>  void arm_handle_psci_call(ARMCPU *cpu)
>  {
>      /*
> @@ -98,7 +84,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
>       * Additional information about the calling convention used is available in
>       * the document 'SMC Calling Convention' (ARM DEN 0028)
>       */
> -    CPUState *cs = CPU(cpu);
>      CPUARMState *env = &cpu->env;
>      uint64_t param[4];
>      uint64_t context_id, mpidr;
> @@ -123,7 +108,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
>      switch (param[0]) {
>          CPUState *target_cpu_state;
>          ARMCPU *target_cpu;
> -        CPUClass *target_cpu_class;
>
>      case QEMU_PSCI_0_2_FN_PSCI_VERSION:
>          ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
> @@ -137,7 +121,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
>
>          switch (param[2]) {
>          case 0:
> -            target_cpu_state = get_cpu_by_id(mpidr);
> +            target_cpu_state = arm_get_cpu_by_id(mpidr);
>              if (!target_cpu_state) {
>                  ret = QEMU_PSCI_RET_INVALID_PARAMS;
>                  break;
> @@ -167,52 +151,14 @@ void arm_handle_psci_call(ARMCPU *cpu)
>          mpidr = param[1];
>          entry = param[2];
>          context_id = param[3];
> -
> -        /* change to the cpu we are powering up */
> -        target_cpu_state = get_cpu_by_id(mpidr);
> -        if (!target_cpu_state) {
> -            ret = QEMU_PSCI_RET_INVALID_PARAMS;
> -            break;
> -        }
> -        target_cpu = ARM_CPU(target_cpu_state);
> -        if (!target_cpu->powered_off) {
> -            ret = QEMU_PSCI_RET_ALREADY_ON;
> -            break;
> -        }
> -        target_cpu_class = CPU_GET_CLASS(target_cpu);
> -
> -        /* Initialize the cpu we are turning on */
> -        cpu_reset(target_cpu_state);
> -        target_cpu->powered_off = false;
> -        target_cpu_state->halted = 0;
> -
>          /*
>           * The PSCI spec mandates that newly brought up CPUs enter the
>           * exception level of the caller in the same execution mode as
>           * the caller, with context_id in x0/r0, respectively.
> -         *
> -         * For now, it is sufficient to assert() that CPUs come out of
> -         * reset in the same mode as the calling CPU, since we only
> -         * implement EL1, which means that
> -         * (a) there is no EL2 for the calling CPU to trap into to change
> -         *     its state
> -         * (b) the newly brought up CPU enters EL1 immediately after coming
> -         *     out of reset in the default state
>           */
> -        assert(is_a64(env) == is_a64(&target_cpu->env));
> -        if (is_a64(env)) {
> -            if (entry & 1) {
> -                ret = QEMU_PSCI_RET_INVALID_PARAMS;
> -                break;
> -            }
> -            target_cpu->env.xregs[0] = context_id;
> -        } else {
> -            target_cpu->env.regs[0] = context_id;
> -            target_cpu->env.thumb = entry & 1;
> -        }
> -        target_cpu_class->set_pc(target_cpu_state, entry);
> -
> -        ret = 0;
> +        ret = arm_set_cpu_on(mpidr, entry, context_id,
> +                             arm_current_el(&ARM_CPU(current_cpu)->env),
> +                             is_a64(&ARM_CPU(current_cpu)->env));

You already have the current CPU's env in the 'env' variable;
you don't need to go via current_cpu for this.

>          break;
>      case QEMU_PSCI_0_1_FN_CPU_OFF:
>      case QEMU_PSCI_0_2_FN_CPU_OFF:
> @@ -250,9 +196,6 @@ err:
>      return;
>
>  cpu_off:
> -    cpu->powered_off = true;
> -    cs->halted = 1;
> -    cs->exception_index = EXCP_HLT;
> -    cpu_loop_exit(cs);
> +    arm_set_cpu_off(cpu->mp_affinity);
>      /* notreached */
>  }
> --
> 2.5.0

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-23 15:24   ` Peter Maydell
@ 2016-03-24  7:10     ` Jean-Christophe DUBOIS
  2016-03-24 10:46       ` Peter Maydell
  0 siblings, 1 reply; 14+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-24  7:10 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 23/03/2016 16:24, Peter Maydell a écrit :
> On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
>> Split ARM on/off function from PSCI support code.
>>
>> This will allow to reuse these functions in other code.
>>
>> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
>> ---
>>
>> Changes since V1:
>>   * Not present on V1
>>
>> Changes since V2:
>>   * Not present on V2
>>
>> Changes since V3:
>>   * Move to a more generic/usefull API
>>   * Manage CPU level/mode change at startup
>>   * Allow PSCI to cope with EL2/HYP level.
>>   * Keep distinct errors for different causes.
>>
>>   target-arm/Makefile.objs  |   1 +
>>   target-arm/arm-powerctl.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++
>>   target-arm/arm-powerctl.h |  44 +++++++++
>>   target-arm/psci.c         |  69 ++------------
>>   4 files changed, 275 insertions(+), 63 deletions(-)
>>   create mode 100644 target-arm/arm-powerctl.c
>>   create mode 100644 target-arm/arm-powerctl.h
>>
>> diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
>> index a80eb39..60fd1dd 100644
>> --- a/target-arm/Makefile.objs
>> +++ b/target-arm/Makefile.objs
>> @@ -9,3 +9,4 @@ obj-y += neon_helper.o iwmmxt_helper.o
>>   obj-y += gdbstub.o
>>   obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
>>   obj-y += crypto_helper.o
>> +obj-y += arm-powerctl.o
>> diff --git a/target-arm/arm-powerctl.c b/target-arm/arm-powerctl.c
>> new file mode 100644
>> index 0000000..8ebb3d8
>> --- /dev/null
>> +++ b/target-arm/arm-powerctl.c
>> @@ -0,0 +1,224 @@
>> +/*
>> + * QEMU support -- ARM Power Control specific functions.
>> + *
>> + * Copyright (c) 2016 Jean-Christophe Dubois
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + *
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include <cpu.h>
>> +#include <cpu-qom.h>
>> +#include "internals.h"
>> +#include "arm-powerctl.h"
>> +
>> +#ifndef DEBUG_ARM_POWERCTL
>> +#define DEBUG_ARM_POWERCTL 0
>> +#endif
>> +
>> +#define DPRINTF(fmt, args...) \
>> +    do { \
>> +        if (DEBUG_ARM_POWERCTL) { \
>> +            fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
>> +        } \
>> +    } while (0)
>> +
>> +CPUState *arm_get_cpu_by_id(uint64_t id)
>> +{
>> +    CPUState *cpu;
>> +
>> +    DPRINTF("cpu %" PRId64 "\n", id);
>> +
>> +    CPU_FOREACH(cpu) {
>> +        ARMCPU *armcpu = ARM_CPU(cpu);
>> +
>> +        if (armcpu->mp_affinity == id) {
>> +            return cpu;
>> +        }
>> +    }
>> +
>> +    qemu_log_mask(LOG_GUEST_ERROR,
>> +                  "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
>> +                  __func__, id);
>> +
>> +    return NULL;
>> +}
>> +
>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
>> +                   int target_el, bool target_aa64)
>> +{
>> +    CPUState *target_cpu_state;
>> +    ARMCPU *target_cpu;
>> +    CPUClass *target_cpu_class;
>> +
>> +    DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
>> +            cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
>> +            context_id);
>> +
>> +    /* requested EL level need to be above 0 */
>> +    assert(target_el >= 1 && target_el <= 3);
>> +
>> +    /* change to the cpu we are powering up */
>> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
>> +    if (!target_cpu_state) {
>> +        /* The cpu was not found */
>> +        return QEMU_ARM_POWERCTL_INVALID_PARAM;
>> +    }
>> +    target_cpu = ARM_CPU(target_cpu_state);
>> +    if (!target_cpu->powered_off) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "[ARM]%s: CPU %" PRId64 " is already running\n",
>> +                      __func__, cpuid);
>> +        return QEMU_ARM_POWERCTL_ALREADY_ON;
>> +    }
>> +    target_cpu_class = CPU_GET_CLASS(target_cpu);
>> +
>> +    /* Initialize the cpu we are turning on */
>> +    cpu_reset(target_cpu_state);
>> +    target_cpu->powered_off = false;
>> +    target_cpu_state->halted = 0;
>> +
>> +    /*
>> +     * The newly brought CPU is requested to enter the exception level
>> +     * "target_el" and be in the requested mode (aarch64 ou aarch32).
>> +     */
>> +
>> +    /*
>> +     * Check that the CPU is supporting the requested level
>> +     */
>> +    switch (target_el) {
>> +    case 3:
>> +        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
>> +            if (is_a64(&target_cpu->env)) {
> Why are we looking at the is_a64() state of the CPU as it comes
> out of reset? What we care about is target_aa64 here (if we
> are putting the CPU into a 64-bit state at a lower exception
> level then we must ensure the state is consistent with all
> the higher levels being 64-bit).
Because we are trying to switch the CPU to the desired level and we 
don't do it the same for aarch64 and aarch32. This is not about current 
mode but about the real architecture. Other functions like 
arm_is_secure() or arm_current_el() are doing the same. Even cpu_reset() 
is doing the same ...
>
>> +                /* Lower EL3 exception is aarch64 */
>> +                target_cpu->env.cp15.scr_el3 |= SCR_RW;
>> +                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
>> +                    /* Lower EL2 exception is aarch64 */
>> +                    target_cpu->env.cp15.hcr_el2 |= HCR_RW;
> If you're starting in EL3 you don't need to mess with any of
> these registers, because they control register width for
> EL2 and EL1. You can let the guest do that itself.
OK
>
>> +                }
>> +                target_cpu->env.pstate = PSTATE_MODE_EL3h;
>> +            } else {
>> +                switch_mode(&target_cpu->env, ARM_CPU_MODE_MON);
> switch_mode() is an internal function (and does some register
> bank copying we don't need); use
>   cpsr_write(&target_cpu->env, ARM_CPU_MODE_MON, CPSR_M, CPSRWriteRaw);
OK
>
>> +            }
>> +            /* Processor is in secure mode */
>> +            target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
>> +        } else {
>> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
>> +        }
>> +        break;
>> +    case 2:
>> +        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
>> +            if (is_a64(&target_cpu->env)) {
>> +                if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
>> +                    /* Lower EL3 exception is aarch64 */
>> +                    target_cpu->env.cp15.scr_el3 |= SCR_RW;
>> +                }
>> +                /* Lower EL2 exception is aarch64 */
>> +                target_cpu->env.cp15.hcr_el2 |= HCR_RW;
> Again, to go into EL2 you don't need to worry about setting HCR_EL2.RW.

OK
>
>> +                target_cpu->env.pstate = PSTATE_MODE_EL2h;
>> +            } else {
>> +                switch_mode(&target_cpu->env, ARM_CPU_MODE_HYP);
>> +            }
>> +            /* Processor is not in secure mode */
>> +            target_cpu->env.cp15.scr_el3 |= SCR_NS;
>> +        } else {
>> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
>> +        }
>> +        break;
>> +    case 1:
>> +    default:
>> +        if (is_a64(&target_cpu->env)) {
>> +            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
>> +                /* Lower EL3 exception is aarch64 */
>> +                target_cpu->env.cp15.scr_el3 |= SCR_RW;
>> +            }
>> +            if (arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
>> +                /* Lower EL2 exception is aarch64 */
>> +                target_cpu->env.cp15.hcr_el2 |= HCR_RW;
>> +            }
>> +            target_cpu->env.pstate = PSTATE_MODE_EL1h;
>> +        } else {
>> +            switch_mode(&target_cpu->env, ARM_CPU_MODE_SVC);
>> +        }
>> +        /* Processor is not in secure mode */
>> +        target_cpu->env.cp15.scr_el3 |= SCR_NS;
>> +        break;
>> +    }
>> +
>> +    /* We assert if the request cannot be fulfilled */
>> +    assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));
> So, when can this happen, or is it genuinely "can't happen" ?

Well, for now I don't force the mode of the desired level. So if we are 
asked to go EL1 in arch32 but by default the CORE foes EL1 in arch64 
this will fail.

>
>> +
>> +    /* We check if the started CPU is now in the correct level */
>> +    assert(target_el == arm_current_el(&target_cpu->env));
>> +
>> +    if (target_aa64) {
>> +        /* Thumb32 is not supported on AARCH64 */
>> +        if (entry & 1) {
>> +            return QEMU_ARM_POWERCTL_INVALID_PARAM;
>> +        }
> If you're going to check the PC alignment for 64-bit then you might
> as well check that it's 4-aligned.
OK, I'll change that.
>
>> +        target_cpu->env.xregs[0] = context_id;
>> +    } else {
>> +        target_cpu->env.regs[0] = context_id;
>> +        target_cpu->env.thumb = entry & 1;
>> +    }
>> +
>> +    /* Start the new CPU at the requested address */
>> +    target_cpu_class->set_pc(target_cpu_state, entry);
>> +
>> +    /* We are good to go */
>> +    return QEMU_ARM_POWERCTL_RET_SUCCESS;
>> +}
>> +
>> +void arm_set_cpu_off(uint64_t cpuid)
>> +{
>> +    CPUState *target_cpu_state;
>> +    ARMCPU *target_cpu;
>> +
>> +    DPRINTF("cpu %" PRId64 "\n", cpuid);
>> +
>> +    /* change to the cpu we are powering up */
>> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
>> +    if (!target_cpu_state) {
>> +        return;
>> +    }
>> +    target_cpu = ARM_CPU(target_cpu_state);
>> +    if (target_cpu->powered_off) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "[ARM]%s: CPU %" PRId64 " is already off\n",
>> +                      __func__, cpuid);
>> +        return;
>> +    }
>> +
>> +    target_cpu->powered_off = true;
>> +    target_cpu_state->halted = 1;
>> +    target_cpu_state->exception_index = EXCP_HLT;
>> +    cpu_loop_exit(target_cpu_state);
>> +}
>> +
>> +int arm_reset_cpu(uint64_t cpuid)
>> +{
>> +    CPUState *target_cpu_state;
>> +    ARMCPU *target_cpu;
>> +
>> +    DPRINTF("cpu %" PRId64 "\n", cpuid);
>> +
>> +    /* change to the cpu we are resetting */
>> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
>> +    if (!target_cpu_state) {
>> +        return QEMU_ARM_POWERCTL_INVALID_PARAM;
>> +    }
>> +    target_cpu = ARM_CPU(target_cpu_state);
>> +    if (target_cpu->powered_off) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "[ARM]%s: CPU %" PRId64 " is off\n",
>> +                      __func__, cpuid);
>> +        return -1;
>> +    }
>> +
>> +    /* Reset the cpu */
>> +    cpu_reset(target_cpu_state);
>> +
>> +    return QEMU_ARM_POWERCTL_RET_SUCCESS;
>> +}
>> diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
>> new file mode 100644
>> index 0000000..2195483
>> --- /dev/null
>> +++ b/target-arm/arm-powerctl.h
>> @@ -0,0 +1,44 @@
>> +/*
>> + * QEMU support -- ARM Power Control specific functions.
>> + *
>> + * Copyright (c) 2016 Jean-Christophe Dubois
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + *
>> + */
>> +
>> +#ifndef QEMU_ARM_POWERCTL_H
>> +#define QEMU_ARM_POWERCTL_H
>> +
>> +#include "kvm-consts.h"
>> +
>> +#define QEMU_ARM_POWERCTL_RET_SUCCESS QEMU_PSCI_RET_SUCCESS
>> +#define QEMU_ARM_POWERCTL_INVALID_PARAM QEMU_PSCI_RET_INVALID_PARAMS
>> +#define QEMU_ARM_POWERCTL_ALREADY_ON QEMU_PSCI_RET_ALREADY_ON
>> +
>> +/*
>> + * Retreive a CPUState object from its CPU ID
>> + */
>> +CPUState *arm_get_cpu_by_id(uint64_t id);
> New global functions should all have properly formatted doc comments.

I'll wok on it. Do you have a good citizen to point me to as a reference?

>
>> +
>> +/*
>> + * Start a give CPU. The CPU will start running at address "entry" with
>> + * "context_id" in r0/x0. The CPU should start at exception level "target_el"
>> + * and in mode aa64 or aa32 depending on "target_aa64"
>> + */
>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
>> +                   int target_el, bool target_aa64);
> What's the return value? Do we start the CPU secure or nonsecure?
> In AArch32, in which mode do we start? We need to either document
> these things or let the caller specify.
The idea is to start the CPU in the requested level/mode (or fail if it 
is not possible).

I'll document return value/error.
>
> (For PSCI we want to always start in non-secure, and we want to start
> in the same execution state as the calling cpu, which I take to include
> the mode. We won't ever try to start a cpu in EL3.)

The PSCI call will take care of the requested level/mode.

This is a more generic function that can be use for other purpose than PSCI.
>
>> +
>> +/*
>> + * Stop/Power off a given CPU
>> + */
>> +
>> +void arm_set_cpu_off(uint64_t cpuid);
>> +
>> +/*
>> + * Reset a given CPU
>> + */
>> +int arm_reset_cpu(uint64_t cpuid);
>> +
>> +#endif
>> diff --git a/target-arm/psci.c b/target-arm/psci.c
>> index c55487f..f1c8eb2 100644
>> --- a/target-arm/psci.c
>> +++ b/target-arm/psci.c
>> @@ -22,6 +22,7 @@
>>   #include <kvm-consts.h>
>>   #include <sysemu/sysemu.h>
>>   #include "internals.h"
>> +#include "arm-powerctl.h"
>>
>>   bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
>>   {
>> @@ -73,21 +74,6 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
>>       }
>>   }
>>
>> -static CPUState *get_cpu_by_id(uint64_t id)
>> -{
>> -    CPUState *cpu;
>> -
>> -    CPU_FOREACH(cpu) {
>> -        ARMCPU *armcpu = ARM_CPU(cpu);
>> -
>> -        if (armcpu->mp_affinity == id) {
>> -            return cpu;
>> -        }
>> -    }
>> -
>> -    return NULL;
>> -}
>> -
>>   void arm_handle_psci_call(ARMCPU *cpu)
>>   {
>>       /*
>> @@ -98,7 +84,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
>>        * Additional information about the calling convention used is available in
>>        * the document 'SMC Calling Convention' (ARM DEN 0028)
>>        */
>> -    CPUState *cs = CPU(cpu);
>>       CPUARMState *env = &cpu->env;
>>       uint64_t param[4];
>>       uint64_t context_id, mpidr;
>> @@ -123,7 +108,6 @@ void arm_handle_psci_call(ARMCPU *cpu)
>>       switch (param[0]) {
>>           CPUState *target_cpu_state;
>>           ARMCPU *target_cpu;
>> -        CPUClass *target_cpu_class;
>>
>>       case QEMU_PSCI_0_2_FN_PSCI_VERSION:
>>           ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
>> @@ -137,7 +121,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
>>
>>           switch (param[2]) {
>>           case 0:
>> -            target_cpu_state = get_cpu_by_id(mpidr);
>> +            target_cpu_state = arm_get_cpu_by_id(mpidr);
>>               if (!target_cpu_state) {
>>                   ret = QEMU_PSCI_RET_INVALID_PARAMS;
>>                   break;
>> @@ -167,52 +151,14 @@ void arm_handle_psci_call(ARMCPU *cpu)
>>           mpidr = param[1];
>>           entry = param[2];
>>           context_id = param[3];
>> -
>> -        /* change to the cpu we are powering up */
>> -        target_cpu_state = get_cpu_by_id(mpidr);
>> -        if (!target_cpu_state) {
>> -            ret = QEMU_PSCI_RET_INVALID_PARAMS;
>> -            break;
>> -        }
>> -        target_cpu = ARM_CPU(target_cpu_state);
>> -        if (!target_cpu->powered_off) {
>> -            ret = QEMU_PSCI_RET_ALREADY_ON;
>> -            break;
>> -        }
>> -        target_cpu_class = CPU_GET_CLASS(target_cpu);
>> -
>> -        /* Initialize the cpu we are turning on */
>> -        cpu_reset(target_cpu_state);
>> -        target_cpu->powered_off = false;
>> -        target_cpu_state->halted = 0;
>> -
>>           /*
>>            * The PSCI spec mandates that newly brought up CPUs enter the
>>            * exception level of the caller in the same execution mode as
>>            * the caller, with context_id in x0/r0, respectively.
>> -         *
>> -         * For now, it is sufficient to assert() that CPUs come out of
>> -         * reset in the same mode as the calling CPU, since we only
>> -         * implement EL1, which means that
>> -         * (a) there is no EL2 for the calling CPU to trap into to change
>> -         *     its state
>> -         * (b) the newly brought up CPU enters EL1 immediately after coming
>> -         *     out of reset in the default state
>>            */
>> -        assert(is_a64(env) == is_a64(&target_cpu->env));
>> -        if (is_a64(env)) {
>> -            if (entry & 1) {
>> -                ret = QEMU_PSCI_RET_INVALID_PARAMS;
>> -                break;
>> -            }
>> -            target_cpu->env.xregs[0] = context_id;
>> -        } else {
>> -            target_cpu->env.regs[0] = context_id;
>> -            target_cpu->env.thumb = entry & 1;
>> -        }
>> -        target_cpu_class->set_pc(target_cpu_state, entry);
>> -
>> -        ret = 0;
>> +        ret = arm_set_cpu_on(mpidr, entry, context_id,
>> +                             arm_current_el(&ARM_CPU(current_cpu)->env),
>> +                             is_a64(&ARM_CPU(current_cpu)->env));
> You already have the current CPU's env in the 'env' variable;
> you don't need to go via current_cpu for this.
OK
>
>>           break;
>>       case QEMU_PSCI_0_1_FN_CPU_OFF:
>>       case QEMU_PSCI_0_2_FN_CPU_OFF:
>> @@ -250,9 +196,6 @@ err:
>>       return;
>>
>>   cpu_off:
>> -    cpu->powered_off = true;
>> -    cs->halted = 1;
>> -    cs->exception_index = EXCP_HLT;
>> -    cpu_loop_exit(cs);
>> +    arm_set_cpu_off(cpu->mp_affinity);
>>       /* notreached */
>>   }
>> --
>> 2.5.0
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-23 14:48   ` Peter Maydell
@ 2016-03-24  7:11     ` Jean-Christophe DUBOIS
  0 siblings, 0 replies; 14+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-24  7:11 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 23/03/2016 15:48, Peter Maydell a écrit :
> On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
>> The sabrelite supports one SPI FLASH memory on SPI1
>>
>> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
>> +    {
>> +        /* Add the sst25vf016b NOR FLASH memory to first SPI */
>> +        Object *spi_dev;
>> +
>> +        spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1");
> Can you add a comment to remind us there's a todo here:
>   /* Ideally we would expose the chip select and spi bus on the SoC
>    * object using alias properties; then we would not need to directly
>    * access the underlying spi device object.
>    */
OK
>
> Otherwise
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-24  7:10     ` Jean-Christophe DUBOIS
@ 2016-03-24 10:46       ` Peter Maydell
  2016-03-24 13:54         ` jcd
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2016-03-24 10:46 UTC (permalink / raw)
  To: Jean-Christophe DUBOIS; +Cc: QEMU Developers, Peter Crosthwaite

On 24 March 2016 at 07:10, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Le 23/03/2016 16:24, Peter Maydell a écrit :
>>
>> On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net>
>> wrote:
>>> +
>>> +    /*
>>> +     * The newly brought CPU is requested to enter the exception level
>>> +     * "target_el" and be in the requested mode (aarch64 ou aarch32).
>>> +     */
>>> +
>>> +    /*
>>> +     * Check that the CPU is supporting the requested level
>>> +     */
>>> +    switch (target_el) {
>>> +    case 3:
>>> +        if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
>>> +            if (is_a64(&target_cpu->env)) {
>>
>> Why are we looking at the is_a64() state of the CPU as it comes
>> out of reset? What we care about is target_aa64 here (if we
>> are putting the CPU into a 64-bit state at a lower exception
>> level then we must ensure the state is consistent with all
>> the higher levels being 64-bit).
>
> Because we are trying to switch the CPU to the desired level and we don't do
> it the same for aarch64 and aarch32. This is not about current mode but
> about the real architecture. Other functions like arm_is_secure() or
> arm_current_el() are doing the same. Even cpu_reset() is doing the same ...

No, this doesn't seem right to me. You're trying to set
the SCR_EL3.RW and HCR_EL2.RW bits, and the condition
for setting them is "are we trying to transition *to*
an EL which is 64 bits". Whether the current EL is 64 bits
or not is not relevant (and we know that it must be 64 bits
because 64-bit capable CPUs always reset into 64-bits,
assuming the caller didn't buggily ask us for a 64-bit
state on a 32-bit CPU).

arm_is_secure() and arm_current_el() are functions which
are asking questions about the current state of a CPU,
so obviously they look at the current state of the CPU.


>>> +    /* We assert if the request cannot be fulfilled */
>>> +    assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));
>>
>> So, when can this happen, or is it genuinely "can't happen" ?
>
>
> Well, for now I don't force the mode of the desired level. So if we are
> asked to go EL1 in arch32 but by default the CORE foes EL1 in arch64 this
> will fail.

Oh, I see. That needs at least a TODO comment, because it's
a missing feature that we'll need to implement at some point.
(The current PSCI code doesn't do it, but the conditions it
documents for why it can't do it aren't true any more -- we
do now implement more than just EL1.)

It might be better to check this early and return failure to the
caller, since the guest can provoke it.

>> New global functions should all have properly formatted doc comments.
>
>
> I'll wok on it. Do you have a good citizen to point me to as a reference?

I usually use the extract/deposit functions in include/qemu/bitops.h
as a reference.

>>
>>> +
>>> +/*
>>> + * Start a give CPU. The CPU will start running at address "entry" with
>>> + * "context_id" in r0/x0. The CPU should start at exception level
>>> "target_el"
>>> + * and in mode aa64 or aa32 depending on "target_aa64"
>>> + */
>>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
>>> +                   int target_el, bool target_aa64);
>>
>> What's the return value? Do we start the CPU secure or nonsecure?
>> In AArch32, in which mode do we start? We need to either document
>> these things or let the caller specify.
>
> The idea is to start the CPU in the requested level/mode (or fail if it is
> not possible).
>
> I'll document return value/error.
>>
>>
>> (For PSCI we want to always start in non-secure, and we want to start
>> in the same execution state as the calling cpu, which I take to include
>> the mode. We won't ever try to start a cpu in EL3.)
>
>
> The PSCI call will take care of the requested level/mode.
>
> This is a more generic function that can be use for other purpose than PSCI.

Yes; I was just giving you the PSCI information to save you having to
go look up the spec. The generic function needs to be able to at least
provide sufficient functionality to implement the PSCI requirements.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-24 10:46       ` Peter Maydell
@ 2016-03-24 13:54         ` jcd
  2016-03-24 14:03           ` Peter Maydell
  0 siblings, 1 reply; 14+ messages in thread
From: jcd @ 2016-03-24 13:54 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel, Peter Crosthwaite



----- Le 24 Mar 16, à 11:46, Peter Maydell peter.maydell@linaro.org a écrit :

> On 24 March 2016 at 07:10, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> > Le 23/03/2016 16:24, Peter Maydell a écrit :

> >> On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net>
> >> wrote:
> >>> +
> >>> + /*
> >>> + * The newly brought CPU is requested to enter the exception level
> >>> + * "target_el" and be in the requested mode (aarch64 ou aarch32).
> >>> + */
> >>> +
> >>> + /*
> >>> + * Check that the CPU is supporting the requested level
> >>> + */
> >>> + switch (target_el) {
> >>> + case 3:
> >>> + if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
> >>> + if (is_a64(&target_cpu->env)) {

> >> Why are we looking at the is_a64() state of the CPU as it comes
> >> out of reset? What we care about is target_aa64 here (if we
> >> are putting the CPU into a 64-bit state at a lower exception
> >> level then we must ensure the state is consistent with all
> >> the higher levels being 64-bit).

> > Because we are trying to switch the CPU to the desired level and we don't do
> > it the same for aarch64 and aarch32. This is not about current mode but
> > about the real architecture. Other functions like arm_is_secure() or
> > arm_current_el() are doing the same. Even cpu_reset() is doing the same ...

> No, this doesn't seem right to me. You're trying to set
> the SCR_EL3.RW and HCR_EL2.RW bits, and the condition
> for setting them is "are we trying to transition *to*
> an EL which is 64 bits". Whether the current EL is 64 bits
> or not is not relevant (and we know that it must be 64 bits
> because 64-bit capable CPUs always reset into 64-bits,
> assuming the caller didn't buggily ask us for a 64-bit
> state on a 32-bit CPU).

You mean that env->aarch64 will change dynamically depending on the CPU mode?

> arm_is_secure() and arm_current_el() are functions which
> are asking questions about the current state of a CPU,
> so obviously they look at the current state of the CPU.

> >>> + /* We assert if the request cannot be fulfilled */
> >>> + assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));

> >> So, when can this happen, or is it genuinely "can't happen" ?


> > Well, for now I don't force the mode of the desired level. So if we are
> > asked to go EL1 in arch32 but by default the CORE foes EL1 in arch64 this
> > will fail.

> Oh, I see. That needs at least a TODO comment, because it's
> a missing feature that we'll need to implement at some point.
> (The current PSCI code doesn't do it, but the conditions it
> documents for why it can't do it aren't true any more -- we
> do now implement more than just EL1.)

I was wondeing if I could influence on the capability to get the requested mode
by tweaking target_cpu->env.cp15.scr_el3 and target_cpu->env.cp15.hcr_el2

> It might be better to check this early and return failure to the
> caller, since the guest can provoke it.

> >> New global functions should all have properly formatted doc comments.


> > I'll wok on it. Do you have a good citizen to point me to as a reference?

> I usually use the extract/deposit functions in include/qemu/bitops.h
> as a reference.


> >>> +
> >>> +/*
> >>> + * Start a give CPU. The CPU will start running at address "entry" with
> >>> + * "context_id" in r0/x0. The CPU should start at exception level
> >>> "target_el"
> >>> + * and in mode aa64 or aa32 depending on "target_aa64"
> >>> + */
> >>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
> >>> + int target_el, bool target_aa64);

> >> What's the return value? Do we start the CPU secure or nonsecure?
> >> In AArch32, in which mode do we start? We need to either document
> >> these things or let the caller specify.

> > The idea is to start the CPU in the requested level/mode (or fail if it is
> > not possible).

> > I'll document return value/error.


> >> (For PSCI we want to always start in non-secure, and we want to start
> >> in the same execution state as the calling cpu, which I take to include
> >> the mode. We won't ever try to start a cpu in EL3.)


> > The PSCI call will take care of the requested level/mode.

> > This is a more generic function that can be use for other purpose than PSCI.

> Yes; I was just giving you the PSCI information to save you having to
> go look up the spec. The generic function needs to be able to at least
> provide sufficient functionality to implement the PSCI requirements.

> thanks
> -- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions
  2016-03-24 13:54         ` jcd
@ 2016-03-24 14:03           ` Peter Maydell
  0 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2016-03-24 14:03 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: qemu-devel, Peter Crosthwaite

On 24 March 2016 at 13:54,  <jcd@tribudubois.net> wrote:
>
>
> ----- Le 24 Mar 16, à 11:46, Peter Maydell peter.maydell@linaro.org a écrit :
>
>> On 24 March 2016 at 07:10, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
>> > Le 23/03/2016 16:24, Peter Maydell a écrit :
>
>> >> On 19 March 2016 at 20:59, Jean-Christophe Dubois <jcd@tribudubois.net>
>> >> wrote:
>> >>> +
>> >>> + /*
>> >>> + * The newly brought CPU is requested to enter the exception level
>> >>> + * "target_el" and be in the requested mode (aarch64 ou aarch32).
>> >>> + */
>> >>> +
>> >>> + /*
>> >>> + * Check that the CPU is supporting the requested level
>> >>> + */
>> >>> + switch (target_el) {
>> >>> + case 3:
>> >>> + if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
>> >>> + if (is_a64(&target_cpu->env)) {
>
>> >> Why are we looking at the is_a64() state of the CPU as it comes
>> >> out of reset? What we care about is target_aa64 here (if we
>> >> are putting the CPU into a 64-bit state at a lower exception
>> >> level then we must ensure the state is consistent with all
>> >> the higher levels being 64-bit).
>
>> > Because we are trying to switch the CPU to the desired level and we don't do
>> > it the same for aarch64 and aarch32. This is not about current mode but
>> > about the real architecture. Other functions like arm_is_secure() or
>> > arm_current_el() are doing the same. Even cpu_reset() is doing the same ...
>
>> No, this doesn't seem right to me. You're trying to set
>> the SCR_EL3.RW and HCR_EL2.RW bits, and the condition
>> for setting them is "are we trying to transition *to*
>> an EL which is 64 bits". Whether the current EL is 64 bits
>> or not is not relevant (and we know that it must be 64 bits
>> because 64-bit capable CPUs always reset into 64-bits,
>> assuming the caller didn't buggily ask us for a 64-bit
>> state on a 32-bit CPU).
>
> You mean that env->aarch64 will change dynamically depending on
> the CPU mode?

Yes, env->aarch64 tells you whether the CPU is currently in
AArch32 or AArch64. (You should use is_a64() rather than directly
looking at it, though.) If you want to know if the CPU
supports AArch64 in general, you can call
arm_feature(env, ARM_FEATURE_AARCH64).

>> arm_is_secure() and arm_current_el() are functions which
>> are asking questions about the current state of a CPU,
>> so obviously they look at the current state of the CPU.
>
>> >>> + /* We assert if the request cannot be fulfilled */
>> >>> + assert(target_aa64 == arm_el_is_aa64(&target_cpu->env, target_el));
>
>> >> So, when can this happen, or is it genuinely "can't happen" ?
>
>
>> > Well, for now I don't force the mode of the desired level. So if we are
>> > asked to go EL1 in arch32 but by default the CORE foes EL1 in arch64 this
>> > will fail.
>
>> Oh, I see. That needs at least a TODO comment, because it's
>> a missing feature that we'll need to implement at some point.
>> (The current PSCI code doesn't do it, but the conditions it
>> documents for why it can't do it aren't true any more -- we
>> do now implement more than just EL1.)
>
> I was wondeing if I could influence on the capability to get the requested mode
> by tweaking target_cpu->env.cp15.scr_el3 and target_cpu->env.cp15.hcr_el2

The RW bits in SCR_EL3 and HCR_EL2 are there only to define
what the register width of lower exception levels are. If a
guest tries an exception return asking for a different register
width to what the system register say then this is an illegal
return event. So since we're manually adjusting the state of the
CPU to make it look as though we're in some lower exception level,
we need to adjust the RW bits to be consistent with the fact that
we're now in (say) AArch64 EL1; otherwise when the guest later does
an exception return it will break.

If you want to actually change the current mode then it's probably
sufficient to set the SCR_EL3.RW and HCR_EL2.RW bits correctly
and then set env->aarch64 to 0 for aarch32. You then need to make
sure you write a full correct cpsr. This is getting complicated
enough I'd rather leave it to a separate patch, though.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-03-24 14:03 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-19 20:59 [Qemu-devel] [PATCH v4 0/6] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 1/6] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
2016-03-23 15:24   ` Peter Maydell
2016-03-24  7:10     ` Jean-Christophe DUBOIS
2016-03-24 10:46       ` Peter Maydell
2016-03-24 13:54         ` jcd
2016-03-24 14:03           ` Peter Maydell
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 2/6] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 3/6] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 4/6] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 5/6] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
2016-03-19 20:59 ` [Qemu-devel] [PATCH v4 6/6] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
2016-03-23 14:48   ` Peter Maydell
2016-03-24  7:11     ` Jean-Christophe DUBOIS

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.