qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support
@ 2016-03-01 22:27 Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 01/12] i.MX: Allow GPT timer to rollover Jean-Christophe Dubois
                   ` (12 more replies)
  0 siblings, 13 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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 (12):
  i.MX: Allow GPT timer to rollover.
  i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency.
  i.MX: Remove CCM useless clock computation handling.
  i.MX: Add the CLK_IPG_HIGH clock
  i.MX: Add i.MX6 CCM and ANALOG device.
  ARM: Factor out ARM on/off PSCI control functions
  i.MX: Add i.MX6 System Reset Controller device.
  i.MX: Add missing descriptions in devices.
  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-imx25.c              |   1 +
 hw/arm/fsl-imx31.c              |   1 +
 hw/arm/fsl-imx6.c               | 449 +++++++++++++++++++++++
 hw/arm/sabrelite.c              | 117 ++++++
 hw/i2c/imx_i2c.c                |   1 +
 hw/misc/Makefile.objs           |   2 +
 hw/misc/imx25_ccm.c             |  29 +-
 hw/misc/imx31_ccm.c             |  35 +-
 hw/misc/imx6_ccm.c              | 774 ++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx6_src.c              | 260 ++++++++++++++
 hw/net/imx_fec.c                |   1 +
 hw/ssi/Makefile.objs            |   1 +
 hw/ssi/imx_spi.c                | 459 ++++++++++++++++++++++++
 hw/timer/imx_epit.c             |   8 +-
 hw/timer/imx_gpt.c              |  43 +--
 include/hw/arm/fsl-imx6.h       | 450 +++++++++++++++++++++++
 include/hw/misc/imx6_ccm.h      | 197 ++++++++++
 include/hw/misc/imx6_src.h      |  73 ++++
 include/hw/misc/imx_ccm.h       |  10 +-
 include/hw/ssi/imx_spi.h        | 103 ++++++
 include/qemu/fifo32.h           | 186 ++++++++++
 target-arm/Makefile.objs        |   1 +
 target-arm/arm-powerctl.c       | 156 ++++++++
 target-arm/arm-powerctl.h       |  22 ++
 target-arm/psci.c               |  72 +---
 27 files changed, 3294 insertions(+), 159 deletions(-)
 create mode 100644 hw/arm/fsl-imx6.c
 create mode 100644 hw/arm/sabrelite.c
 create mode 100644 hw/misc/imx6_ccm.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_ccm.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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 01/12] i.MX: Allow GPT timer to rollover.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 02/12] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency Jean-Christophe Dubois
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

GPT timer need to rollover when it reaches 0xffffffff.

It also need to reset to 0 when in "restart mode" and crossing the
compare 1 register.

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

Changes since V1:
 * None 

Changes since V2:
 * None 

 hw/timer/imx_gpt.c | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 5cdc3b3..916577b 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -134,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
 static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
 {
     uint32_t timeout = GPT_TIMER_MAX;
-    uint32_t count = 0;
+    uint32_t count;
     long long limit;
 
     if (!(s->cr & GPT_CR_EN)) {
@@ -142,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
         return;
     }
 
-    if (event) {
-        /* This is a timer event  */
+    /* update the count */
+    count = imx_gpt_update_count(s);
 
-        if ((s->cr & GPT_CR_FRR)  && (s->next_timeout != GPT_TIMER_MAX)) {
-            /*
-             * if we are in free running mode and we have not reached
-             * the GPT_TIMER_MAX limit, then update the count
+    if (event) {
+        /*
+         * This is an event (the ptimer reached 0 and stopped), and the
+         * timer counter is now equal to s->next_timeout.
+         */
+        if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) {
+            /* We are in restart mode and we crossed the compare channel 1
+             * value. We need to reset the counter to 0.
              */
-            count = imx_gpt_update_count(s);
+            count = s->cnt = s->next_timeout = 0;
+        } else if (count == GPT_TIMER_MAX) {
+            /* We reached GPT_TIMER_MAX so we need to rollover */
+            count = s->cnt = s->next_timeout = 0;
         }
-    } else {
-        /* not a timer event, then just update the count */
-
-        count = imx_gpt_update_count(s);
     }
 
     /* now, find the next timeout related to count */
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 02/12] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 01/12] i.MX: Allow GPT timer to rollover Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 03/12] i.MX: Remove CCM useless clock computation handling Jean-Christophe Dubois
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

This way all CCM clock defines/enums are named CLK_XXX

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

Changes since V1:
 * Not present on V1

Changes since V2:
 * None

 hw/misc/imx25_ccm.c       |  2 +-
 hw/misc/imx31_ccm.c       |  2 +-
 hw/timer/imx_epit.c       |  2 +-
 hw/timer/imx_gpt.c        | 10 +++++-----
 include/hw/misc/imx_ccm.h |  2 +-
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index 498e84c..7a29c19 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -182,7 +182,7 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     DPRINTF("Clock = %d)\n", clock);
 
     switch (clock) {
-    case NOCLK:
+    case CLK_NONE:
         break;
     case CLK_MPLL:
         freq = imx25_ccm_get_mpll_clk(dev);
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index 17640bf..7a8fcd2 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -209,7 +209,7 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     uint32_t freq = 0;
 
     switch (clock) {
-    case NOCLK:
+    case CLK_NONE:
         break;
     case CLK_MCU:
         freq = imx31_ccm_get_mcu_clk(dev);
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 38fbf27..9f26ba8 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -52,7 +52,7 @@ static char const *imx_epit_reg_name(uint32_t reg)
  * These are typical.
  */
 static const IMXClk imx_epit_clocks[] =  {
-    NOCLK,    /* 00 disabled */
+    CLK_NONE, /* 00 disabled */
     CLK_IPG,  /* 01 ipg_clk, ~532MHz */
     CLK_IPG,  /* 10 ipg_clk_highfreq */
     CLK_32k,  /* 11 ipg_clk_32k -- ~32kHz */
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 916577b..40db63c 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
 };
 
 static const IMXClk imx_gpt_clocks[] = {
-    NOCLK,    /* 000 No clock source */
+    CLK_NONE, /* 000 No clock source */
     CLK_IPG,  /* 001 ipg_clk, 532MHz*/
     CLK_IPG,  /* 010 ipg_clk_highfreq */
-    NOCLK,    /* 011 not defined */
+    CLK_NONE, /* 011 not defined */
     CLK_32k,  /* 100 ipg_clk_32k */
-    NOCLK,    /* 101 not defined */
-    NOCLK,    /* 110 not defined */
-    NOCLK,    /* 111 not defined */
+    CLK_NONE, /* 101 not defined */
+    CLK_NONE, /* 110 not defined */
+    CLK_NONE, /* 111 not defined */
 };
 
 static void imx_gpt_set_freq(IMXGPTState *s)
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 5c4b795..74e2705 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -43,7 +43,7 @@ typedef struct IMXCCMState {
 } IMXCCMState;
 
 typedef enum  {
-    NOCLK,
+    CLK_NONE,
     CLK_MPLL,
     CLK_UPLL,
     CLK_MCU,
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 03/12] i.MX: Remove CCM useless clock computation handling.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 01/12] i.MX: Allow GPT timer to rollover Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 02/12] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 04/12] i.MX: Add the CLK_IPG_HIGH clock Jean-Christophe Dubois
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

Most clocks supported by the CCM are useless to the qemu framework.

Only clocks related to timers (EPIT, GPT, PWM, WATCHDOG, ...) are usefull
to QEMU code.

Therefore this patch removes clock computation handling for all clocks but:
* CLK_NONE,
* CLK_IPG,
* CLK_32k

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

Changes since V1:
 * move NOCLK renaming in its own patch
 * move CLK_IPG_HIGH addition in its own patch

Changes since V2:
 * None

 hw/misc/imx25_ccm.c       | 26 --------------------------
 hw/misc/imx31_ccm.c       | 32 --------------------------------
 include/hw/misc/imx_ccm.h |  7 -------
 3 files changed, 65 deletions(-)

diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index 7a29c19..e138fc6 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -120,20 +120,6 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
     return freq;
 }
 
-static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
-{
-    uint32_t freq = 0;
-    IMX25CCMState *s = IMX25_CCM(dev);
-
-    if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
-        freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
-    }
-
-    DPRINTF("freq = %d\n", freq);
-
-    return freq;
-}
-
 static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
 {
     uint32_t freq;
@@ -184,18 +170,6 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     switch (clock) {
     case CLK_NONE:
         break;
-    case CLK_MPLL:
-        freq = imx25_ccm_get_mpll_clk(dev);
-        break;
-    case CLK_UPLL:
-        freq = imx25_ccm_get_upll_clk(dev);
-        break;
-    case CLK_MCU:
-        freq = imx25_ccm_get_mcu_clk(dev);
-        break;
-    case CLK_AHB:
-        freq = imx25_ccm_get_ahb_clk(dev);
-        break;
     case CLK_IPG:
         freq = imx25_ccm_get_ipg_clk(dev);
         break;
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index 7a8fcd2..a5caabb 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -152,32 +152,6 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
     return freq;
 }
 
-static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
-{
-    uint32_t freq;
-    IMX31CCMState *s = IMX31_CCM(dev);
-
-    freq = imx31_ccm_get_mcu_main_clk(dev)
-           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
-
-    DPRINTF("freq = %d\n", freq);
-
-    return freq;
-}
-
-static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
-{
-    uint32_t freq;
-    IMX31CCMState *s = IMX31_CCM(dev);
-
-    freq = imx31_ccm_get_mcu_main_clk(dev)
-           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
-
-    DPRINTF("freq = %d\n", freq);
-
-    return freq;
-}
-
 static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
 {
     uint32_t freq;
@@ -211,12 +185,6 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     switch (clock) {
     case CLK_NONE:
         break;
-    case CLK_MCU:
-        freq = imx31_ccm_get_mcu_clk(dev);
-        break;
-    case CLK_HSP:
-        freq = imx31_ccm_get_hsp_clk(dev);
-        break;
     case CLK_IPG:
         freq = imx31_ccm_get_ipg_clk(dev);
         break;
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 74e2705..378b78d 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -44,14 +44,7 @@ typedef struct IMXCCMState {
 
 typedef enum  {
     CLK_NONE,
-    CLK_MPLL,
-    CLK_UPLL,
-    CLK_MCU,
-    CLK_HSP,
-    CLK_MAX,
-    CLK_AHB,
     CLK_IPG,
-    CLK_PER,
     CLK_32k
 } IMXClk;
 
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 04/12] i.MX: Add the CLK_IPG_HIGH clock
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (2 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 03/12] i.MX: Remove CCM useless clock computation handling Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 05/12] i.MX: Add i.MX6 CCM and ANALOG device Jean-Christophe Dubois
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

EPIT, GPT and other i.MX timers are using "abstract" clocks among which
a CLK_IPG_HIGH clock.

On i.MX25 and i.MX31 CLK_IPG and CLK_IPG_HIGH are mapped to the same clock
but on other SOC like i.MX6 they are mapped to distinct clocks.

This patch add the CLK_IPG_HIGH to prepare for SOC where these 2 clocks are
different.

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

Changes since V1:
 * Not present on V1

Changes since V2:
 * None

 hw/misc/imx25_ccm.c       |  1 +
 hw/misc/imx31_ccm.c       |  1 +
 hw/timer/imx_epit.c       |  8 ++++----
 hw/timer/imx_gpt.c        | 16 ++++++++--------
 include/hw/misc/imx_ccm.h |  1 +
 5 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index e138fc6..225604d 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -171,6 +171,7 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     case CLK_NONE:
         break;
     case CLK_IPG:
+    case CLK_IPG_HIGH:
         freq = imx25_ccm_get_ipg_clk(dev);
         break;
     case CLK_32k:
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index a5caabb..80c1647 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -186,6 +186,7 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
     case CLK_NONE:
         break;
     case CLK_IPG:
+    case CLK_IPG_HIGH:
         freq = imx31_ccm_get_ipg_clk(dev);
         break;
     case CLK_32k:
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 9f26ba8..f5836e2 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -52,10 +52,10 @@ static char const *imx_epit_reg_name(uint32_t reg)
  * These are typical.
  */
 static const IMXClk imx_epit_clocks[] =  {
-    CLK_NONE, /* 00 disabled */
-    CLK_IPG,  /* 01 ipg_clk, ~532MHz */
-    CLK_IPG,  /* 10 ipg_clk_highfreq */
-    CLK_32k,  /* 11 ipg_clk_32k -- ~32kHz */
+    CLK_NONE,      /* 00 disabled */
+    CLK_IPG,       /* 01 ipg_clk, ~532MHz */
+    CLK_IPG_HIGH,  /* 10 ipg_clk_highfreq */
+    CLK_32k,       /* 11 ipg_clk_32k -- ~32kHz */
 };
 
 /*
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 40db63c..ab2e213 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
 };
 
 static const IMXClk imx_gpt_clocks[] = {
-    CLK_NONE, /* 000 No clock source */
-    CLK_IPG,  /* 001 ipg_clk, 532MHz*/
-    CLK_IPG,  /* 010 ipg_clk_highfreq */
-    CLK_NONE, /* 011 not defined */
-    CLK_32k,  /* 100 ipg_clk_32k */
-    CLK_NONE, /* 101 not defined */
-    CLK_NONE, /* 110 not defined */
-    CLK_NONE, /* 111 not defined */
+    CLK_NONE,      /* 000 No clock source */
+    CLK_IPG,       /* 001 ipg_clk, 532MHz*/
+    CLK_IPG_HIGH,  /* 010 ipg_clk_highfreq */
+    CLK_NONE,      /* 011 not defined */
+    CLK_32k,       /* 100 ipg_clk_32k */
+    CLK_NONE,      /* 101 not defined */
+    CLK_NONE,      /* 110 not defined */
+    CLK_NONE,      /* 111 not defined */
 };
 
 static void imx_gpt_set_freq(IMXGPTState *s)
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 378b78d..48a7afa 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -45,6 +45,7 @@ typedef struct IMXCCMState {
 typedef enum  {
     CLK_NONE,
     CLK_IPG,
+    CLK_IPG_HIGH,
     CLK_32k
 } IMXClk;
 
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 05/12] i.MX: Add i.MX6 CCM and ANALOG device.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (3 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 04/12] i.MX: Add the CLK_IPG_HIGH clock Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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:
 * move clk computation to uint64_t to avoid overflow
 * added explanation on _SET, _CLR and _TOG registers
 * move CCM and ANALOG handling in sub memory regions.

Changes since V2:
 * Added #include "qemu/osdep.h"

 hw/misc/Makefile.objs      |   1 +
 hw/misc/imx6_ccm.c         | 774 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/imx6_ccm.h | 197 ++++++++++++
 3 files changed, 972 insertions(+)
 create mode 100644 hw/misc/imx6_ccm.c
 create mode 100644 include/hw/misc/imx6_ccm.h

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ea6cd3c..44ac2e1 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
 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_MILKYMIST) += milkymist-hpdmc.o
 obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c
new file mode 100644
index 0000000..4e1d49d
--- /dev/null
+++ b/hw/misc/imx6_ccm.c
@@ -0,0 +1,774 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * 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.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx6_ccm.h"
+
+#ifndef DEBUG_IMX6_CCM
+#define DEBUG_IMX6_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_IMX6_CCM) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+static char const *imx6_ccm_reg_name(uint32_t reg)
+{
+    static char unknown[20];
+
+    switch (reg) {
+    case CCM_CCR:
+        return "CCR";
+    case CCM_CCDR:
+        return "CCDR";
+    case CCM_CSR:
+        return "CSR";
+    case CCM_CCSR:
+        return "CCSR";
+    case CCM_CACRR:
+        return "CACRR";
+    case CCM_CBCDR:
+        return "CBCDR";
+    case CCM_CBCMR:
+        return "CBCMR";
+    case CCM_CSCMR1:
+        return "CSCMR1";
+    case CCM_CSCMR2:
+        return "CSCMR2";
+    case CCM_CSCDR1:
+        return "CSCDR1";
+    case CCM_CS1CDR:
+        return "CS1CDR";
+    case CCM_CS2CDR:
+        return "CS2CDR";
+    case CCM_CDCDR:
+        return "CDCDR";
+    case CCM_CHSCCDR:
+        return "CHSCCDR";
+    case CCM_CSCDR2:
+        return "CSCDR2";
+    case CCM_CSCDR3:
+        return "CSCDR3";
+    case CCM_CDHIPR:
+        return "CDHIPR";
+    case CCM_CTOR:
+        return "CTOR";
+    case CCM_CLPCR:
+        return "CLPCR";
+    case CCM_CISR:
+        return "CISR";
+    case CCM_CIMR:
+        return "CIMR";
+    case CCM_CCOSR:
+        return "CCOSR";
+    case CCM_CGPR:
+        return "CGPR";
+    case CCM_CCGR0:
+        return "CCGR0";
+    case CCM_CCGR1:
+        return "CCGR1";
+    case CCM_CCGR2:
+        return "CCGR2";
+    case CCM_CCGR3:
+        return "CCGR3";
+    case CCM_CCGR4:
+        return "CCGR4";
+    case CCM_CCGR5:
+        return "CCGR5";
+    case CCM_CCGR6:
+        return "CCGR6";
+    case CCM_CMEOR:
+        return "CMEOR";
+    default:
+        sprintf(unknown, "%d ?", reg);
+        return unknown;
+    }
+}
+
+static char const *imx6_analog_reg_name(uint32_t reg)
+{
+    static char unknown[20];
+
+    switch (reg) {
+    case CCM_ANALOG_PLL_ARM:
+        return "PLL_ARM";
+    case CCM_ANALOG_PLL_ARM_SET:
+        return "PLL_ARM_SET";
+    case CCM_ANALOG_PLL_ARM_CLR:
+        return "PLL_ARM_CLR";
+    case CCM_ANALOG_PLL_ARM_TOG:
+        return "PLL_ARM_TOG";
+    case CCM_ANALOG_PLL_USB1:
+        return "PLL_USB1";
+    case CCM_ANALOG_PLL_USB1_SET:
+        return "PLL_USB1_SET";
+    case CCM_ANALOG_PLL_USB1_CLR:
+        return "PLL_USB1_CLR";
+    case CCM_ANALOG_PLL_USB1_TOG:
+        return "PLL_USB1_TOG";
+    case CCM_ANALOG_PLL_USB2:
+        return "PLL_USB2";
+    case CCM_ANALOG_PLL_USB2_SET:
+        return "PLL_USB2_SET";
+    case CCM_ANALOG_PLL_USB2_CLR:
+        return "PLL_USB2_CLR";
+    case CCM_ANALOG_PLL_USB2_TOG:
+        return "PLL_USB2_TOG";
+    case CCM_ANALOG_PLL_SYS:
+        return "PLL_SYS";
+    case CCM_ANALOG_PLL_SYS_SET:
+        return "PLL_SYS_SET";
+    case CCM_ANALOG_PLL_SYS_CLR:
+        return "PLL_SYS_CLR";
+    case CCM_ANALOG_PLL_SYS_TOG:
+        return "PLL_SYS_TOG";
+    case CCM_ANALOG_PLL_SYS_SS:
+        return "PLL_SYS_SS";
+    case CCM_ANALOG_PLL_SYS_NUM:
+        return "PLL_SYS_NUM";
+    case CCM_ANALOG_PLL_SYS_DENOM:
+        return "PLL_SYS_DENOM";
+    case CCM_ANALOG_PLL_AUDIO:
+        return "PLL_AUDIO";
+    case CCM_ANALOG_PLL_AUDIO_SET:
+        return "PLL_AUDIO_SET";
+    case CCM_ANALOG_PLL_AUDIO_CLR:
+        return "PLL_AUDIO_CLR";
+    case CCM_ANALOG_PLL_AUDIO_TOG:
+        return "PLL_AUDIO_TOG";
+    case CCM_ANALOG_PLL_AUDIO_NUM:
+        return "PLL_AUDIO_NUM";
+    case CCM_ANALOG_PLL_AUDIO_DENOM:
+        return "PLL_AUDIO_DENOM";
+    case CCM_ANALOG_PLL_VIDEO:
+        return "PLL_VIDEO";
+    case CCM_ANALOG_PLL_VIDEO_SET:
+        return "PLL_VIDEO_SET";
+    case CCM_ANALOG_PLL_VIDEO_CLR:
+        return "PLL_VIDEO_CLR";
+    case CCM_ANALOG_PLL_VIDEO_TOG:
+        return "PLL_VIDEO_TOG";
+    case CCM_ANALOG_PLL_VIDEO_NUM:
+        return "PLL_VIDEO_NUM";
+    case CCM_ANALOG_PLL_VIDEO_DENOM:
+        return "PLL_VIDEO_DENOM";
+    case CCM_ANALOG_PLL_MLB:
+        return "PLL_MLB";
+    case CCM_ANALOG_PLL_MLB_SET:
+        return "PLL_MLB_SET";
+    case CCM_ANALOG_PLL_MLB_CLR:
+        return "PLL_MLB_CLR";
+    case CCM_ANALOG_PLL_MLB_TOG:
+        return "PLL_MLB_TOG";
+    case CCM_ANALOG_PLL_ENET:
+        return "PLL_ENET";
+    case CCM_ANALOG_PLL_ENET_SET:
+        return "PLL_ENET_SET";
+    case CCM_ANALOG_PLL_ENET_CLR:
+        return "PLL_ENET_CLR";
+    case CCM_ANALOG_PLL_ENET_TOG:
+        return "PLL_ENET_TOG";
+    case CCM_ANALOG_PFD_480:
+        return "PFD_480";
+    case CCM_ANALOG_PFD_480_SET:
+        return "PFD_480_SET";
+    case CCM_ANALOG_PFD_480_CLR:
+        return "PFD_480_CLR";
+    case CCM_ANALOG_PFD_480_TOG:
+        return "PFD_480_TOG";
+    case CCM_ANALOG_PFD_528:
+        return "PFD_528";
+    case CCM_ANALOG_PFD_528_SET:
+        return "PFD_528_SET";
+    case CCM_ANALOG_PFD_528_CLR:
+        return "PFD_528_CLR";
+    case CCM_ANALOG_PFD_528_TOG:
+        return "PFD_528_TOG";
+    case CCM_ANALOG_MISC0:
+        return "MISC0";
+    case CCM_ANALOG_MISC0_SET:
+        return "MISC0_SET";
+    case CCM_ANALOG_MISC0_CLR:
+        return "MISC0_CLR";
+    case CCM_ANALOG_MISC0_TOG:
+        return "MISC0_TOG";
+    case CCM_ANALOG_MISC2:
+        return "MISC2";
+    case CCM_ANALOG_MISC2_SET:
+        return "MISC2_SET";
+    case CCM_ANALOG_MISC2_CLR:
+        return "MISC2_CLR";
+    case CCM_ANALOG_MISC2_TOG:
+        return "MISC2_TOG";
+    case PMU_REG_1P1:
+        return "PMU_REG_1P1";
+    case PMU_REG_3P0:
+        return "PMU_REG_3P0";
+    case PMU_REG_2P5:
+        return "PMU_REG_2P5";
+    case PMU_REG_CORE:
+        return "PMU_REG_CORE";
+    case PMU_MISC1:
+        return "PMU_MISC1";
+    case PMU_MISC1_SET:
+        return "PMU_MISC1_SET";
+    case PMU_MISC1_CLR:
+        return "PMU_MISC1_CLR";
+    case PMU_MISC1_TOG:
+        return "PMU_MISC1_TOG";
+    case USB_ANALOG_DIGPROG:
+        return "USB_ANALOG_DIGPROG";
+    default:
+        sprintf(unknown, "%d ?", reg);
+        return unknown;
+    }
+}
+
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx6_ccm = {
+    .name = TYPE_IMX6_CCM,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
+        VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 24000000;
+
+    if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
+        freq *= 22;
+    } else {
+        freq *= 20;
+    }
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    freq = imx6_analog_get_pll2_clk(dev) * 18
+           / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    freq = imx6_analog_get_pll2_clk(dev) * 18
+           / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
+    case 0:
+        freq = imx6_analog_get_pll2_clk(dev);
+        break;
+    case 1:
+        freq = imx6_analog_get_pll2_pfd2_clk(dev);
+        break;
+    case 2:
+        freq = imx6_analog_get_pll2_pfd0_clk(dev);
+        break;
+    case 3:
+        freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
+        break;
+    default:
+        /* We should never get there */
+        g_assert_not_reached();
+        break;
+    }
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    freq = imx6_analog_get_periph_clk(dev)
+           / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    freq = imx6_ccm_get_ahb_clk(dev)
+           / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
+{
+    uint64_t freq = 0;
+
+    freq = imx6_ccm_get_ipg_clk(dev)
+           / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
+
+    DPRINTF("freq = %d\n", (uint32_t)freq);
+
+    return freq;
+}
+
+static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+    uint32_t freq = 0;
+    IMX6CCMState *s = IMX6_CCM(dev);
+
+    switch (clock) {
+    case CLK_NONE:
+        break;
+    case CLK_IPG:
+        freq = imx6_ccm_get_ipg_clk(s);
+        break;
+    case CLK_IPG_HIGH:
+        freq = imx6_ccm_get_per_clk(s);
+        break;
+    case CLK_32k:
+        freq = CKIL_FREQ;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+                      TYPE_IMX6_CCM, __func__, clock);
+        break;
+    }
+
+    DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+    return freq;
+}
+
+static void imx6_ccm_reset(DeviceState *dev)
+{
+    IMX6CCMState *s = IMX6_CCM(dev);
+
+    DPRINTF("\n");
+
+    s->ccm[CCM_CCR] = 0x040116FF;
+    s->ccm[CCM_CCDR] = 0x00000000;
+    s->ccm[CCM_CSR] = 0x00000010;
+    s->ccm[CCM_CCSR] = 0x00000100;
+    s->ccm[CCM_CACRR] = 0x00000000;
+    s->ccm[CCM_CBCDR] = 0x00018D40;
+    s->ccm[CCM_CBCMR] = 0x00022324;
+    s->ccm[CCM_CSCMR1] = 0x00F00000;
+    s->ccm[CCM_CSCMR2] = 0x02B92F06;
+    s->ccm[CCM_CSCDR1] = 0x00490B00;
+    s->ccm[CCM_CS1CDR] = 0x0EC102C1;
+    s->ccm[CCM_CS2CDR] = 0x000736C1;
+    s->ccm[CCM_CDCDR] = 0x33F71F92;
+    s->ccm[CCM_CHSCCDR] = 0x0002A150;
+    s->ccm[CCM_CSCDR2] = 0x0002A150;
+    s->ccm[CCM_CSCDR3] = 0x00014841;
+    s->ccm[CCM_CDHIPR] = 0x00000000;
+    s->ccm[CCM_CTOR] = 0x00000000;
+    s->ccm[CCM_CLPCR] = 0x00000079;
+    s->ccm[CCM_CISR] = 0x00000000;
+    s->ccm[CCM_CIMR] = 0xFFFFFFFF;
+    s->ccm[CCM_CCOSR] = 0x000A0001;
+    s->ccm[CCM_CGPR] = 0x0000FE62;
+    s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
+    s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
+    s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
+    s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
+    s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
+    s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
+    s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
+    s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
+
+    s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
+    s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
+    s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
+    s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
+    s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
+    s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
+    s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
+    s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
+    s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
+    s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
+    s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
+    s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
+    s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
+    s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
+    s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
+    s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
+    s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
+
+    s->analog[PMU_REG_1P1] = 0x00001073;
+    s->analog[PMU_REG_3P0] = 0x00000F74;
+    s->analog[PMU_REG_2P5] = 0x00005071;
+    s->analog[PMU_REG_CORE] = 0x00402010;
+    s->analog[PMU_MISC0] = 0x04000000;
+    s->analog[PMU_MISC1] = 0x00000000;
+    s->analog[PMU_MISC2] = 0x00272727;
+
+    s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
+    s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
+    s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
+    s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
+    s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
+    s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
+    s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
+    s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
+    s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
+
+    /* all PLLs need to be locked */
+    s->analog[CCM_ANALOG_PLL_ARM]   |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_USB1]  |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_USB2]  |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_SYS]   |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_MLB]   |= CCM_ANALOG_PLL_LOCK;
+    s->analog[CCM_ANALOG_PLL_ENET]  |= CCM_ANALOG_PLL_LOCK;
+}
+
+static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t value = 0;
+    uint32_t index = offset >> 2;
+    IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+    value = s->ccm[index];
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
+
+    return (uint64_t)value;
+}
+
+static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
+{
+    uint32_t index = offset >> 2;
+    IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
+            (uint32_t)value);
+
+    /*
+     * We will do a better implementation later. In particular some bits
+     * cannot be written to.
+     */
+    s->ccm[index] = (uint32_t)value;
+}
+
+static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t value;
+    uint32_t index = offset >> 2;
+    IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+    switch (index) {
+    case CCM_ANALOG_PLL_ARM_SET:
+    case CCM_ANALOG_PLL_USB1_SET:
+    case CCM_ANALOG_PLL_USB2_SET:
+    case CCM_ANALOG_PLL_SYS_SET:
+    case CCM_ANALOG_PLL_AUDIO_SET:
+    case CCM_ANALOG_PLL_VIDEO_SET:
+    case CCM_ANALOG_PLL_MLB_SET:
+    case CCM_ANALOG_PLL_ENET_SET:
+    case CCM_ANALOG_PFD_480_SET:
+    case CCM_ANALOG_PFD_528_SET:
+    case CCM_ANALOG_MISC0_SET:
+    case PMU_MISC1_SET:
+    case CCM_ANALOG_MISC2_SET:
+    case USB_ANALOG_USB1_VBUS_DETECT_SET:
+    case USB_ANALOG_USB1_CHRG_DETECT_SET:
+    case USB_ANALOG_USB1_MISC_SET:
+    case USB_ANALOG_USB2_VBUS_DETECT_SET:
+    case USB_ANALOG_USB2_CHRG_DETECT_SET:
+    case USB_ANALOG_USB2_MISC_SET:
+        /*
+         * All REG_NAME_SET register access are in fact targeting the
+         * the REG_NAME register.
+         */
+        value = s->analog[index - 1];
+        break;
+    case CCM_ANALOG_PLL_ARM_CLR:
+    case CCM_ANALOG_PLL_USB1_CLR:
+    case CCM_ANALOG_PLL_USB2_CLR:
+    case CCM_ANALOG_PLL_SYS_CLR:
+    case CCM_ANALOG_PLL_AUDIO_CLR:
+    case CCM_ANALOG_PLL_VIDEO_CLR:
+    case CCM_ANALOG_PLL_MLB_CLR:
+    case CCM_ANALOG_PLL_ENET_CLR:
+    case CCM_ANALOG_PFD_480_CLR:
+    case CCM_ANALOG_PFD_528_CLR:
+    case CCM_ANALOG_MISC0_CLR:
+    case PMU_MISC1_CLR:
+    case CCM_ANALOG_MISC2_CLR:
+    case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+    case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+    case USB_ANALOG_USB1_MISC_CLR:
+    case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+    case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+    case USB_ANALOG_USB2_MISC_CLR:
+        /*
+         * All REG_NAME_CLR register access are in fact targeting the
+         * the REG_NAME register.
+         */
+        value = s->analog[index - 2];
+        break;
+    case CCM_ANALOG_PLL_ARM_TOG:
+    case CCM_ANALOG_PLL_USB1_TOG:
+    case CCM_ANALOG_PLL_USB2_TOG:
+    case CCM_ANALOG_PLL_SYS_TOG:
+    case CCM_ANALOG_PLL_AUDIO_TOG:
+    case CCM_ANALOG_PLL_VIDEO_TOG:
+    case CCM_ANALOG_PLL_MLB_TOG:
+    case CCM_ANALOG_PLL_ENET_TOG:
+    case CCM_ANALOG_PFD_480_TOG:
+    case CCM_ANALOG_PFD_528_TOG:
+    case CCM_ANALOG_MISC0_TOG:
+    case PMU_MISC1_TOG:
+    case CCM_ANALOG_MISC2_TOG:
+    case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+    case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+    case USB_ANALOG_USB1_MISC_TOG:
+    case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+    case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+    case USB_ANALOG_USB2_MISC_TOG:
+        /*
+         * All REG_NAME_TOG register access are in fact targeting the
+         * the REG_NAME register.
+         */
+        value = s->analog[index - 3];
+        break;
+    default:
+        value = s->analog[index];
+        break;
+    }
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
+
+    return (uint64_t)value;
+}
+
+static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
+                              unsigned size)
+{
+    uint32_t index = offset >> 2;
+    IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
+            (uint32_t)value);
+
+    switch (index) {
+    case CCM_ANALOG_PLL_ARM_SET:
+    case CCM_ANALOG_PLL_USB1_SET:
+    case CCM_ANALOG_PLL_USB2_SET:
+    case CCM_ANALOG_PLL_SYS_SET:
+    case CCM_ANALOG_PLL_AUDIO_SET:
+    case CCM_ANALOG_PLL_VIDEO_SET:
+    case CCM_ANALOG_PLL_MLB_SET:
+    case CCM_ANALOG_PLL_ENET_SET:
+    case CCM_ANALOG_PFD_480_SET:
+    case CCM_ANALOG_PFD_528_SET:
+    case CCM_ANALOG_MISC0_SET:
+    case PMU_MISC1_SET:
+    case CCM_ANALOG_MISC2_SET:
+    case USB_ANALOG_USB1_VBUS_DETECT_SET:
+    case USB_ANALOG_USB1_CHRG_DETECT_SET:
+    case USB_ANALOG_USB1_MISC_SET:
+    case USB_ANALOG_USB2_VBUS_DETECT_SET:
+    case USB_ANALOG_USB2_CHRG_DETECT_SET:
+    case USB_ANALOG_USB2_MISC_SET:
+        /*
+         * All REG_NAME_SET register access are in fact targeting the
+         * the REG_NAME register. So we change the value of the
+         * REG_NAME register, setting bits passed in the value.
+         */
+        s->analog[index - 1] |= value;
+        break;
+    case CCM_ANALOG_PLL_ARM_CLR:
+    case CCM_ANALOG_PLL_USB1_CLR:
+    case CCM_ANALOG_PLL_USB2_CLR:
+    case CCM_ANALOG_PLL_SYS_CLR:
+    case CCM_ANALOG_PLL_AUDIO_CLR:
+    case CCM_ANALOG_PLL_VIDEO_CLR:
+    case CCM_ANALOG_PLL_MLB_CLR:
+    case CCM_ANALOG_PLL_ENET_CLR:
+    case CCM_ANALOG_PFD_480_CLR:
+    case CCM_ANALOG_PFD_528_CLR:
+    case CCM_ANALOG_MISC0_CLR:
+    case PMU_MISC1_CLR:
+    case CCM_ANALOG_MISC2_CLR:
+    case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+    case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+    case USB_ANALOG_USB1_MISC_CLR:
+    case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+    case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+    case USB_ANALOG_USB2_MISC_CLR:
+        /*
+         * All REG_NAME_CLR register access are in fact targeting the
+         * the REG_NAME register. So we change the value of the
+         * REG_NAME register, unsetting bits passed in the value.
+         */
+        s->analog[index - 2] &= ~value;
+        break;
+    case CCM_ANALOG_PLL_ARM_TOG:
+    case CCM_ANALOG_PLL_USB1_TOG:
+    case CCM_ANALOG_PLL_USB2_TOG:
+    case CCM_ANALOG_PLL_SYS_TOG:
+    case CCM_ANALOG_PLL_AUDIO_TOG:
+    case CCM_ANALOG_PLL_VIDEO_TOG:
+    case CCM_ANALOG_PLL_MLB_TOG:
+    case CCM_ANALOG_PLL_ENET_TOG:
+    case CCM_ANALOG_PFD_480_TOG:
+    case CCM_ANALOG_PFD_528_TOG:
+    case CCM_ANALOG_MISC0_TOG:
+    case PMU_MISC1_TOG:
+    case CCM_ANALOG_MISC2_TOG:
+    case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+    case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+    case USB_ANALOG_USB1_MISC_TOG:
+    case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+    case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+    case USB_ANALOG_USB2_MISC_TOG:
+        /*
+         * All REG_NAME_TOG register access are in fact targeting the
+         * the REG_NAME register. So we change the value of the
+         * REG_NAME register, toggling bits passed in the value.
+         */
+        s->analog[index - 3] ^= value;
+        break;
+    default:
+        /*
+         * We will do a better implementation later. In particular some bits
+         * cannot be written to.
+         */
+        s->analog[index] = value;
+        break;
+    }
+}
+
+static const struct MemoryRegionOps imx6_ccm_ops = {
+    .read = imx6_ccm_read,
+    .write = imx6_ccm_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 const struct MemoryRegionOps imx6_analog_ops = {
+    .read = imx6_analog_read,
+    .write = imx6_analog_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_ccm_init(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+    IMX6CCMState *s = IMX6_CCM(obj);
+
+    /* initialize a container for the all memory range */
+    memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
+
+    /* We initialize an IO memory region for the CCM part */
+    memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
+                          TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
+
+    /* Add the CCM as a subregion at offset 0 */
+    memory_region_add_subregion(&s->container, 0, &s->ioccm);
+
+    /* We initialize an IO memory region for the ANALOG part */
+    memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
+                          TYPE_IMX6_CCM ".analog",
+                          CCM_ANALOG_MAX * sizeof(uint32_t));
+
+    /* Add the ANALOG as a subregion at offset 0x4000 */
+    memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
+
+    sysbus_init_mmio(sd, &s->container);
+}
+
+static void imx6_ccm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+    dc->reset = imx6_ccm_reset;
+    dc->vmsd = &vmstate_imx6_ccm;
+    dc->desc = "i.MX6 Clock Control Module";
+
+    ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx6_ccm_info = {
+    .name          = TYPE_IMX6_CCM,
+    .parent        = TYPE_IMX_CCM,
+    .instance_size = sizeof(IMX6CCMState),
+    .instance_init = imx6_ccm_init,
+    .class_init    = imx6_ccm_class_init,
+};
+
+static void imx6_ccm_register_types(void)
+{
+    type_register_static(&imx6_ccm_info);
+}
+
+type_init(imx6_ccm_register_types)
diff --git a/include/hw/misc/imx6_ccm.h b/include/hw/misc/imx6_ccm.h
new file mode 100644
index 0000000..8050580
--- /dev/null
+++ b/include/hw/misc/imx6_ccm.h
@@ -0,0 +1,197 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * 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_CCM_H
+#define IMX6_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+#include "qemu/bitops.h"
+
+#define CCM_CCR 0
+#define CCM_CCDR 1
+#define CCM_CSR 2
+#define CCM_CCSR 3
+#define CCM_CACRR 4
+#define CCM_CBCDR 5
+#define CCM_CBCMR 6
+#define CCM_CSCMR1 7
+#define CCM_CSCMR2 8
+#define CCM_CSCDR1 9
+#define CCM_CS1CDR 10
+#define CCM_CS2CDR 11
+#define CCM_CDCDR 12
+#define CCM_CHSCCDR 13
+#define CCM_CSCDR2 14
+#define CCM_CSCDR3 15
+#define CCM_CDHIPR 18
+#define CCM_CTOR 20
+#define CCM_CLPCR 21
+#define CCM_CISR 22
+#define CCM_CIMR 23
+#define CCM_CCOSR 24
+#define CCM_CGPR 25
+#define CCM_CCGR0 26
+#define CCM_CCGR1 27
+#define CCM_CCGR2 28
+#define CCM_CCGR3 29
+#define CCM_CCGR4 30
+#define CCM_CCGR5 31
+#define CCM_CCGR6 32
+#define CCM_CMEOR 34
+#define CCM_MAX 35
+
+#define CCM_ANALOG_PLL_ARM 0
+#define CCM_ANALOG_PLL_ARM_SET 1
+#define CCM_ANALOG_PLL_ARM_CLR 2
+#define CCM_ANALOG_PLL_ARM_TOG 3
+#define CCM_ANALOG_PLL_USB1 4
+#define CCM_ANALOG_PLL_USB1_SET 5
+#define CCM_ANALOG_PLL_USB1_CLR 6
+#define CCM_ANALOG_PLL_USB1_TOG 7
+#define CCM_ANALOG_PLL_USB2 8
+#define CCM_ANALOG_PLL_USB2_SET 9
+#define CCM_ANALOG_PLL_USB2_CLR 10
+#define CCM_ANALOG_PLL_USB2_TOG 11
+#define CCM_ANALOG_PLL_SYS 12
+#define CCM_ANALOG_PLL_SYS_SET 13
+#define CCM_ANALOG_PLL_SYS_CLR 14
+#define CCM_ANALOG_PLL_SYS_TOG 15
+#define CCM_ANALOG_PLL_SYS_SS 16
+#define CCM_ANALOG_PLL_SYS_NUM 20
+#define CCM_ANALOG_PLL_SYS_DENOM 24
+#define CCM_ANALOG_PLL_AUDIO 28
+#define CCM_ANALOG_PLL_AUDIO_SET 29
+#define CCM_ANALOG_PLL_AUDIO_CLR 30
+#define CCM_ANALOG_PLL_AUDIO_TOG 31
+#define CCM_ANALOG_PLL_AUDIO_NUM 32
+#define CCM_ANALOG_PLL_AUDIO_DENOM 36
+#define CCM_ANALOG_PLL_VIDEO 40
+#define CCM_ANALOG_PLL_VIDEO_SET 41
+#define CCM_ANALOG_PLL_VIDEO_CLR 42
+#define CCM_ANALOG_PLL_VIDEO_TOG 44
+#define CCM_ANALOG_PLL_VIDEO_NUM 46
+#define CCM_ANALOG_PLL_VIDEO_DENOM 48
+#define CCM_ANALOG_PLL_MLB 52
+#define CCM_ANALOG_PLL_MLB_SET 53
+#define CCM_ANALOG_PLL_MLB_CLR 54
+#define CCM_ANALOG_PLL_MLB_TOG 55
+#define CCM_ANALOG_PLL_ENET 56
+#define CCM_ANALOG_PLL_ENET_SET 57
+#define CCM_ANALOG_PLL_ENET_CLR 58
+#define CCM_ANALOG_PLL_ENET_TOG 59
+#define CCM_ANALOG_PFD_480 60
+#define CCM_ANALOG_PFD_480_SET 61
+#define CCM_ANALOG_PFD_480_CLR 62
+#define CCM_ANALOG_PFD_480_TOG 63
+#define CCM_ANALOG_PFD_528 64
+#define CCM_ANALOG_PFD_528_SET 65
+#define CCM_ANALOG_PFD_528_CLR 66
+#define CCM_ANALOG_PFD_528_TOG 67
+
+/* PMU registers */
+#define PMU_REG_1P1 68
+#define PMU_REG_3P0 72
+#define PMU_REG_2P5 76
+#define PMU_REG_CORE 80
+
+#define CCM_ANALOG_MISC0 84
+#define PMU_MISC0 84
+#define CCM_ANALOG_MISC0_SET 85
+#define CCM_ANALOG_MISC0_CLR 86
+#define CCM_ANALOG_MISC0_TOG 87
+
+#define PMU_MISC1 88
+#define PMU_MISC1_SET 89
+#define PMU_MISC1_CLR 90
+#define PMU_MISC1_TOG 91
+
+#define CCM_ANALOG_MISC2 92
+#define PMU_MISC2 92
+#define CCM_ANALOG_MISC2_SET 93
+#define CCM_ANALOG_MISC2_CLR 94
+#define CCM_ANALOG_MISC2_TOG 95
+
+#define USB_ANALOG_USB1_VBUS_DETECT 104
+#define USB_ANALOG_USB1_VBUS_DETECT_SET 105
+#define USB_ANALOG_USB1_VBUS_DETECT_CLR 106
+#define USB_ANALOG_USB1_VBUS_DETECT_TOG 107
+#define USB_ANALOG_USB1_CHRG_DETECT 108
+#define USB_ANALOG_USB1_CHRG_DETECT_SET 109
+#define USB_ANALOG_USB1_CHRG_DETECT_CLR 110
+#define USB_ANALOG_USB1_CHRG_DETECT_TOG 111
+#define USB_ANALOG_USB1_VBUS_DETECT_STAT 112
+#define USB_ANALOG_USB1_CHRG_DETECT_STAT 116
+#define USB_ANALOG_USB1_MISC 124
+#define USB_ANALOG_USB1_MISC_SET 125
+#define USB_ANALOG_USB1_MISC_CLR 126
+#define USB_ANALOG_USB1_MISC_TOG 127
+#define USB_ANALOG_USB2_VBUS_DETECT 128
+#define USB_ANALOG_USB2_VBUS_DETECT_SET 129
+#define USB_ANALOG_USB2_VBUS_DETECT_CLR 130
+#define USB_ANALOG_USB2_VBUS_DETECT_TOG 131
+#define USB_ANALOG_USB2_CHRG_DETECT 132
+#define USB_ANALOG_USB2_CHRG_DETECT_SET 133
+#define USB_ANALOG_USB2_CHRG_DETECT_CLR 134
+#define USB_ANALOG_USB2_CHRG_DETECT_TOG 135
+#define USB_ANALOG_USB2_VBUS_DETECT_STAT 136
+#define USB_ANALOG_USB2_CHRG_DETECT_STAT 140
+#define USB_ANALOG_USB2_MISC 148
+#define USB_ANALOG_USB2_MISC_SET 149
+#define USB_ANALOG_USB2_MISC_CLR 150
+#define USB_ANALOG_USB2_MISC_TOG 151
+#define USB_ANALOG_DIGPROG 152
+#define CCM_ANALOG_MAX 153
+
+/* CCM_CBCMR */
+#define PRE_PERIPH_CLK_SEL_SHIFT  (18)
+#define PRE_PERIPH_CLK_SEL_LENGTH (2)
+
+/* CCM_CBCDR */
+#define AHB_PODF_SHIFT           (10)
+#define AHB_PODF_LENGTH          (3)
+#define IPG_PODF_SHIFT           (8)
+#define IPG_PODF_LENGTH          (2)
+
+/* CCM_CSCMR1 */
+#define PERCLK_PODF_SHIFT        (0)
+#define PERCLK_PODF_LENGTH       (6)
+
+/* CCM_ANALOG_PFD_528 */
+#define PFD0_FRAC_SHIFT          (0)
+#define PFD0_FRAC_LENGTH         (6)
+#define PFD2_FRAC_SHIFT          (16)
+#define PFD2_FRAC_LENGTH         (6)
+
+/* CCM_ANALOG_PLL_SYS */
+#define DIV_SELECT_SHIFT         (0)
+#define DIV_SELECT_LENGTH        (1)
+
+#define CCM_ANALOG_PLL_LOCK      (1 << 31);
+
+#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH)
+
+#define TYPE_IMX6_CCM "imx6.ccm"
+#define IMX6_CCM(obj) OBJECT_CHECK(IMX6CCMState, (obj), TYPE_IMX6_CCM)
+
+typedef struct IMX6CCMState {
+    /* <private> */
+    IMXCCMState parent_obj;
+
+    /* <public> */
+    MemoryRegion container;
+    MemoryRegion ioccm;
+    MemoryRegion ioanalog;
+
+    uint32_t ccm[CCM_MAX];
+    uint32_t analog[CCM_ANALOG_MAX];
+
+} IMX6CCMState;
+
+#endif /* IMX6_CCM_H */
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (4 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 05/12] i.MX: Add i.MX6 CCM and ANALOG device Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:14   ` Peter Maydell
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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

 target-arm/Makefile.objs  |   1 +
 target-arm/arm-powerctl.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++
 target-arm/arm-powerctl.h |  22 +++++++
 target-arm/psci.c         |  72 ++-------------------
 4 files changed, 184 insertions(+), 67 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..99c2af2
--- /dev/null
+++ b/target-arm/arm-powerctl.c
@@ -0,0 +1,156 @@
+/*
+ * 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 "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: Resquesting unknown CPU %" PRId64 "\n",
+                  __func__, id);
+
+    return NULL;
+}
+
+int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id)
+{
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+    CPUClass *target_cpu_class;
+    CPUARMState *env = &ARM_CPU(current_cpu)->env;
+
+    DPRINTF("cpu %" PRId64 " @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
+            cpuid, entry, context_id);
+
+    /* change to the cpu we are powering up */
+    target_cpu_state = arm_get_cpu_by_id(cpuid);
+    if (!target_cpu_state) {
+        return -1;
+    }
+    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 -1;
+    }
+    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) {
+            return -1;
+        }
+        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);
+
+    return 0;
+}
+
+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 -1;
+    }
+    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 0;
+}
diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
new file mode 100644
index 0000000..13fb526
--- /dev/null
+++ b/target-arm/arm-powerctl.h
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+CPUState *arm_get_cpu_by_id(uint64_t id);
+
+int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id);
+
+void arm_set_cpu_off(uint64_t cpuid);
+
+int arm_reset_cpu(uint64_t cpuid);
+
+#endif
diff --git a/target-arm/psci.c b/target-arm/psci.c
index c55487f..362105f 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;
@@ -168,51 +152,8 @@ void arm_handle_psci_call(ARMCPU *cpu)
         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) ?
+                             QEMU_PSCI_RET_INVALID_PARAMS : 0;
         break;
     case QEMU_PSCI_0_1_FN_CPU_OFF:
     case QEMU_PSCI_0_2_FN_CPU_OFF:
@@ -250,9 +191,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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (5 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:20   ` Peter Maydell
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 08/12] i.MX: Add missing descriptions in devices Jean-Christophe Dubois
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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.

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"

 hw/misc/Makefile.objs      |   1 +
 hw/misc/imx6_src.c         | 260 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/imx6_src.h |  73 +++++++++++++
 3 files changed, 334 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..2d9867b
--- /dev/null
+++ b/hw/misc/imx6_src.c
@@ -0,0 +1,260 @@
+/*
+ * 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, SRC_MAX * sizeof(uint32_t));
+
+    /* 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 (uint64_t)value;
+}
+
+static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
+{
+    IMX6SRCState *s = (IMX6SRCState *)opaque;
+    uint32_t index = offset >> 2;
+    uint64_t change_mask;
+
+    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)value);
+
+    change_mask = s->regs[index] ^ (uint32_t)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(value, CORE3_ENABLE)) {
+                /* CORE 3 is brought up */
+                arm_set_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8]);
+            } 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, &value);
+            clear_bit(CORE3_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE2_ENABLE)) {
+            if (EXTRACT(value, CORE2_ENABLE)) {
+                /* CORE 2 is brought up */
+                arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6]);
+            } 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, &value);
+            clear_bit(CORE2_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE1_ENABLE)) {
+            if (EXTRACT(value, CORE1_ENABLE)) {
+                /* CORE 1 is brought up */
+                arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4]);
+            } 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, &value);
+            clear_bit(CORE1_RST_SHIFT, &change_mask);
+        }
+        if (EXTRACT(change_mask, CORE0_RST)) {
+            arm_reset_cpu(0);
+            clear_bit(CORE0_RST_SHIFT, &value);
+        }
+        if (EXTRACT(change_mask, CORE1_RST)) {
+            arm_reset_cpu(1);
+            clear_bit(CORE1_RST_SHIFT, &value);
+        }
+        if (EXTRACT(change_mask, CORE2_RST)) {
+            arm_reset_cpu(2);
+            clear_bit(CORE2_RST_SHIFT, &value);
+        }
+        if (EXTRACT(change_mask, CORE3_RST)) {
+            arm_reset_cpu(3);
+            clear_bit(CORE3_RST_SHIFT, &value);
+        }
+        if (EXTRACT(change_mask, SW_IPU2_RST)) {
+            /* We pretend the IPU2 is reseted */
+            clear_bit(SW_IPU2_RST_SHIFT, &value);
+        }
+        if (EXTRACT(change_mask, SW_IPU1_RST)) {
+            /* We pretend the IPU1 is reseted */
+            clear_bit(SW_IPU1_RST_SHIFT, &value);
+        }
+        s->regs[index] = value;
+        break;
+    default:
+        s->regs[index] = 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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 08/12] i.MX: Add missing descriptions in devices.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (6 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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:
 * Not present on V1

Changes since v2: 
 * None

 hw/arm/fsl-imx25.c | 1 +
 hw/arm/fsl-imx31.c | 1 +
 hw/i2c/imx_i2c.c   | 1 +
 hw/net/imx_fec.c   | 1 +
 4 files changed, 4 insertions(+)

diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index fb743bf..1fbc317 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
      * arm_cpu_class_init()
      */
     dc->cannot_destroy_with_object_finalize_yet = true;
+    dc->desc = "i.MX25 SOC";
 }
 
 static const TypeInfo fsl_imx25_type_info = {
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index f2c2ce5..0d69a2c 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
      * arm_cpu_class_init()
      */
     dc->cannot_destroy_with_object_finalize_yet = true;
+    dc->desc = "i.MX31 SOC";
 }
 
 static const TypeInfo fsl_imx31_type_info = {
diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c
index e435448..a01e43e 100644
--- a/hw/i2c/imx_i2c.c
+++ b/hw/i2c/imx_i2c.c
@@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &imx_i2c_vmstate;
     dc->reset = imx_i2c_reset;
     dc->realize = imx_i2c_realize;
+    dc->desc = "i.MX I2C Controller";
 }
 
 static const TypeInfo imx_i2c_type_info = {
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index 3bd5517..e60e338 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -693,6 +693,7 @@ static void imx_fec_class_init(ObjectClass *klass, void *data)
     dc->reset = imx_fec_reset;
     dc->props = imx_fec_properties;
     dc->realize = imx_fec_realize;
+    dc->desc = "i.MX FEC Ethernet Controller";
 }
 
 static const TypeInfo imx_fec_info = {
-- 
2.5.0

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

* [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (7 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 08/12] i.MX: Add missing descriptions in devices Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:25   ` Peter Maydell
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

This one is build on top of the existing FIFO8

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()

 include/qemu/fifo32.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 186 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..f1b9ecf
--- /dev/null
+++ b/include/qemu/fifo32.h
@@ -0,0 +1,186 @@
+/*
+ * 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 bits 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 bits 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 bits 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 bits words to push
+ *
+ * Push a 32 bits 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;
+}
+
+/**
+ * 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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (8 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:31   ` Peter Maydell
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter; +Cc: Jean-Christophe Dubois

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

 hw/ssi/Makefile.objs     |   1 +
 hw/ssi/imx_spi.c         | 459 +++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ssi/imx_spi.h | 103 +++++++++++
 3 files changed, 563 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..bf1c660
--- /dev/null
+++ b/hw/ssi/imx_spi.c
@@ -0,0 +1,459 @@
+/*
+ * 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);
+    int i;
+
+    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;
+
+    for (i = 0; i < 4; i++) {
+        qemu_set_irq(s->cs_lines[i], 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:
+        /* Linux is not using MSGDATA */
+        /* We assume the beavior is idential to TXDATA */
+        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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (9 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:33   ` Peter Maydell
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
  2016-03-16 11:04 ` [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Peter Maydell
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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.

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.

 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 a9f82a1..5562926 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 a711e4d..4a3011a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,3 +16,4 @@ 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
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] 30+ messages in thread

* [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (10 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
@ 2016-03-01 22:27 ` Jean-Christophe Dubois
  2016-03-10 10:38   ` Peter Maydell
  2016-03-16 11:04 ` [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Peter Maydell
  12 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe Dubois @ 2016-03-01 22:27 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.

 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 4a3011a..d244d10 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,4 +16,4 @@ 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
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] 30+ messages in thread

* Re: [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
@ 2016-03-10 10:14   ` Peter Maydell
  2016-03-16 22:19     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:14 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, 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
>
>  target-arm/Makefile.objs  |   1 +
>  target-arm/arm-powerctl.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++
>  target-arm/arm-powerctl.h |  22 +++++++
>  target-arm/psci.c         |  72 ++-------------------
>  4 files changed, 184 insertions(+), 67 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..99c2af2
> --- /dev/null
> +++ b/target-arm/arm-powerctl.c
> @@ -0,0 +1,156 @@
> +/*
> + * 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 "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: Resquesting unknown CPU %" PRId64 "\n",
> +                  __func__, id);

"Requesting"

> +
> +    return NULL;
> +}
> +
> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id)
> +{
> +    CPUState *target_cpu_state;
> +    ARMCPU *target_cpu;
> +    CPUClass *target_cpu_class;
> +    CPUARMState *env = &ARM_CPU(current_cpu)->env;
> +
> +    DPRINTF("cpu %" PRId64 " @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
> +            cpuid, entry, context_id);
> +
> +    /* change to the cpu we are powering up */
> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
> +    if (!target_cpu_state) {
> +        return -1;
> +    }
> +    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 -1;
> +    }
> +    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.

This is a generic function so it's a bit out of place for it to
talk about PSCI spec details. What are the semantics provided by
this helper function?

> +     *
> +     * 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

It's no longer true that we only implement EL1, incidentally.

> +     * (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) {
> +            return -1;
> +        }
> +        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);
> +
> +    return 0;
> +}
> +
> +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 -1;
> +    }
> +    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);

Whether requesting a power-off of a powered off CPU
is a guest error depends on the API used to request
it, so you can't log this as a guest error in the
generic helper function.

> +        return -1;
> +    }
> +
> +    /* Reset the cpu */
> +    cpu_reset(target_cpu_state);
> +
> +    return 0;
> +}
> diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
> new file mode 100644
> index 0000000..13fb526
> --- /dev/null
> +++ b/target-arm/arm-powerctl.h
> @@ -0,0 +1,22 @@
> +/*
> + * 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
> +
> +CPUState *arm_get_cpu_by_id(uint64_t id);
> +
> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id);
> +
> +void arm_set_cpu_off(uint64_t cpuid);
> +
> +int arm_reset_cpu(uint64_t cpuid);

Please provide doc comments for new globally-visible functions.

> +
> +#endif
> diff --git a/target-arm/psci.c b/target-arm/psci.c
> index c55487f..362105f 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;
> @@ -168,51 +152,8 @@ void arm_handle_psci_call(ARMCPU *cpu)
>          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;
> -        }

You've lost the distinction between these two failure
modes -- the PSCI error codes are returned to the guest
and are mandated by the PSCI spec so you need to distinguish
them.


> -        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) ?
> +                             QEMU_PSCI_RET_INVALID_PARAMS : 0;
>          break;
>      case QEMU_PSCI_0_1_FN_CPU_OFF:
>      case QEMU_PSCI_0_2_FN_CPU_OFF:
> @@ -250,9 +191,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] 30+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device.
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
@ 2016-03-10 10:20   ` Peter Maydell
  0 siblings, 0 replies; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:20 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> This controller is also present in i.MX5X devices but they are not
> yet emulated by QEMU.
>
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---

> +static void imx6_src_reset(DeviceState *dev)
> +{
> +    IMX6SRCState *s = IMX6_SRC(dev);
> +
> +    DPRINTF("\n");
> +
> +    memset(s->regs, 0, SRC_MAX * sizeof(uint32_t));

   memset(s->regs, 0, sizeof(s->regs));

is slightly better.

> +
> +    /* 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 (uint64_t)value;

This cast is unnecessary.

> +}
> +
> +static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
> +                           unsigned size)
> +{
> +    IMX6SRCState *s = (IMX6SRCState *)opaque;
> +    uint32_t index = offset >> 2;
> +    uint64_t change_mask;
> +
> +    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)value);
> +
> +    change_mask = s->regs[index] ^ (uint32_t)value;

s->regs[] is 32 bit, and you've explicitly cast value to 32-bits,
so why is change_mask 64 bits?

> +        if (EXTRACT(change_mask, CORE3_RST)) {
> +            arm_reset_cpu(3);
> +            clear_bit(CORE3_RST_SHIFT, &value);
> +        }
> +        if (EXTRACT(change_mask, SW_IPU2_RST)) {
> +            /* We pretend the IPU2 is reseted */

"reset" (and below).

> +            clear_bit(SW_IPU2_RST_SHIFT, &value);
> +        }
> +        if (EXTRACT(change_mask, SW_IPU1_RST)) {
> +            /* We pretend the IPU1 is reseted */
> +            clear_bit(SW_IPU1_RST_SHIFT, &value);
> +        }
> +        s->regs[index] = value;
> +        break;

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

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
@ 2016-03-10 10:25   ` Peter Maydell
  2016-03-10 19:26     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:25 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> This one is build on top of the existing FIFO8
>
> 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()
>
>  include/qemu/fifo32.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 186 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..f1b9ecf
> --- /dev/null
> +++ b/include/qemu/fifo32.h
> @@ -0,0 +1,186 @@
> +/*
> + * 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 bits words

"32 bits word" should be "32 bit word" throughout.

There should be a comment somewhere noting that there is no
fifo32_pop_buf() because the data is not stored in the buffer
as a set of native-order words.

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

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
@ 2016-03-10 10:31   ` Peter Maydell
  2016-03-10 19:26     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:31 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---
>
> +
> +static void imx_spi_reset(DeviceState *dev)
> +{
> +    IMXSPIState *s = IMX_SPI(dev);
> +    int i;
> +
> +    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;
> +
> +    for (i = 0; i < 4; i++) {
> +        qemu_set_irq(s->cs_lines[i], 0);
> +    }

Calling qemu_set_irq() from a reset function is generally
a bad idea.

> +}

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

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation.
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
@ 2016-03-10 10:33   ` Peter Maydell
  0 siblings, 0 replies; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:33 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> 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.
>
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>

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

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
@ 2016-03-10 10:38   ` Peter Maydell
  2016-03-10 19:24     ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 10:38 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 2 March 2016 at 05:27, 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");
> +        if (spi_dev) {
> +            SSIBus *spi_bus;
> +
> +            spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi");

This looks odd. You should just be able to do
 spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), "spi1");
without using object_resolve_path_component() to try to find an
SPI device object, because your SoC device should have alias properties
which provide access to its SPI subcomponents' SPI buses.
See hw/arm/xlnx-ep108.c for an example of the board code for this and
hw/arm/xlnx-zynqmp.c for the SoC code which calls
object_property_add_alias() to set up the aliases.

> +            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);
> +                }
> +            }
> +        }
> +    }

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-10 10:38   ` Peter Maydell
@ 2016-03-10 19:24     ` Jean-Christophe DUBOIS
  2016-03-10 23:57       ` Peter Maydell
  0 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-10 19:24 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 10/03/2016 11:38, Peter Maydell a écrit :
> On 2 March 2016 at 05:27, 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");
>> +        if (spi_dev) {
>> +            SSIBus *spi_bus;
>> +
>> +            spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi");
> This looks odd. You should just be able to do
>   spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), "spi1");
> without using object_resolve_path_component() to try to find an
> SPI device object, because your SoC device should have alias properties
> which provide access to its SPI subcomponents' SPI buses.
> See hw/arm/xlnx-ep108.c for an example of the board code for this and
> hw/arm/xlnx-zynqmp.c for the SoC code which calls
> object_property_add_alias() to set up the aliases.

I certainly could do as you proposed.

The problem is that I also need the spi_dev device for the 
sysbus_connect_irq() call below.

My spi_dev is referenced as "spi1" in the i.MX6 doc and I added a 'spi1" 
property for it in the i.MX6 soc.

Once I have the spi_dev device it is trivial to retrieve the spi_bus 
attached to it.

So, yes this is not in line with what is done in xlnx-zynqmp.c but the 
need is a bit different.

JC

>
>> +            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);
>> +                }
>> +            }
>> +        }
>> +    }
> thanks
> -- PMM
>

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

* Re: [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller
  2016-03-10 10:31   ` Peter Maydell
@ 2016-03-10 19:26     ` Jean-Christophe DUBOIS
  2016-03-11  0:01       ` Peter Maydell
  0 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-10 19:26 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 10/03/2016 11:31, Peter Maydell a écrit :
> On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
>> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
>> ---
>>
>> +
>> +static void imx_spi_reset(DeviceState *dev)
>> +{
>> +    IMXSPIState *s = IMX_SPI(dev);
>> +    int i;
>> +
>> +    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;
>> +
>> +    for (i = 0; i < 4; i++) {
>> +        qemu_set_irq(s->cs_lines[i], 0);
>> +    }
> Calling qemu_set_irq() from a reset function is generally
> a bad idea.

Could I assume that all irq lines are set automatically to 0 on reset?

>
>> +}
> Otherwise
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>
> thanks
> -- PMM
>

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

* Re: [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation
  2016-03-10 10:25   ` Peter Maydell
@ 2016-03-10 19:26     ` Jean-Christophe DUBOIS
  0 siblings, 0 replies; 30+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-10 19:26 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 10/03/2016 11:25, Peter Maydell a écrit :
> On 2 March 2016 at 05:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
>> This one is build on top of the existing FIFO8
>>
>> 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()
>>
>>   include/qemu/fifo32.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 186 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..f1b9ecf
>> --- /dev/null
>> +++ b/include/qemu/fifo32.h
>> @@ -0,0 +1,186 @@
>> +/*
>> + * 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 bits words
> "32 bits word" should be "32 bit word" throughout.
>
> There should be a comment somewhere noting that there is no
> fifo32_pop_buf() because the data is not stored in the buffer
> as a set of native-order words.

OK

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

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

* Re: [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-10 19:24     ` Jean-Christophe DUBOIS
@ 2016-03-10 23:57       ` Peter Maydell
  2016-03-15 21:40         ` Jean-Christophe DUBOIS
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-10 23:57 UTC (permalink / raw)
  To: Jean-Christophe DUBOIS; +Cc: QEMU Developers, Peter Crosthwaite

On 11 March 2016 at 02:24, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Le 10/03/2016 11:38, Peter Maydell a écrit :
>>
>> On 2 March 2016 at 05:27, 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");
>>> +        if (spi_dev) {
>>> +            SSIBus *spi_bus;
>>> +
>>> +            spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev),
>>> "spi");
>>
>> This looks odd. You should just be able to do
>>   spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), "spi1");
>> without using object_resolve_path_component() to try to find an
>> SPI device object, because your SoC device should have alias properties
>> which provide access to its SPI subcomponents' SPI buses.
>> See hw/arm/xlnx-ep108.c for an example of the board code for this and
>> hw/arm/xlnx-zynqmp.c for the SoC code which calls
>> object_property_add_alias() to set up the aliases.
>
>
> I certainly could do as you proposed.
>
> The problem is that I also need the spi_dev device for the
> sysbus_connect_irq() call below.
>
> My spi_dev is referenced as "spi1" in the i.MX6 doc and I added a 'spi1"
> property for it in the i.MX6 soc.
>
> Once I have the spi_dev device it is trivial to retrieve the spi_bus
> attached to it.
>
> So, yes this is not in line with what is done in xlnx-zynqmp.c but the need
> is a bit different.

I think the SoC should probably have an externally-facing IRQ line
which it wires up internally to the SPI's IRQ line.
(This corresponds basically to what happens in h/w -- the SoC's
interfaces are defined by it even though many of them may be
directly wired up to some internal component it has, but from
outside the SoC you don't get access to the whole of the internal
component.)

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller
  2016-03-10 19:26     ` Jean-Christophe DUBOIS
@ 2016-03-11  0:01       ` Peter Maydell
  0 siblings, 0 replies; 30+ messages in thread
From: Peter Maydell @ 2016-03-11  0:01 UTC (permalink / raw)
  To: Jean-Christophe DUBOIS; +Cc: QEMU Developers, Peter Crosthwaite

On 11 March 2016 at 02:26, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Le 10/03/2016 11:31, Peter Maydell a écrit :
>> Calling qemu_set_irq() from a reset function is generally
>> a bad idea.
>
>
> Could I assume that all irq lines are set automatically to 0 on reset?

The way this works is that an irq line has two ends on it,
and no internal state of its own. So on reset, the devices
at each end reset themselves. For the device on the receiving
end of the irq line, it will reset itself into the "assume
this input of mine is at 0" state.

(The reason not to call qemu_set_irq() on reset from the
sending end is that there's no guarantee about which order
the two devices reset themselves -- if you reset before
the receiving end then your call would do nothing anyway
because the subsequent reset in the receiver resets its state
and it forgets about the inbound signal being high.)

This is of course broken for the case where a device wants
to assert a line to 1 when it resets, but we've basically
been able to ignore this so far. Trying to clean up our
reset handling would be a huge job and probably not back
compatible either...

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-10 23:57       ` Peter Maydell
@ 2016-03-15 21:40         ` Jean-Christophe DUBOIS
  2016-03-16  6:32           ` Peter Maydell
  0 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-15 21:40 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Le 11/03/2016 00:57, Peter Maydell a écrit :
> On 11 March 2016 at 02:24, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
>> Le 10/03/2016 11:38, Peter Maydell a écrit :
>>> On 2 March 2016 at 05:27, 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");
>>>> +        if (spi_dev) {
>>>> +            SSIBus *spi_bus;
>>>> +
>>>> +            spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev),
>>>> "spi");
>>> This looks odd. You should just be able to do
>>>    spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), "spi1");
>>> without using object_resolve_path_component() to try to find an
>>> SPI device object, because your SoC device should have alias properties
>>> which provide access to its SPI subcomponents' SPI buses.
>>> See hw/arm/xlnx-ep108.c for an example of the board code for this and
>>> hw/arm/xlnx-zynqmp.c for the SoC code which calls
>>> object_property_add_alias() to set up the aliases.
>>
>> I certainly could do as you proposed.
>>
>> The problem is that I also need the spi_dev device for the
>> sysbus_connect_irq() call below.
>>
>> My spi_dev is referenced as "spi1" in the i.MX6 doc and I added a 'spi1"
>> property for it in the i.MX6 soc.
>>
>> Once I have the spi_dev device it is trivial to retrieve the spi_bus
>> attached to it.
>>
>> So, yes this is not in line with what is done in xlnx-zynqmp.c but the need
>> is a bit different.
> I think the SoC should probably have an externally-facing IRQ line
> which it wires up internally to the SPI's IRQ line.
> (This corresponds basically to what happens in h/w -- the SoC's
> interfaces are defined by it even though many of them may be
> directly wired up to some internal component it has, but from
> outside the SoC you don't get access to the whole of the internal
> component.)

Well, each SPI controller has 4 possible CS lines to external devices 
and I have 5 SPI controllers.

This makes 20 externally facing IRQ lines (these are output lines) to 
add to the i.MX6 soc object as properties (with a meaningful naming 
convention).

And anyway, what I need to pass to sysbus_connect_irq() is a 
SYS_BUS_DEVICE and not an IRQ line ...

So do you mean I should define the 20 external lines (properties) and 
wire all of them up with sysbus_connect_irq() in the i.MX6 SOC 
implementation (fsl-imx6.c)?

Then How to you "connect" this externally facing line to the selected 
device CS line (here a FLASH memory) in sabrelite?

JC


>
> thanks
> -- PMM
>

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

* Re: [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation.
  2016-03-15 21:40         ` Jean-Christophe DUBOIS
@ 2016-03-16  6:32           ` Peter Maydell
  2016-03-16 11:28             ` Peter Maydell
  0 siblings, 1 reply; 30+ messages in thread
From: Peter Maydell @ 2016-03-16  6:32 UTC (permalink / raw)
  To: Jean-Christophe DUBOIS; +Cc: QEMU Developers, Peter Crosthwaite

On 15 March 2016 at 21:40, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Well, each SPI controller has 4 possible CS lines to external devices and I
> have 5 SPI controllers.
>
> This makes 20 externally facing IRQ lines (these are output lines) to add to
> the i.MX6 soc object as properties (with a meaningful naming convention).
>
> And anyway, what I need to pass to sysbus_connect_irq() is a SYS_BUS_DEVICE
> and not an IRQ line ...

sysbus_connect_irq() is just syntactic sugar for a
qdev_connect_gpio_out_named() call on the sysbus device; in this case
you'd have a bunch of named gpio lines on the SoC device and use
  qdev_connect_gpio_out_named(soc_device, SOC_CS_WHATEVER, 1, cs_line);

> So do you mean I should define the 20 external lines (properties) and wire
> all of them up with sysbus_connect_irq() in the i.MX6 SOC implementation
> (fsl-imx6.c)?

If you use qdev_init_gpio_out_named() in the SPI controller (which
is probably a good idea anyway, chip selects aren't IRQs so using the
gpio functions is clearer naming) then you ought to be able to create
the appropriate aliases, since gpios are link properties. However I
can't quite figure out how this is supposed to work -- Peter C ?

(There's a qdev_pass_gpios() which is almost right but that operates
on all gpios rather than a specified subset.)

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support
  2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
                   ` (11 preceding siblings ...)
  2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
@ 2016-03-16 11:04 ` Peter Maydell
  12 siblings, 0 replies; 30+ messages in thread
From: Peter Maydell @ 2016-03-16 11:04 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: QEMU Developers, Peter Crosthwaite

On 1 March 2016 at 22:27, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> 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.

I've added patches 1-5 and 8 to target-arm.next, since they've been reviewed
already; that will cut down the size of this series for subsequent versions.

thanks
-- PMM

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

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

On 16 March 2016 at 06:32, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 15 March 2016 at 21:40, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
>> So do you mean I should define the 20 external lines (properties) and wire
>> all of them up with sysbus_connect_irq() in the i.MX6 SOC implementation
>> (fsl-imx6.c)?
>
> If you use qdev_init_gpio_out_named() in the SPI controller (which
> is probably a good idea anyway, chip selects aren't IRQs so using the
> gpio functions is clearer naming) then you ought to be able to create
> the appropriate aliases, since gpios are link properties. However I
> can't quite figure out how this is supposed to work -- Peter C ?

On the other hand if there isn't an obvious way to do this I'd be OK
with taking this series with this part of the code the way you have it
now, and we can clean it up later. We're already well into softfreeze
so I'd rather we didn't delay too long...

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions
  2016-03-10 10:14   ` Peter Maydell
@ 2016-03-16 22:19     ` Jean-Christophe DUBOIS
  2016-03-17  8:46       ` Peter Maydell
  0 siblings, 1 reply; 30+ messages in thread
From: Jean-Christophe DUBOIS @ 2016-03-16 22:19 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Peter Crosthwaite

Hi Peter,

I am wondering what is the "correct" (simple?) way in QEMU to put a CPU 
in a particular state (HYP, SVC, MON, ...) after I reset it (using 
cpu_reset()).

For example, if I reset a core that has "has_el3" property it will start 
in EL3 mode.

Now what is the simple way to transition this CPU to EL1 before starting 
running it with target_cpu_class->set_pc(target_cpu_state, entry);

Thanks

JC

Le 10/03/2016 11:14, Peter Maydell a écrit :
> On 2 March 2016 at 05:27, 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
>>
>>   target-arm/Makefile.objs  |   1 +
>>   target-arm/arm-powerctl.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++
>>   target-arm/arm-powerctl.h |  22 +++++++
>>   target-arm/psci.c         |  72 ++-------------------
>>   4 files changed, 184 insertions(+), 67 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..99c2af2
>> --- /dev/null
>> +++ b/target-arm/arm-powerctl.c
>> @@ -0,0 +1,156 @@
>> +/*
>> + * 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 "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: Resquesting unknown CPU %" PRId64 "\n",
>> +                  __func__, id);
> "Requesting"
>
>> +
>> +    return NULL;
>> +}
>> +
>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id)
>> +{
>> +    CPUState *target_cpu_state;
>> +    ARMCPU *target_cpu;
>> +    CPUClass *target_cpu_class;
>> +    CPUARMState *env = &ARM_CPU(current_cpu)->env;
>> +
>> +    DPRINTF("cpu %" PRId64 " @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n",
>> +            cpuid, entry, context_id);
>> +
>> +    /* change to the cpu we are powering up */
>> +    target_cpu_state = arm_get_cpu_by_id(cpuid);
>> +    if (!target_cpu_state) {
>> +        return -1;
>> +    }
>> +    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 -1;
>> +    }
>> +    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.
> This is a generic function so it's a bit out of place for it to
> talk about PSCI spec details. What are the semantics provided by
> this helper function?
>
>> +     *
>> +     * 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
> It's no longer true that we only implement EL1, incidentally.
>
>> +     * (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) {
>> +            return -1;
>> +        }
>> +        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);
>> +
>> +    return 0;
>> +}
>> +
>> +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 -1;
>> +    }
>> +    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);
> Whether requesting a power-off of a powered off CPU
> is a guest error depends on the API used to request
> it, so you can't log this as a guest error in the
> generic helper function.
>
>> +        return -1;
>> +    }
>> +
>> +    /* Reset the cpu */
>> +    cpu_reset(target_cpu_state);
>> +
>> +    return 0;
>> +}
>> diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h
>> new file mode 100644
>> index 0000000..13fb526
>> --- /dev/null
>> +++ b/target-arm/arm-powerctl.h
>> @@ -0,0 +1,22 @@
>> +/*
>> + * 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
>> +
>> +CPUState *arm_get_cpu_by_id(uint64_t id);
>> +
>> +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id);
>> +
>> +void arm_set_cpu_off(uint64_t cpuid);
>> +
>> +int arm_reset_cpu(uint64_t cpuid);
> Please provide doc comments for new globally-visible functions.
>
>> +
>> +#endif
>> diff --git a/target-arm/psci.c b/target-arm/psci.c
>> index c55487f..362105f 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;
>> @@ -168,51 +152,8 @@ void arm_handle_psci_call(ARMCPU *cpu)
>>           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;
>> -        }
> You've lost the distinction between these two failure
> modes -- the PSCI error codes are returned to the guest
> and are mandated by the PSCI spec so you need to distinguish
> them.
>
>
>> -        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) ?
>> +                             QEMU_PSCI_RET_INVALID_PARAMS : 0;
>>           break;
>>       case QEMU_PSCI_0_1_FN_CPU_OFF:
>>       case QEMU_PSCI_0_2_FN_CPU_OFF:
>> @@ -250,9 +191,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] 30+ messages in thread

* Re: [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions
  2016-03-16 22:19     ` Jean-Christophe DUBOIS
@ 2016-03-17  8:46       ` Peter Maydell
  0 siblings, 0 replies; 30+ messages in thread
From: Peter Maydell @ 2016-03-17  8:46 UTC (permalink / raw)
  To: Jean-Christophe DUBOIS; +Cc: QEMU Developers, Peter Crosthwaite

On 16 March 2016 at 22:19, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote:
> Hi Peter,
>
> I am wondering what is the "correct" (simple?) way in QEMU to put a CPU in a
> particular state (HYP, SVC, MON, ...) after I reset it (using cpu_reset()).
>
> For example, if I reset a core that has "has_el3" property it will start in
> EL3 mode.

If it's AArch64, yes. If it's AArch32 then it will start in Secure-SVC
(which is EL3, but the distinction is that if you wanted NS-SVC then
what you need to flip is the NS bit.)

> Now what is the simple way to transition this CPU to EL1 before starting
> running it with target_cpu_class->set_pc(target_cpu_state, entry);

There is no convenient API provided for this -- if you look at boot.c
it just directly tweaks registers. We might want to abstract this out
a bit better.

thanks
-- PMM

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

end of thread, other threads:[~2016-03-17  8:46 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-03-01 22:27 [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 01/12] i.MX: Allow GPT timer to rollover Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 02/12] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 03/12] i.MX: Remove CCM useless clock computation handling Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 04/12] i.MX: Add the CLK_IPG_HIGH clock Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 05/12] i.MX: Add i.MX6 CCM and ANALOG device Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 06/12] ARM: Factor out ARM on/off PSCI control functions Jean-Christophe Dubois
2016-03-10 10:14   ` Peter Maydell
2016-03-16 22:19     ` Jean-Christophe DUBOIS
2016-03-17  8:46       ` Peter Maydell
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 07/12] i.MX: Add i.MX6 System Reset Controller device Jean-Christophe Dubois
2016-03-10 10:20   ` Peter Maydell
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 08/12] i.MX: Add missing descriptions in devices Jean-Christophe Dubois
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 09/12] FIFO: Add a FIFO32 implementation Jean-Christophe Dubois
2016-03-10 10:25   ` Peter Maydell
2016-03-10 19:26     ` Jean-Christophe DUBOIS
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 10/12] i.MX: Add the Freescale SPI Controller Jean-Christophe Dubois
2016-03-10 10:31   ` Peter Maydell
2016-03-10 19:26     ` Jean-Christophe DUBOIS
2016-03-11  0:01       ` Peter Maydell
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 11/12] i.MX: Add i.MX6 SOC implementation Jean-Christophe Dubois
2016-03-10 10:33   ` Peter Maydell
2016-03-01 22:27 ` [Qemu-devel] [PATCH v3 12/12] i.MX: Add sabrelite i.MX6 emulation Jean-Christophe Dubois
2016-03-10 10:38   ` Peter Maydell
2016-03-10 19:24     ` Jean-Christophe DUBOIS
2016-03-10 23:57       ` Peter Maydell
2016-03-15 21:40         ` Jean-Christophe DUBOIS
2016-03-16  6:32           ` Peter Maydell
2016-03-16 11:28             ` Peter Maydell
2016-03-16 11:04 ` [Qemu-devel] [PATCH v3 00/12] Add i.MX6 (Single/Dual/Quad) support Peter Maydell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).