qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver
@ 2015-12-07 22:53 Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 1/5] i.MX: Fix i.MX31 default/reset configuration Jean-Christophe Dubois
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

i.MX25 SOC has a different CCM device than i.MX31.

Qemu i.MX25 emulation was built with i.MX31 CCM driver. This allows
Linux to work on top of the i.MX25 emultion but this is not correct.

Furthermore, other SOC we could emulate like i.MX6 have yet a different
implementation of the CCM device.

So we split the i.MX31 into a generic base class and a specific i.MX31
class.

We then add an i.MX25 specific CCM Device and have the i.MX25 SOC use it.

Jean-Christophe Dubois (5):
  i.MX: Fix i.MX31 default/reset configuration.
  i.MX: rename i.MX CCM get_clock() function and CLK ID enum names
  i.MX: Split the CCM class into an abstact base class and a concrete
    class.
  i.MX: Add an i.MX25 specific CCM class/instance.
  i.MX: move i.MX31 CCM object to register array.

 hw/arm/fsl-imx25.c          |   6 +-
 hw/arm/fsl-imx31.c          |   6 +-
 hw/misc/Makefile.objs       |   2 +
 hw/misc/imx25_ccm.c         | 341 ++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx31_ccm.c         | 374 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx_ccm.c           | 231 +++------------------------
 hw/timer/imx_epit.c         |  20 ++-
 hw/timer/imx_gpt.c          |  16 +-
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/arm/fsl-imx31.h  |   4 +-
 include/hw/misc/imx25_ccm.h |  79 ++++++++++
 include/hw/misc/imx31_ccm.h |  88 +++++++++++
 include/hw/misc/imx_ccm.h   |  75 ++++-----
 include/hw/timer/imx_epit.h |   5 +-
 include/hw/timer/imx_gpt.h  |   5 +-
 15 files changed, 966 insertions(+), 290 deletions(-)
 create mode 100644 hw/misc/imx25_ccm.c
 create mode 100644 hw/misc/imx31_ccm.c
 create mode 100644 include/hw/misc/imx25_ccm.h
 create mode 100644 include/hw/misc/imx31_ccm.h

-- 
2.5.0

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

* [Qemu-devel] [PATCH v6 1/5] i.MX: Fix i.MX31 default/reset configuration.
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
@ 2015-12-07 22:53 ` Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 2/5] i.MX: rename i.MX CCM get_clock() function and CLK ID enum names Jean-Christophe Dubois
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

Linux on i.MX31/KZM is expecting the CCM to use the CKIH ref clock instead
of the CKIL plus the FPM multiplier.

We change the CCMR reg reset value to match linux expected config.

This allow the CCM to provide a 39MHz clk (as expected by linux) instead of
the actual 50MHz.

With this change the "sleep 60" command on linux is time accurate with
"real world time".

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
---
  
Changes since v1:
 * Not present 
 
Changes since v2:
 * Not present 
 
Changes since v3:
 * Not present
 
Changes since v4:
 * Not present
 
Changes since v4:
 * No change

 hw/misc/imx_ccm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c
index 4cc2bbc..500dda5 100644
--- a/hw/misc/imx_ccm.c
+++ b/hw/misc/imx_ccm.c
@@ -123,7 +123,7 @@ static void imx_ccm_reset(DeviceState *dev)
 {
     IMXCCMState *s = IMX_CCM(dev);
 
-    s->ccmr = 0x074b0b7b;
+    s->ccmr = 0x074b0b7d;
     s->pdr0 = 0xff870b48;
     s->pdr1 = 0x49fcfe7f;
     s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
-- 
2.5.0

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

* [Qemu-devel] [PATCH v6 2/5] i.MX: rename i.MX CCM get_clock() function and CLK ID enum names
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 1/5] i.MX: Fix i.MX31 default/reset configuration Jean-Christophe Dubois
@ 2015-12-07 22:53 ` Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 3/5] i.MX: Split the CCM class into an abstact base class and a concrete class Jean-Christophe Dubois
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

This is to prepare for CCM code refactoring.

This is just a bit of function and enum values renaming.

We also remove some useless intermediate variables.

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
---

Changes since v1:
 * Not present 
  
Changes since v2:
 * Not present 
 
Changes since v3:
 * None 
 
Changes since v4:
 * None 

Changes since v5:
 * None 

 hw/misc/imx_ccm.c         |  8 ++++----
 hw/timer/imx_epit.c       | 20 +++++++++-----------
 hw/timer/imx_gpt.c        | 16 ++++++++--------
 include/hw/misc/imx_ccm.h |  8 ++++----
 4 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c
index 500dda5..1ac697a 100644
--- a/hw/misc/imx_ccm.c
+++ b/hw/misc/imx_ccm.c
@@ -49,18 +49,18 @@ static const VMStateDescription vmstate_imx_ccm = {
     .post_load = imx_ccm_post_load,
 };
 
-uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
+uint32_t imx_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
 {
     IMXCCMState *s = IMX_CCM(dev);
 
     switch (clock) {
     case NOCLK:
         return 0;
-    case MCU:
+    case CLK_MCU:
         return s->mcu_clk_freq;
-    case HSP:
+    case CLK_HSP:
         return s->hsp_clk_freq;
-    case IPG:
+    case CLK_IPG:
         return s->ipg_clk_freq;
     case CLK_32k:
         return CKIL_FREQ;
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 967be4a..50bf83c 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -51,9 +51,9 @@ static char const *imx_epit_reg_name(uint32_t reg)
  * These are typical.
  */
 static const IMXClk imx_epit_clocks[] =  {
-    0,        /* 00 disabled */
-    IPG,      /* 01 ipg_clk, ~532MHz */
-    IPG,      /* 10 ipg_clk_highfreq */
+    NOCLK,    /* 00 disabled */
+    CLK_IPG,  /* 01 ipg_clk, ~532MHz */
+    CLK_IPG,  /* 10 ipg_clk_highfreq */
     CLK_32k,  /* 11 ipg_clk_32k -- ~32kHz */
 };
 
@@ -73,20 +73,18 @@ static void imx_epit_set_freq(IMXEPITState *s)
 {
     uint32_t clksrc;
     uint32_t prescaler;
-    uint32_t freq;
 
     clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
     prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
 
-    freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler;
+    s->freq = imx_ccm_get_clock_frequency(s->ccm,
+                                imx_epit_clocks[clksrc]) / prescaler;
 
-    s->freq = freq;
+    DPRINTF("Setting ptimer frequency to %u\n", s->freq);
 
-    DPRINTF("Setting ptimer frequency to %u\n", freq);
-
-    if (freq) {
-        ptimer_set_freq(s->timer_reload, freq);
-        ptimer_set_freq(s->timer_cmp, freq);
+    if (s->freq) {
+        ptimer_set_freq(s->timer_reload, s->freq);
+        ptimer_set_freq(s->timer_cmp, s->freq);
     }
 }
 
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 7257f42..b1893b8 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,8 +81,8 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
 
 static const IMXClk imx_gpt_clocks[] = {
     NOCLK,    /* 000 No clock source */
-    IPG,      /* 001 ipg_clk, 532MHz*/
-    IPG,      /* 010 ipg_clk_highfreq */
+    CLK_IPG,  /* 001 ipg_clk, 532MHz*/
+    CLK_IPG,  /* 010 ipg_clk_highfreq */
     NOCLK,    /* 011 not defined */
     CLK_32k,  /* 100 ipg_clk_32k */
     NOCLK,    /* 101 not defined */
@@ -93,14 +93,14 @@ static const IMXClk imx_gpt_clocks[] = {
 static void imx_gpt_set_freq(IMXGPTState *s)
 {
     uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
-    uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
-                    / (1 + s->pr);
-    s->freq = freq;
 
-    DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
+    s->freq = imx_ccm_get_clock_frequency(s->ccm,
+                                imx_gpt_clocks[clksrc]) / (1 + s->pr);
 
-    if (freq) {
-        ptimer_set_freq(s->timer, freq);
+    DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq);
+
+    if (s->freq) {
+        ptimer_set_freq(s->timer, s->freq);
     }
 }
 
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 0f2e469..09f6248 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -80,12 +80,12 @@ typedef struct IMXCCMState {
 
 typedef enum  {
     NOCLK,
-    MCU,
-    HSP,
-    IPG,
+    CLK_MCU,
+    CLK_HSP,
+    CLK_IPG,
     CLK_32k
 } IMXClk;
 
-uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock);
+uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock);
 
 #endif /* IMX_CCM_H */
-- 
2.5.0

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

* [Qemu-devel] [PATCH v6 3/5] i.MX: Split the CCM class into an abstact base class and a concrete class.
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 1/5] i.MX: Fix i.MX31 default/reset configuration Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 2/5] i.MX: rename i.MX CCM get_clock() function and CLK ID enum names Jean-Christophe Dubois
@ 2015-12-07 22:53 ` Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 4/5] i.MX: Add an i.MX25 specific CCM class/instance Jean-Christophe Dubois
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

The IMX_CCM class is now the base abstract class that is used by EPIT and GPT
timer implementation.

IMX31_CCM class is the concrete class implementing CCM for i.MX31 SOC.

For now the i.MX25 continues to use the i.MX31 CCM implementation.

An i.MX25 specific CCM will be introduced in a later patch.

We also rework initialization to stop using deprecated sysbus device init

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
---

Changes since v1:
 * None 
 
Changes since v2:
 * We moved to an inheritance QOM scheme
 
Changes since v3:
 * Rework logging based on comment on i.MX25 CCM patch.
 * Change abstract class function parameter to abstract class type instead
   of DEVICE type.
 * EPIT and GPT timers use abstract class instead of DEVICE class.

Changes sinve v4:
 * improve debug logging.
 * make IPG clk dependent on HCLK clk.

Changes since v5:
 * None

 hw/arm/fsl-imx25.c          |   6 +-
 hw/arm/fsl-imx31.c          |   6 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx31_ccm.c         | 392 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx_ccm.c           | 231 +++-----------------------
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/arm/fsl-imx31.h  |   4 +-
 include/hw/misc/imx31_ccm.h |  66 ++++++++
 include/hw/misc/imx_ccm.h   |  69 +++-----
 include/hw/timer/imx_epit.h |   5 +-
 include/hw/timer/imx_gpt.h  |   5 +-
 11 files changed, 521 insertions(+), 268 deletions(-)
 create mode 100644 hw/misc/imx31_ccm.c
 create mode 100644 include/hw/misc/imx31_ccm.h

diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index e1cadac..9f302ed 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj)
     object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
     qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
 
-    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
     qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
 
     for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
@@ -150,7 +150,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
             { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
         };
 
-        s->gpt[i].ccm = DEVICE(&s->ccm);
+        s->gpt[i].ccm = IMX_CCM(&s->ccm);
 
         object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
         if (err) {
@@ -173,7 +173,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
             { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
         };
 
-        s->epit[i].ccm = DEVICE(&s->ccm);
+        s->epit[i].ccm = IMX_CCM(&s->ccm);
 
         object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
         if (err) {
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index 53d4473..abdea06 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -35,7 +35,7 @@ static void fsl_imx31_init(Object *obj)
     object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
     qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
 
-    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
     qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
 
     for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
@@ -128,7 +128,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
                                             serial_table[i].irq));
     }
 
-    s->gpt.ccm = DEVICE(&s->ccm);
+    s->gpt.ccm = IMX_CCM(&s->ccm);
 
     object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
     if (err) {
@@ -150,7 +150,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
             { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
         };
 
-        s->epit[i].ccm = DEVICE(&s->ccm);
+        s->epit[i].ccm = IMX_CCM(&s->ccm);
 
         object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
         if (err) {
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index aeb6b7d..c77f3e3 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -26,6 +26,7 @@ obj-$(CONFIG_NSERIES) += cbus.o
 obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
 obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
 obj-$(CONFIG_IMX) += imx_ccm.o
+obj-$(CONFIG_IMX) += imx31_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/imx31_ccm.c b/hw/misc/imx31_ccm.c
new file mode 100644
index 0000000..b92d2e0
--- /dev/null
+++ b/hw/misc/imx31_ccm.c
@@ -0,0 +1,392 @@
+/*
+ * IMX31 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.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the i.MX31 CCM.
+ */
+
+#include "hw/misc/imx31_ccm.h"
+
+#define CKIH_FREQ 26000000 /* 26MHz crystal input */
+
+#ifndef DEBUG_IMX31_CCM
+#define DEBUG_IMX31_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_IMX31_CCM) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+static char const *imx31_ccm_reg_name(uint32_t reg)
+{
+    switch (reg) {
+    case 0:
+        return "CCMR";
+    case 1:
+        return "PDR0";
+    case 2:
+        return "PDR1";
+    case 3:
+        return "RCSR";
+    case 4:
+        return "MPCTL";
+    case 5:
+        return "UPCTL";
+    case 6:
+        return "SPCTL";
+    case 7:
+        return "COSR";
+    case 8:
+        return "CGR0";
+    case 9:
+        return "CGR1";
+    case 10:
+        return "CGR2";
+    case 11:
+        return "WIMR";
+    case 12:
+        return "LDC";
+    case 13:
+        return "DCVR0";
+    case 14:
+        return "DCVR1";
+    case 15:
+        return "DCVR2";
+    case 16:
+        return "DCVR3";
+    case 17:
+        return "LTR0";
+    case 18:
+        return "LTR1";
+    case 19:
+        return "LTR2";
+    case 20:
+        return "LTR3";
+    case 21:
+        return "LTBR0";
+    case 22:
+        return "LTBR1";
+    case 23:
+        return "PMCR0";
+    case 24:
+        return "PMCR1";
+    case 25:
+        return "PDR2";
+    default:
+        return "???";
+    }
+}
+
+static const VMStateDescription vmstate_imx31_ccm = {
+    .name = TYPE_IMX31_CCM,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ccmr, IMX31CCMState),
+        VMSTATE_UINT32(pdr0, IMX31CCMState),
+        VMSTATE_UINT32(pdr1, IMX31CCMState),
+        VMSTATE_UINT32(mpctl, IMX31CCMState),
+        VMSTATE_UINT32(spctl, IMX31CCMState),
+        VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
+        VMSTATE_UINT32(pmcr0, IMX31CCMState),
+        VMSTATE_UINT32(pmcr1, IMX31CCMState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
+{
+    uint32_t freq = 0;
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    if ((s->ccmr & CCMR_PRCS) == 2) {
+        if (s->ccmr & CCMR_FPME) {
+            freq = CKIL_FREQ;
+            if (s->ccmr & CCMR_FPMF) {
+                freq *= 1024;
+            }
+        } 
+    } else {
+        freq = CKIH_FREQ;
+    }
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+        freq = imx31_ccm_get_pll_ref_clk(dev);
+    } else {
+        freq = imx31_ccm_get_mpll_clk(dev);
+    }
+
+    DPRINTF("freq = %d\n", freq);
+
+    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->pdr0, 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->pdr0, HSP));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+    uint32_t freq = 0;
+
+    switch (clock) {
+    case NOCLK:
+        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;
+    case CLK_32k:
+        freq = CKIL_FREQ;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+                      TYPE_IMX31_CCM, __func__, clock);
+        break;
+    }
+
+    DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+    return freq;
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    s->ccmr   = 0x074b0b7d;
+    s->pdr0   = 0xff870b48;
+    s->pdr1   = 0x49fcfe7f;
+    s->mpctl  = 0x04001800;
+    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
+    s->spctl  = 0x04043001;
+    s->pmcr0  = 0x80209828;
+    s->pmcr1  = 0x00aa0000;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32 value = 0;
+    IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+    switch (offset >> 2) {
+    case 0: /* CCMR */
+        value = s->ccmr;
+        break;
+    case 1:
+        value = s->pdr0;
+        break;
+    case 2:
+        value = s->pdr1;
+        break;
+    case 4:
+        value = s->mpctl;
+        break;
+    case 6:
+        value = s->spctl;
+        break;
+    case 8:
+        value = s->cgr[0];
+        break;
+    case 9:
+        value = s->cgr[1];
+        break;
+    case 10:
+        value = s->cgr[2];
+        break;
+    case 18: /* LTR1 */
+        value = 0x00004040;
+        break;
+    case 23:
+        value = s->pmcr0;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+        break;
+    }
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+            value);
+
+    return (uint64_t)value;
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+            (uint32_t)value);
+
+    switch (offset >> 2) {
+    case 0:
+        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
+        break;
+    case 1:
+        s->pdr0 = value & 0xff9f3fff;
+        break;
+    case 2:
+        s->pdr1 = value;
+        break;
+    case 4:
+        s->mpctl = value & 0xbfff3fff;
+        break;
+    case 6:
+        s->spctl = value & 0xbfff3fff;
+        break;
+    case 8:
+        s->cgr[0] = value;
+        break;
+    case 9:
+        s->cgr[1] = value;
+        break;
+    case 10:
+        s->cgr[2] = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+        break;
+    }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+    .read = imx31_ccm_read,
+    .write = imx31_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 void imx31_ccm_init(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+    IMX31CCMState *s = IMX31_CCM(obj);
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
+                          TYPE_IMX31_CCM, 0x1000);
+    sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx31_ccm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc  = DEVICE_CLASS(klass);
+    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+    dc->reset = imx31_ccm_reset;
+    dc->vmsd  = &vmstate_imx31_ccm;
+    dc->desc  = "i.MX31 Clock Control Module";
+
+    ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx31_ccm_info = {
+    .name          = TYPE_IMX31_CCM,
+    .parent        = TYPE_IMX_CCM,
+    .instance_size = sizeof(IMX31CCMState),
+    .instance_init = imx31_ccm_init,
+    .class_init    = imx31_ccm_class_init,
+};
+
+static void imx31_ccm_register_types(void)
+{
+    type_register_static(&imx31_ccm_info);
+}
+
+type_init(imx31_ccm_register_types)
diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c
index 1ac697a..0c9740b 100644
--- a/hw/misc/imx_ccm.c
+++ b/hw/misc/imx_ccm.c
@@ -7,15 +7,12 @@
  * 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.
+ * This is an abstract base class used to get a common interface to
+ * retrieve the CCM frequencies from the various i.MX SOC.
  */
 
 #include "hw/misc/imx_ccm.h"
 
-#define CKIH_FREQ 26000000 /* 26MHz crystal input */
-#define CKIL_FREQ    32768 /* nominal 32khz clock */
-
 #ifndef DEBUG_IMX_CCM
 #define DEBUG_IMX_CCM 0
 #endif
@@ -28,51 +25,27 @@
         } \
     } while (0)
 
-static int imx_ccm_post_load(void *opaque, int version_id);
-
-static const VMStateDescription vmstate_imx_ccm = {
-    .name = TYPE_IMX_CCM,
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(ccmr, IMXCCMState),
-        VMSTATE_UINT32(pdr0, IMXCCMState),
-        VMSTATE_UINT32(pdr1, IMXCCMState),
-        VMSTATE_UINT32(mpctl, IMXCCMState),
-        VMSTATE_UINT32(spctl, IMXCCMState),
-        VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
-        VMSTATE_UINT32(pmcr0, IMXCCMState),
-        VMSTATE_UINT32(pmcr1, IMXCCMState),
-        VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
-        VMSTATE_END_OF_LIST()
-    },
-    .post_load = imx_ccm_post_load,
-};
 
-uint32_t imx_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
 {
-    IMXCCMState *s = IMX_CCM(dev);
+    uint32_t freq = 0;
+    IMXCCMClass *klass = IMX_GET_CLASS(dev);
 
-    switch (clock) {
-    case NOCLK:
-        return 0;
-    case CLK_MCU:
-        return s->mcu_clk_freq;
-    case CLK_HSP:
-        return s->hsp_clk_freq;
-    case CLK_IPG:
-        return s->ipg_clk_freq;
-    case CLK_32k:
-        return CKIL_FREQ;
+    if (klass->get_clock_frequency) {
+        freq = klass->get_clock_frequency(dev, clock);
     }
-    return 0;
+
+    DPRINTF("(clock = %d) = %d\n", clock, freq);
+
+    return freq;
 }
 
 /*
  * Calculate PLL output frequency
  */
-static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
 {
+    int32_t freq;
     int32_t mfn = MFN(pllreg);  /* Numerator */
     uint32_t mfi = MFI(pllreg); /* Integer part */
     uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
@@ -81,186 +54,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
     if (mfi < 5) {
         mfi = 5;
     }
+
     /* mfn is 10-bit signed twos-complement */
     mfn <<= 32 - 10;
     mfn >>= 32 - 10;
 
-    return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
+    freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
             (mfd * pd)) << 10;
-}
-
-static void update_clocks(IMXCCMState *s)
-{
-    /*
-     * If we ever emulate more clocks, this should switch to a data-driven
-     * approach
-     */
-
-    if ((s->ccmr & CCMR_PRCS) == 2) {
-        s->pll_refclk_freq = CKIL_FREQ * 1024;
-    } else {
-        s->pll_refclk_freq = CKIH_FREQ;
-    }
-
-    /* ipg_clk_arm aka MCU clock */
-    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
-        s->mcu_clk_freq = s->pll_refclk_freq;
-    } else {
-        s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
-    }
-
-    /* High-speed clock */
-    s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
-    s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
-
-    DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
-            s->mcu_clk_freq / 1000000,
-            s->hsp_clk_freq / 1000000,
-            s->ipg_clk_freq);
-}
-
-static void imx_ccm_reset(DeviceState *dev)
-{
-    IMXCCMState *s = IMX_CCM(dev);
-
-    s->ccmr = 0x074b0b7d;
-    s->pdr0 = 0xff870b48;
-    s->pdr1 = 0x49fcfe7f;
-    s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
-    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
-    s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
-    s->pmcr0 = 0x80209828;
-
-    update_clocks(s);
-}
-
-static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
-                                unsigned size)
-{
-    IMXCCMState *s = (IMXCCMState *)opaque;
 
-    DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
-
-    switch (offset >> 2) {
-    case 0: /* CCMR */
-        DPRINTF(" ccmr = 0x%x\n", s->ccmr);
-        return s->ccmr;
-    case 1:
-        DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
-        return s->pdr0;
-    case 2:
-        DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
-        return s->pdr1;
-    case 4:
-        DPRINTF(" mpctl = 0x%x\n", s->mpctl);
-        return s->mpctl;
-    case 6:
-        DPRINTF(" spctl = 0x%x\n", s->spctl);
-        return s->spctl;
-    case 8:
-        DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
-        return s->cgr[0];
-    case 9:
-        DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
-        return s->cgr[1];
-    case 10:
-        DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
-        return s->cgr[2];
-    case 18: /* LTR1 */
-        return 0x00004040;
-    case 23:
-        DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
-        return s->pmcr0;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
-                      HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
-        return 0;
-    }
-}
-
-static void imx_ccm_write(void *opaque, hwaddr offset,
-                          uint64_t value, unsigned size)
-{
-    IMXCCMState *s = (IMXCCMState *)opaque;
-
-    DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
-            offset, (unsigned int)value);
-
-    switch (offset >> 2) {
-    case 0:
-        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
-        break;
-    case 1:
-        s->pdr0 = value & 0xff9f3fff;
-        break;
-    case 2:
-        s->pdr1 = value;
-        break;
-    case 4:
-        s->mpctl = value & 0xbfff3fff;
-        break;
-    case 6:
-        s->spctl = value & 0xbfff3fff;
-        break;
-    case 8:
-        s->cgr[0] = value;
-        return;
-    case 9:
-        s->cgr[1] = value;
-        return;
-    case 10:
-        s->cgr[2] = value;
-        return;
-
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
-                      HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
-        return;
-    }
-    update_clocks(s);
-}
-
-static const struct MemoryRegionOps imx_ccm_ops = {
-    .read = imx_ccm_read,
-    .write = imx_ccm_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int imx_ccm_init(SysBusDevice *dev)
-{
-    IMXCCMState *s = IMX_CCM(dev);
-
-    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
-                          TYPE_IMX_CCM, 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static int imx_ccm_post_load(void *opaque, int version_id)
-{
-    IMXCCMState *s = (IMXCCMState *)opaque;
-
-    update_clocks(s);
-    return 0;
-}
-
-static void imx_ccm_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+    DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
+            freq);
 
-    sbc->init = imx_ccm_init;
-    dc->reset = imx_ccm_reset;
-    dc->vmsd = &vmstate_imx_ccm;
-    dc->desc = "i.MX Clock Control Module";
+    return freq;
 }
 
 static const TypeInfo imx_ccm_info = {
-    .name = TYPE_IMX_CCM,
-    .parent = TYPE_SYS_BUS_DEVICE,
+    .name          = TYPE_IMX_CCM,
+    .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(IMXCCMState),
-    .class_init = imx_ccm_class_init,
+    .class_size    = sizeof(IMXCCMClass),
+    .abstract      = true,
 };
 
 static void imx_ccm_register_types(void)
diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h
index 73f50c6..5c62fde 100644
--- a/include/hw/arm/fsl-imx25.h
+++ b/include/hw/arm/fsl-imx25.h
@@ -19,7 +19,7 @@
 
 #include "hw/arm/arm.h"
 #include "hw/intc/imx_avic.h"
-#include "hw/misc/imx_ccm.h"
+#include "hw/misc/imx31_ccm.h"
 #include "hw/char/imx_serial.h"
 #include "hw/timer/imx_gpt.h"
 #include "hw/timer/imx_epit.h"
@@ -44,7 +44,7 @@ typedef struct FslIMX25State {
     /*< public >*/
     ARMCPU         cpu;
     IMXAVICState   avic;
-    IMXCCMState    ccm;
+    IMX31CCMState  ccm;
     IMXSerialState uart[FSL_IMX25_NUM_UARTS];
     IMXGPTState    gpt[FSL_IMX25_NUM_GPTS];
     IMXEPITState   epit[FSL_IMX25_NUM_EPITS];
diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h
index 5e8f795..d408abb 100644
--- a/include/hw/arm/fsl-imx31.h
+++ b/include/hw/arm/fsl-imx31.h
@@ -19,7 +19,7 @@
 
 #include "hw/arm/arm.h"
 #include "hw/intc/imx_avic.h"
-#include "hw/misc/imx_ccm.h"
+#include "hw/misc/imx31_ccm.h"
 #include "hw/char/imx_serial.h"
 #include "hw/timer/imx_gpt.h"
 #include "hw/timer/imx_epit.h"
@@ -42,7 +42,7 @@ typedef struct FslIMX31State {
     /*< public >*/
     ARMCPU         cpu;
     IMXAVICState   avic;
-    IMXCCMState    ccm;
+    IMX31CCMState  ccm;
     IMXSerialState uart[FSL_IMX31_NUM_UARTS];
     IMXGPTState    gpt;
     IMXEPITState   epit[FSL_IMX31_NUM_EPITS];
diff --git a/include/hw/misc/imx31_ccm.h b/include/hw/misc/imx31_ccm.h
new file mode 100644
index 0000000..fcae36d
--- /dev/null
+++ b/include/hw/misc/imx31_ccm.h
@@ -0,0 +1,66 @@
+/*
+ * IMX31 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 IMX31_CCM_H
+#define IMX31_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+
+/* CCMR */
+#define CCMR_FPME    (1<<0)
+#define CCMR_MPE     (1<<3)
+#define CCMR_MDS     (1<<7)
+#define CCMR_FPMF    (1<<26)
+#define CCMR_PRCS    (3<<1)
+
+#define PMCR0_DFSUP1 (1<<31)
+
+/* PDR0 */
+#define PDR0_MCU_PODF_SHIFT (0)
+#define PDR0_MCU_PODF_MASK (0x7)
+#define PDR0_MAX_PODF_SHIFT (3)
+#define PDR0_MAX_PODF_MASK (0x7)
+#define PDR0_IPG_PODF_SHIFT (6)
+#define PDR0_IPG_PODF_MASK (0x3)
+#define PDR0_NFC_PODF_SHIFT (8)
+#define PDR0_NFC_PODF_MASK (0x7)
+#define PDR0_HSP_PODF_SHIFT (11)
+#define PDR0_HSP_PODF_MASK (0x7)
+#define PDR0_PER_PODF_SHIFT (16)
+#define PDR0_PER_PODF_MASK (0x1f)
+#define PDR0_CSI_PODF_SHIFT (23)
+#define PDR0_CSI_PODF_MASK (0x1ff)
+
+#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
+                              & PDR0_##name##_PODF_MASK)
+#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
+                             PDR0_##name##_PODF_SHIFT)
+
+#define TYPE_IMX31_CCM "imx31.ccm"
+#define IMX31_CCM(obj) OBJECT_CHECK(IMX31CCMState, (obj), TYPE_IMX31_CCM)
+
+typedef struct IMX31CCMState {
+    /* <private> */
+    IMXCCMState parent_obj;
+
+    /* <public> */
+    MemoryRegion iomem;
+
+    uint32_t ccmr;
+    uint32_t pdr0;
+    uint32_t pdr1;
+    uint32_t mpctl;
+    uint32_t spctl;
+    uint32_t cgr[3];
+    uint32_t pmcr0;
+    uint32_t pmcr1;
+} IMX31CCMState;
+
+#endif /* IMX31_CCM_H */
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 09f6248..5c4b795 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -1,5 +1,5 @@
 /*
- * IMX31 Clock Control Module
+ * IMX Clock Control Module base class
  *
  * Copyright (C) 2012 NICTA
  * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
@@ -13,33 +13,7 @@
 
 #include "hw/sysbus.h"
 
-/* CCMR */
-#define CCMR_FPME (1<<0)
-#define CCMR_MPE  (1<<3)
-#define CCMR_MDS  (1<<7)
-#define CCMR_FPMF (1<<26)
-#define CCMR_PRCS (3<<1)
-
-/* PDR0 */
-#define PDR0_MCU_PODF_SHIFT (0)
-#define PDR0_MCU_PODF_MASK (0x7)
-#define PDR0_MAX_PODF_SHIFT (3)
-#define PDR0_MAX_PODF_MASK (0x7)
-#define PDR0_IPG_PODF_SHIFT (6)
-#define PDR0_IPG_PODF_MASK (0x3)
-#define PDR0_NFC_PODF_SHIFT (8)
-#define PDR0_NFC_PODF_MASK (0x7)
-#define PDR0_HSP_PODF_SHIFT (11)
-#define PDR0_HSP_PODF_MASK (0x7)
-#define PDR0_PER_PODF_SHIFT (16)
-#define PDR0_PER_PODF_MASK (0x1f)
-#define PDR0_CSI_PODF_SHIFT (23)
-#define PDR0_CSI_PODF_MASK (0x1ff)
-
-#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
-                              & PDR0_##name##_PODF_MASK)
-#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
-                             PDR0_##name##_PODF_SHIFT)
+#define CKIL_FREQ 32768 /* nominal 32khz clock */
 
 /* PLL control registers */
 #define PD(v) (((v) >> 26) & 0xf)
@@ -53,39 +27,44 @@
 #define PLL_MFN(x)              (((x) & 0x3ff) << 0)
 
 #define TYPE_IMX_CCM "imx.ccm"
-#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
+#define IMX_CCM(obj) \
+     OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
+#define IMX_CCM_CLASS(klass) \
+     OBJECT_CLASS_CHECK(IMXCCMClass, (klass), TYPE_IMX_CCM)
+#define IMX_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(IMXCCMClass, (obj), TYPE_IMX_CCM)
 
 typedef struct IMXCCMState {
     /* <private> */
     SysBusDevice parent_obj;
 
     /* <public> */
-    MemoryRegion iomem;
 
-    uint32_t ccmr;
-    uint32_t pdr0;
-    uint32_t pdr1;
-    uint32_t mpctl;
-    uint32_t spctl;
-    uint32_t cgr[3];
-    uint32_t pmcr0;
-    uint32_t pmcr1;
-
-    /* Frequencies precalculated on register changes */
-    uint32_t pll_refclk_freq;
-    uint32_t mcu_clk_freq;
-    uint32_t hsp_clk_freq;
-    uint32_t ipg_clk_freq;
 } IMXCCMState;
 
 typedef enum  {
     NOCLK,
+    CLK_MPLL,
+    CLK_UPLL,
     CLK_MCU,
     CLK_HSP,
+    CLK_MAX,
+    CLK_AHB,
     CLK_IPG,
+    CLK_PER,
     CLK_32k
 } IMXClk;
 
-uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock);
+typedef struct IMXCCMClass {
+    /* <private> */
+    SysBusDeviceClass parent_class;
+
+    /* <public> */
+    uint32_t (*get_clock_frequency)(IMXCCMState *s, IMXClk clk);
+} IMXCCMClass;
+
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq);
+
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *s, IMXClk clock);
 
 #endif /* IMX_CCM_H */
diff --git a/include/hw/timer/imx_epit.h b/include/hw/timer/imx_epit.h
index c5328ae..0730ac3 100644
--- a/include/hw/timer/imx_epit.h
+++ b/include/hw/timer/imx_epit.h
@@ -31,6 +31,7 @@
 
 #include "hw/sysbus.h"
 #include "hw/ptimer.h"
+#include "hw/misc/imx_ccm.h"
 
 /*
  * EPIT: Enhanced periodic interrupt timer
@@ -63,8 +64,8 @@ typedef struct IMXEPITState{
     /*< public >*/
     ptimer_state *timer_reload;
     ptimer_state *timer_cmp;
-    MemoryRegion iomem;
-    DeviceState *ccm;
+    MemoryRegion  iomem;
+    IMXCCMState  *ccm;
 
     uint32_t cr;
     uint32_t sr;
diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h
index 3f02d3b..461adbe 100644
--- a/include/hw/timer/imx_gpt.h
+++ b/include/hw/timer/imx_gpt.h
@@ -31,6 +31,7 @@
 
 #include "hw/sysbus.h"
 #include "hw/ptimer.h"
+#include "hw/misc/imx_ccm.h"
 
 /*
  * GPT : General purpose timer
@@ -82,8 +83,8 @@ typedef struct IMXGPTState{
 
     /*< public >*/
     ptimer_state *timer;
-    MemoryRegion iomem;
-    DeviceState *ccm;
+    MemoryRegion  iomem;
+    IMXCCMState  *ccm;
 
     uint32_t cr;
     uint32_t pr;
-- 
2.5.0

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

* [Qemu-devel] [PATCH v6 4/5] i.MX: Add an i.MX25 specific CCM class/instance.
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
                   ` (2 preceding siblings ...)
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 3/5] i.MX: Split the CCM class into an abstact base class and a concrete class Jean-Christophe Dubois
@ 2015-12-07 22:53 ` Jean-Christophe Dubois
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array Jean-Christophe Dubois
  2015-12-17 11:30 ` [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Peter Maydell
  5 siblings, 0 replies; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

With this CCM, i.MX25 timer is accurate with "real world time".

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by Peter Crosthwaite <crosthwaite.peter@gmail.com>
---

Changes since v1:
 * rework loging to match other i.MX drivers

Changes since v2:
 * We moved to an inheritance QOM scheme

Changes since v3:
 * Rework logging based on comments.

Changes since v4:
 * Improve debug logging.

Changes since v5:
 * move object state to uint32 array.

 hw/arm/fsl-imx25.c          |   2 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx25_ccm.c         | 341 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/misc/imx25_ccm.h |  79 ++++++++++
 5 files changed, 424 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/imx25_ccm.c
 create mode 100644 include/hw/misc/imx25_ccm.h

diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 9f302ed..36818ee 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj)
     object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
     qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
 
-    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
+    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
     qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
 
     for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c77f3e3..8a235df 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -27,6 +27,7 @@ obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
 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_MILKYMIST) += milkymist-hpdmc.o
 obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
new file mode 100644
index 0000000..23759ca
--- /dev/null
+++ b/hw/misc/imx25_ccm.c
@@ -0,0 +1,341 @@
+/*
+ * IMX25 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.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "hw/misc/imx25_ccm.h"
+
+#ifndef DEBUG_IMX25_CCM
+#define DEBUG_IMX25_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_IMX25_CCM) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+static char const *imx25_ccm_reg_name(uint32_t reg)
+{
+    static char unknown[20];
+
+    switch (reg) {
+    case IMX25_CCM_MPCTL_REG:
+        return "mpctl";
+    case IMX25_CCM_UPCTL_REG:
+        return "upctl";
+    case IMX25_CCM_CCTL_REG:
+        return "cctl";
+    case IMX25_CCM_CGCR0_REG:
+        return "cgcr0";
+    case IMX25_CCM_CGCR1_REG:
+        return "cgcr1";
+    case IMX25_CCM_CGCR2_REG:
+        return "cgcr2";
+    case IMX25_CCM_PCDR0_REG:
+        return "pcdr0";
+    case IMX25_CCM_PCDR1_REG:
+        return "pcdr1";
+    case IMX25_CCM_PCDR2_REG:
+        return "pcdr2";
+    case IMX25_CCM_PCDR3_REG:
+        return "pcdr3";
+    case IMX25_CCM_RCSR_REG:
+        return "rcsr";
+    case IMX25_CCM_CRDR_REG:
+        return "crdr";
+    case IMX25_CCM_DCVR0_REG:
+        return "dcvr0";
+    case IMX25_CCM_DCVR1_REG:
+        return "dcvr1";
+    case IMX25_CCM_DCVR2_REG:
+        return "dcvr2";
+    case IMX25_CCM_DCVR3_REG:
+        return "dcvr3";
+    case IMX25_CCM_LTR0_REG:
+        return "ltr0";
+    case IMX25_CCM_LTR1_REG:
+        return "ltr1";
+    case IMX25_CCM_LTR2_REG:
+        return "ltr2";
+    case IMX25_CCM_LTR3_REG:
+        return "ltr3";
+    case IMX25_CCM_LTBR0_REG:
+        return "ltbr0";
+    case IMX25_CCM_LTBR1_REG:
+        return "ltbr1";
+    case IMX25_CCM_PMCR0_REG:
+        return "pmcr0";
+    case IMX25_CCM_PMCR1_REG:
+        return "pmcr1";
+    case IMX25_CCM_PMCR2_REG:
+        return "pmcr2";
+    case IMX25_CCM_MCR_REG:
+        return "mcr";
+    case IMX25_CCM_LPIMR0_REG:
+        return "lpimr0";
+    case IMX25_CCM_LPIMR1_REG:
+        return "lpimr1";
+    default:
+        sprintf(unknown, "[%d ?]", reg);
+        return unknown;
+    }
+}
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx25_ccm = {
+    .name = TYPE_IMX25_CCM,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
+        freq = CKIH_FREQ;
+    } else {
+        freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
+    }
+
+    DPRINTF("freq = %d\n", freq);
+
+    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;
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    freq = imx25_ccm_get_mpll_clk(dev);
+
+    if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
+        freq = (freq * 3 / 4);
+    }
+
+    freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    freq = imx25_ccm_get_mcu_clk(dev)
+           / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+    uint32_t freq;
+
+    freq = imx25_ccm_get_ahb_clk(dev) / 2;
+
+    DPRINTF("freq = %d\n", freq);
+
+    return freq;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+    uint32_t freq = 0;
+    DPRINTF("Clock = %d)\n", clock);
+
+    switch (clock) {
+    case NOCLK:
+        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;
+    case CLK_32k:
+        freq = CKIL_FREQ;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+                      TYPE_IMX25_CCM, __func__, clock);
+        break;
+    }
+
+    DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+    return freq;
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("\n");
+
+    memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
+    s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
+    s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
+    /* 
+     * The value below gives:
+     * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz. 
+     */
+    s->reg[IMX25_CCM_CCTL_REG]  = 0xd0030000;
+    s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
+    s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
+    s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
+    s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
+    s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
+    s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
+    s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
+    s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
+    s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
+    s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
+    s->reg[IMX25_CCM_MCR_REG]   = 0x43000000;
+
+    /*
+     * default boot will change the reset values to allow:
+     * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz. 
+     * For some reason, this doesn't work. With the value below, linux
+     * detects a 88 MHz IPG CLK instead of 66,5 MHz.
+    s->reg[IMX25_CCM_CCTL_REG]  = 0x20032000;
+     */
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32 value = 0;
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+    if (offset < 0x70) {
+        value = s->reg[offset >> 2];
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+    }
+
+    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+            value);
+
+    return value;
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+            (uint32_t)value);
+
+    if (offset < 0x70) {
+        /*
+         * We will do a better implementation later. In particular some bits
+         * cannot be written to.
+         */
+        s->reg[offset >> 2] = value;
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+    }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+    .read = imx25_ccm_read,
+    .write = imx25_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 void imx25_ccm_init(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+    IMX25CCMState *s = IMX25_CCM(obj);
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
+                          TYPE_IMX25_CCM, 0x1000);
+    sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx25_ccm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+    dc->reset = imx25_ccm_reset;
+    dc->vmsd = &vmstate_imx25_ccm;
+    dc->desc = "i.MX25 Clock Control Module";
+
+    ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx25_ccm_info = {
+    .name          = TYPE_IMX25_CCM,
+    .parent        = TYPE_IMX_CCM,
+    .instance_size = sizeof(IMX25CCMState),
+    .instance_init = imx25_ccm_init,
+    .class_init    = imx25_ccm_class_init,
+};
+
+static void imx25_ccm_register_types(void)
+{
+    type_register_static(&imx25_ccm_info);
+}
+
+type_init(imx25_ccm_register_types)
diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h
index 5c62fde..d0e8e9d 100644
--- a/include/hw/arm/fsl-imx25.h
+++ b/include/hw/arm/fsl-imx25.h
@@ -19,7 +19,7 @@
 
 #include "hw/arm/arm.h"
 #include "hw/intc/imx_avic.h"
-#include "hw/misc/imx31_ccm.h"
+#include "hw/misc/imx25_ccm.h"
 #include "hw/char/imx_serial.h"
 #include "hw/timer/imx_gpt.h"
 #include "hw/timer/imx_epit.h"
@@ -44,7 +44,7 @@ typedef struct FslIMX25State {
     /*< public >*/
     ARMCPU         cpu;
     IMXAVICState   avic;
-    IMX31CCMState  ccm;
+    IMX25CCMState  ccm;
     IMXSerialState uart[FSL_IMX25_NUM_UARTS];
     IMXGPTState    gpt[FSL_IMX25_NUM_GPTS];
     IMXEPITState   epit[FSL_IMX25_NUM_EPITS];
diff --git a/include/hw/misc/imx25_ccm.h b/include/hw/misc/imx25_ccm.h
new file mode 100644
index 0000000..296321c
--- /dev/null
+++ b/include/hw/misc/imx25_ccm.h
@@ -0,0 +1,79 @@
+/*
+ * IMX25 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 IMX25_CCM_H
+#define IMX25_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+
+#define IMX25_CCM_MPCTL_REG  0
+#define IMX25_CCM_UPCTL_REG  1
+#define IMX25_CCM_CCTL_REG   2
+#define IMX25_CCM_CGCR0_REG  3
+#define IMX25_CCM_CGCR1_REG  4
+#define IMX25_CCM_CGCR2_REG  5
+#define IMX25_CCM_PCDR0_REG  6
+#define IMX25_CCM_PCDR1_REG  7
+#define IMX25_CCM_PCDR2_REG  8
+#define IMX25_CCM_PCDR3_REG  9
+#define IMX25_CCM_RCSR_REG   10
+#define IMX25_CCM_CRDR_REG   11
+#define IMX25_CCM_DCVR0_REG  12
+#define IMX25_CCM_DCVR1_REG  13
+#define IMX25_CCM_DCVR2_REG  14
+#define IMX25_CCM_DCVR3_REG  15
+#define IMX25_CCM_LTR0_REG   16
+#define IMX25_CCM_LTR1_REG   17
+#define IMX25_CCM_LTR2_REG   18
+#define IMX25_CCM_LTR3_REG   19
+#define IMX25_CCM_LTBR0_REG  20
+#define IMX25_CCM_LTBR1_REG  21
+#define IMX25_CCM_PMCR0_REG  22
+#define IMX25_CCM_PMCR1_REG  23
+#define IMX25_CCM_PMCR2_REG  24
+#define IMX25_CCM_MCR_REG    25
+#define IMX25_CCM_LPIMR0_REG 26
+#define IMX25_CCM_LPIMR1_REG 27
+#define IMX25_CCM_MAX_REG    28
+
+/* CCTL */
+#define CCTL_ARM_CLK_DIV_SHIFT (30)
+#define CCTL_ARM_CLK_DIV_MASK  (0x3)
+#define CCTL_AHB_CLK_DIV_SHIFT (28)
+#define CCTL_AHB_CLK_DIV_MASK  (0x3)
+#define CCTL_MPLL_BYPASS_SHIFT (22)
+#define CCTL_MPLL_BYPASS_MASK  (0x1)
+#define CCTL_USB_DIV_SHIFT     (16)
+#define CCTL_USB_DIV_MASK      (0x3F)
+#define CCTL_ARM_SRC_SHIFT     (13)
+#define CCTL_ARM_SRC_MASK      (0x1)
+#define CCTL_UPLL_DIS_SHIFT    (23)
+#define CCTL_UPLL_DIS_MASK     (0x1)
+
+#define EXTRACT(value, name) (((value) >> CCTL_##name##_SHIFT) \
+                              & CCTL_##name##_MASK)
+#define INSERT(value, name) (((value) & CCTL_##name##_MASK) << \
+                             CCTL_##name##_SHIFT)
+
+#define TYPE_IMX25_CCM "imx25.ccm"
+#define IMX25_CCM(obj) OBJECT_CHECK(IMX25CCMState, (obj), TYPE_IMX25_CCM)
+
+typedef struct IMX25CCMState {
+    /* <private> */
+    IMXCCMState parent_obj;
+
+    /* <public> */
+    MemoryRegion iomem;
+
+    uint32_t reg[IMX25_CCM_MAX_REG];
+
+} IMX25CCMState;
+
+#endif /* IMX25_CCM_H */
-- 
2.5.0

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

* [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array.
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
                   ` (3 preceding siblings ...)
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 4/5] i.MX: Add an i.MX25 specific CCM class/instance Jean-Christophe Dubois
@ 2015-12-07 22:53 ` Jean-Christophe Dubois
  2015-12-19 21:27   ` pcrost
  2015-12-17 11:30 ` [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Peter Maydell
  5 siblings, 1 reply; 9+ messages in thread
From: Jean-Christophe Dubois @ 2015-12-07 22:53 UTC (permalink / raw)
  To: qemu-devel, peter.maydell, crosthwaite.peter, qemu-arm
  Cc: Jean-Christophe Dubois

With this i.MX25 and i.MX31 to have close implementation.

Moreover all i.MX31 CCM registers are now present

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

Changes since v1:
 * Not present

Changes since v2:
 * Not present

Changes since v3:
 * Not present

Changes since v4:
 * Not present

Changes since v5:
 * Not present

 hw/misc/imx31_ccm.c         | 188 ++++++++++++++++++++------------------------
 include/hw/misc/imx31_ccm.h |  38 +++++++--
 2 files changed, 115 insertions(+), 111 deletions(-)

diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index b92d2e0..47d6ead 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -29,77 +29,73 @@
 
 static char const *imx31_ccm_reg_name(uint32_t reg)
 {
+    static char unknown[20];
+
     switch (reg) {
-    case 0:
+    case IMX31_CCM_CCMR_REG:
         return "CCMR";
-    case 1:
+    case IMX31_CCM_PDR0_REG:
         return "PDR0";
-    case 2:
+    case IMX31_CCM_PDR1_REG:
         return "PDR1";
-    case 3:
+    case IMX31_CCM_RCSR_REG:
         return "RCSR";
-    case 4:
+    case IMX31_CCM_MPCTL_REG:
         return "MPCTL";
-    case 5:
+    case IMX31_CCM_UPCTL_REG:
         return "UPCTL";
-    case 6:
+    case IMX31_CCM_SPCTL_REG:
         return "SPCTL";
-    case 7:
+    case IMX31_CCM_COSR_REG:
         return "COSR";
-    case 8:
+    case IMX31_CCM_CGR0_REG:
         return "CGR0";
-    case 9:
+    case IMX31_CCM_CGR1_REG:
         return "CGR1";
-    case 10:
+    case IMX31_CCM_CGR2_REG:
         return "CGR2";
-    case 11:
+    case IMX31_CCM_WIMR_REG:
         return "WIMR";
-    case 12:
+    case IMX31_CCM_LDC_REG:
         return "LDC";
-    case 13:
+    case IMX31_CCM_DCVR0_REG:
         return "DCVR0";
-    case 14:
+    case IMX31_CCM_DCVR1_REG:
         return "DCVR1";
-    case 15:
+    case IMX31_CCM_DCVR2_REG:
         return "DCVR2";
-    case 16:
+    case IMX31_CCM_DCVR3_REG:
         return "DCVR3";
-    case 17:
+    case IMX31_CCM_LTR0_REG:
         return "LTR0";
-    case 18:
+    case IMX31_CCM_LTR1_REG:
         return "LTR1";
-    case 19:
+    case IMX31_CCM_LTR2_REG:
         return "LTR2";
-    case 20:
+    case IMX31_CCM_LTR3_REG:
         return "LTR3";
-    case 21:
+    case IMX31_CCM_LTBR0_REG:
         return "LTBR0";
-    case 22:
+    case IMX31_CCM_LTBR1_REG:
         return "LTBR1";
-    case 23:
+    case IMX31_CCM_PMCR0_REG:
         return "PMCR0";
-    case 24:
+    case IMX31_CCM_PMCR1_REG:
         return "PMCR1";
-    case 25:
+    case IMX31_CCM_PDR2_REG:
         return "PDR2";
     default:
-        return "???";
+        sprintf(unknown, "[%d ?]", reg);
+        return unknown;
     }
 }
 
 static const VMStateDescription vmstate_imx31_ccm = {
     .name = TYPE_IMX31_CCM,
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .fields = (VMStateField[]) {
-        VMSTATE_UINT32(ccmr, IMX31CCMState),
-        VMSTATE_UINT32(pdr0, IMX31CCMState),
-        VMSTATE_UINT32(pdr1, IMX31CCMState),
-        VMSTATE_UINT32(mpctl, IMX31CCMState),
-        VMSTATE_UINT32(spctl, IMX31CCMState),
-        VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
-        VMSTATE_UINT32(pmcr0, IMX31CCMState),
-        VMSTATE_UINT32(pmcr1, IMX31CCMState),
+        VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG),
         VMSTATE_END_OF_LIST()
     },
 };
@@ -109,10 +105,10 @@ static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
     uint32_t freq = 0;
     IMX31CCMState *s = IMX31_CCM(dev);
 
-    if ((s->ccmr & CCMR_PRCS) == 2) {
-        if (s->ccmr & CCMR_FPME) {
+    if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_PRCS) == 2) {
+        if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPME) {
             freq = CKIL_FREQ;
-            if (s->ccmr & CCMR_FPMF) {
+            if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPMF) {
                 freq *= 1024;
             }
         } 
@@ -130,7 +126,8 @@ static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
     uint32_t freq;
     IMX31CCMState *s = IMX31_CCM(dev);
 
-    freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
+    freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG],
+                            imx31_ccm_get_pll_ref_clk(dev));
 
     DPRINTF("freq = %d\n", freq);
 
@@ -142,7 +139,8 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
     uint32_t freq;
     IMX31CCMState *s = IMX31_CCM(dev);
 
-    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+    if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_MDS) ||
+        !(s->reg[IMX31_CCM_CCMR_REG] & CCMR_MPE)) {
         freq = imx31_ccm_get_pll_ref_clk(dev);
     } else {
         freq = imx31_ccm_get_mpll_clk(dev);
@@ -158,7 +156,8 @@ 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->pdr0, MCU));
+    freq = imx31_ccm_get_mcu_main_clk(dev)
+           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
 
     DPRINTF("freq = %d\n", freq);
 
@@ -170,7 +169,8 @@ 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->pdr0, HSP));
+    freq = imx31_ccm_get_mcu_main_clk(dev)
+           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
 
     DPRINTF("freq = %d\n", freq);
 
@@ -182,7 +182,8 @@ static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
     uint32_t freq;
     IMX31CCMState *s = IMX31_CCM(dev);
 
-    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
+    freq = imx31_ccm_get_mcu_main_clk(dev)
+           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX));
 
     DPRINTF("freq = %d\n", freq);
 
@@ -194,7 +195,8 @@ static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
     uint32_t freq;
     IMX31CCMState *s = IMX31_CCM(dev);
 
-    freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+    freq = imx31_ccm_get_hclk_clk(dev)
+           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG));
 
     DPRINTF("freq = %d\n", freq);
 
@@ -237,14 +239,24 @@ static void imx31_ccm_reset(DeviceState *dev)
 
     DPRINTF("()\n");
 
-    s->ccmr   = 0x074b0b7d;
-    s->pdr0   = 0xff870b48;
-    s->pdr1   = 0x49fcfe7f;
-    s->mpctl  = 0x04001800;
-    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
-    s->spctl  = 0x04043001;
-    s->pmcr0  = 0x80209828;
-    s->pmcr1  = 0x00aa0000;
+    memset(s->reg, 0, sizeof(uint32_t) * IMX31_CCM_MAX_REG);
+
+    s->reg[IMX31_CCM_CCMR_REG]   = 0x074b0b7d;
+    s->reg[IMX31_CCM_PDR0_REG]   = 0xff870b48;
+    s->reg[IMX31_CCM_PDR1_REG]   = 0x49fcfe7f;
+    s->reg[IMX31_CCM_RCSR_REG]   = 0x007f0000;
+    s->reg[IMX31_CCM_MPCTL_REG]  = 0x04001800;
+    s->reg[IMX31_CCM_UPCTL_REG]  = 0x04051c03;
+    s->reg[IMX31_CCM_SPCTL_REG]  = 0x04043001;
+    s->reg[IMX31_CCM_COSR_REG]   = 0x00000280;
+    s->reg[IMX31_CCM_CGR0_REG]   = 0xffffffff;
+    s->reg[IMX31_CCM_CGR1_REG]   = 0xffffffff;
+    s->reg[IMX31_CCM_CGR2_REG]   = 0xffffffff;
+    s->reg[IMX31_CCM_WIMR_REG]   = 0xffffffff;
+    s->reg[IMX31_CCM_LTR1_REG]   = 0x00004040;
+    s->reg[IMX31_CCM_PMCR0_REG]  = 0x80209828;
+    s->reg[IMX31_CCM_PMCR1_REG]  = 0x00aa0000;
+    s->reg[IMX31_CCM_PDR2_REG]   = 0x00000285;
 }
 
 static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
@@ -252,41 +264,11 @@ static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
     uint32 value = 0;
     IMX31CCMState *s = (IMX31CCMState *)opaque;
 
-    switch (offset >> 2) {
-    case 0: /* CCMR */
-        value = s->ccmr;
-        break;
-    case 1:
-        value = s->pdr0;
-        break;
-    case 2:
-        value = s->pdr1;
-        break;
-    case 4:
-        value = s->mpctl;
-        break;
-    case 6:
-        value = s->spctl;
-        break;
-    case 8:
-        value = s->cgr[0];
-        break;
-    case 9:
-        value = s->cgr[1];
-        break;
-    case 10:
-        value = s->cgr[2];
-        break;
-    case 18: /* LTR1 */
-        value = 0x00004040;
-        break;
-    case 23:
-        value = s->pmcr0;
-        break;
-    default:
+    if ((offset >> 2) < IMX31_CCM_MAX_REG) {
+        value = s->reg[offset >> 2];
+    } else {
         qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
                       HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
-        break;
     }
 
     DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
@@ -304,29 +286,29 @@ static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
             (uint32_t)value);
 
     switch (offset >> 2) {
-    case 0:
-        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
+    case IMX31_CCM_CCMR_REG:
+        s->reg[IMX31_CCM_CCMR_REG] = CCMR_FPMF | (value & 0x3b6fdfff);
         break;
-    case 1:
-        s->pdr0 = value & 0xff9f3fff;
+    case IMX31_CCM_PDR0_REG:
+        s->reg[IMX31_CCM_PDR0_REG] = value & 0xff9f3fff;
         break;
-    case 2:
-        s->pdr1 = value;
+    case IMX31_CCM_PDR1_REG:
+        s->reg[IMX31_CCM_PDR1_REG] = value;
         break;
-    case 4:
-        s->mpctl = value & 0xbfff3fff;
+    case IMX31_CCM_MPCTL_REG:
+        s->reg[IMX31_CCM_MPCTL_REG] = value & 0xbfff3fff;
         break;
-    case 6:
-        s->spctl = value & 0xbfff3fff;
+    case IMX31_CCM_SPCTL_REG:
+        s->reg[IMX31_CCM_SPCTL_REG] = value & 0xbfff3fff;
         break;
-    case 8:
-        s->cgr[0] = value;
+    case IMX31_CCM_CGR0_REG:
+        s->reg[IMX31_CCM_CGR0_REG] = value;
         break;
-    case 9:
-        s->cgr[1] = value;
+    case IMX31_CCM_CGR1_REG:
+        s->reg[IMX31_CCM_CGR1_REG] = value;
         break;
-    case 10:
-        s->cgr[2] = value;
+    case IMX31_CCM_CGR2_REG:
+        s->reg[IMX31_CCM_CGR2_REG] = value;
         break;
     default:
         qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
diff --git a/include/hw/misc/imx31_ccm.h b/include/hw/misc/imx31_ccm.h
index fcae36d..c376fad 100644
--- a/include/hw/misc/imx31_ccm.h
+++ b/include/hw/misc/imx31_ccm.h
@@ -13,6 +13,34 @@
 
 #include "hw/misc/imx_ccm.h"
 
+#define IMX31_CCM_CCMR_REG  0
+#define IMX31_CCM_PDR0_REG  1
+#define IMX31_CCM_PDR1_REG  2
+#define IMX31_CCM_RCSR_REG  3
+#define IMX31_CCM_MPCTL_REG 4
+#define IMX31_CCM_UPCTL_REG 5
+#define IMX31_CCM_SPCTL_REG 6
+#define IMX31_CCM_COSR_REG  7
+#define IMX31_CCM_CGR0_REG  8
+#define IMX31_CCM_CGR1_REG  9
+#define IMX31_CCM_CGR2_REG  10
+#define IMX31_CCM_WIMR_REG  11
+#define IMX31_CCM_LDC_REG   12
+#define IMX31_CCM_DCVR0_REG 13
+#define IMX31_CCM_DCVR1_REG 14
+#define IMX31_CCM_DCVR2_REG 15
+#define IMX31_CCM_DCVR3_REG 16
+#define IMX31_CCM_LTR0_REG  17
+#define IMX31_CCM_LTR1_REG  18
+#define IMX31_CCM_LTR2_REG  19
+#define IMX31_CCM_LTR3_REG  20
+#define IMX31_CCM_LTBR0_REG 21
+#define IMX31_CCM_LTBR1_REG 22
+#define IMX31_CCM_PMCR0_REG 23
+#define IMX31_CCM_PMCR1_REG 24
+#define IMX31_CCM_PDR2_REG  25
+#define IMX31_CCM_MAX_REG   26
+
 /* CCMR */
 #define CCMR_FPME    (1<<0)
 #define CCMR_MPE     (1<<3)
@@ -53,14 +81,8 @@ typedef struct IMX31CCMState {
     /* <public> */
     MemoryRegion iomem;
 
-    uint32_t ccmr;
-    uint32_t pdr0;
-    uint32_t pdr1;
-    uint32_t mpctl;
-    uint32_t spctl;
-    uint32_t cgr[3];
-    uint32_t pmcr0;
-    uint32_t pmcr1;
+    uint32_t reg[IMX31_CCM_MAX_REG];
+
 } IMX31CCMState;
 
 #endif /* IMX31_CCM_H */
-- 
2.5.0

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

* Re: [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver
  2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
                   ` (4 preceding siblings ...)
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array Jean-Christophe Dubois
@ 2015-12-17 11:30 ` Peter Maydell
  5 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2015-12-17 11:30 UTC (permalink / raw)
  To: Jean-Christophe Dubois; +Cc: qemu-arm, QEMU Developers, Peter Crosthwaite

On 7 December 2015 at 22:53, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> i.MX25 SOC has a different CCM device than i.MX31.
>
> Qemu i.MX25 emulation was built with i.MX31 CCM driver. This allows
> Linux to work on top of the i.MX25 emultion but this is not correct.
>
> Furthermore, other SOC we could emulate like i.MX6 have yet a different
> implementation of the CCM device.
>
> So we split the i.MX31 into a generic base class and a specific i.MX31
> class.
>
> We then add an i.MX25 specific CCM Device and have the i.MX25 SOC use it.
>
> Jean-Christophe Dubois (5):
>   i.MX: Fix i.MX31 default/reset configuration.
>   i.MX: rename i.MX CCM get_clock() function and CLK ID enum names
>   i.MX: Split the CCM class into an abstact base class and a concrete
>     class.
>   i.MX: Add an i.MX25 specific CCM class/instance.
>   i.MX: move i.MX31 CCM object to register array.

At Peter C's suggestion I've applied 1-4 to target-arm.next; 5
is still awaiting Peter's review.

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array.
  2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array Jean-Christophe Dubois
@ 2015-12-19 21:27   ` pcrost
  2016-01-07 11:36     ` Peter Maydell
  0 siblings, 1 reply; 9+ messages in thread
From: pcrost @ 2015-12-19 21:27 UTC (permalink / raw)
  To: Jean-Christophe Dubois
  Cc: peter.maydell, qemu-arm, qemu-devel, crosthwaite.peter

On Mon, Dec 07, 2015 at 11:53:42PM +0100, Jean-Christophe Dubois wrote:
> With this i.MX25 and i.MX31 to have close implementation.
> 

"will have closer implementations".

Otherwise,

Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>

Peter, do you want to just amend the grammar in the queue?

Regards,
Peter

> Moreover all i.MX31 CCM registers are now present
> 
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---
> 
> Changes since v1:
>  * Not present
> 
> Changes since v2:
>  * Not present
> 
> Changes since v3:
>  * Not present
> 
> Changes since v4:
>  * Not present
> 
> Changes since v5:
>  * Not present
> 
>  hw/misc/imx31_ccm.c         | 188 ++++++++++++++++++++------------------------
>  include/hw/misc/imx31_ccm.h |  38 +++++++--
>  2 files changed, 115 insertions(+), 111 deletions(-)
> 
> diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
> index b92d2e0..47d6ead 100644
> --- a/hw/misc/imx31_ccm.c
> +++ b/hw/misc/imx31_ccm.c
> @@ -29,77 +29,73 @@
>  
>  static char const *imx31_ccm_reg_name(uint32_t reg)
>  {
> +    static char unknown[20];
> +
>      switch (reg) {
> -    case 0:
> +    case IMX31_CCM_CCMR_REG:
>          return "CCMR";
> -    case 1:
> +    case IMX31_CCM_PDR0_REG:
>          return "PDR0";
> -    case 2:
> +    case IMX31_CCM_PDR1_REG:
>          return "PDR1";
> -    case 3:
> +    case IMX31_CCM_RCSR_REG:
>          return "RCSR";
> -    case 4:
> +    case IMX31_CCM_MPCTL_REG:
>          return "MPCTL";
> -    case 5:
> +    case IMX31_CCM_UPCTL_REG:
>          return "UPCTL";
> -    case 6:
> +    case IMX31_CCM_SPCTL_REG:
>          return "SPCTL";
> -    case 7:
> +    case IMX31_CCM_COSR_REG:
>          return "COSR";
> -    case 8:
> +    case IMX31_CCM_CGR0_REG:
>          return "CGR0";
> -    case 9:
> +    case IMX31_CCM_CGR1_REG:
>          return "CGR1";
> -    case 10:
> +    case IMX31_CCM_CGR2_REG:
>          return "CGR2";
> -    case 11:
> +    case IMX31_CCM_WIMR_REG:
>          return "WIMR";
> -    case 12:
> +    case IMX31_CCM_LDC_REG:
>          return "LDC";
> -    case 13:
> +    case IMX31_CCM_DCVR0_REG:
>          return "DCVR0";
> -    case 14:
> +    case IMX31_CCM_DCVR1_REG:
>          return "DCVR1";
> -    case 15:
> +    case IMX31_CCM_DCVR2_REG:
>          return "DCVR2";
> -    case 16:
> +    case IMX31_CCM_DCVR3_REG:
>          return "DCVR3";
> -    case 17:
> +    case IMX31_CCM_LTR0_REG:
>          return "LTR0";
> -    case 18:
> +    case IMX31_CCM_LTR1_REG:
>          return "LTR1";
> -    case 19:
> +    case IMX31_CCM_LTR2_REG:
>          return "LTR2";
> -    case 20:
> +    case IMX31_CCM_LTR3_REG:
>          return "LTR3";
> -    case 21:
> +    case IMX31_CCM_LTBR0_REG:
>          return "LTBR0";
> -    case 22:
> +    case IMX31_CCM_LTBR1_REG:
>          return "LTBR1";
> -    case 23:
> +    case IMX31_CCM_PMCR0_REG:
>          return "PMCR0";
> -    case 24:
> +    case IMX31_CCM_PMCR1_REG:
>          return "PMCR1";
> -    case 25:
> +    case IMX31_CCM_PDR2_REG:
>          return "PDR2";
>      default:
> -        return "???";
> +        sprintf(unknown, "[%d ?]", reg);
> +        return unknown;
>      }
>  }
>  
>  static const VMStateDescription vmstate_imx31_ccm = {
>      .name = TYPE_IMX31_CCM,
> -    .version_id = 1,
> -    .minimum_version_id = 1,
> +    .version_id = 2,
> +    .minimum_version_id = 2,
>      .fields = (VMStateField[]) {
> -        VMSTATE_UINT32(ccmr, IMX31CCMState),
> -        VMSTATE_UINT32(pdr0, IMX31CCMState),
> -        VMSTATE_UINT32(pdr1, IMX31CCMState),
> -        VMSTATE_UINT32(mpctl, IMX31CCMState),
> -        VMSTATE_UINT32(spctl, IMX31CCMState),
> -        VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
> -        VMSTATE_UINT32(pmcr0, IMX31CCMState),
> -        VMSTATE_UINT32(pmcr1, IMX31CCMState),
> +        VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG),
>          VMSTATE_END_OF_LIST()
>      },
>  };
> @@ -109,10 +105,10 @@ static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
>      uint32_t freq = 0;
>      IMX31CCMState *s = IMX31_CCM(dev);
>  
> -    if ((s->ccmr & CCMR_PRCS) == 2) {
> -        if (s->ccmr & CCMR_FPME) {
> +    if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_PRCS) == 2) {
> +        if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPME) {
>              freq = CKIL_FREQ;
> -            if (s->ccmr & CCMR_FPMF) {
> +            if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPMF) {
>                  freq *= 1024;
>              }
>          } 
> @@ -130,7 +126,8 @@ static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
>      uint32_t freq;
>      IMX31CCMState *s = IMX31_CCM(dev);
>  
> -    freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
> +    freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG],
> +                            imx31_ccm_get_pll_ref_clk(dev));
>  
>      DPRINTF("freq = %d\n", freq);
>  
> @@ -142,7 +139,8 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
>      uint32_t freq;
>      IMX31CCMState *s = IMX31_CCM(dev);
>  
> -    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
> +    if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_MDS) ||
> +        !(s->reg[IMX31_CCM_CCMR_REG] & CCMR_MPE)) {
>          freq = imx31_ccm_get_pll_ref_clk(dev);
>      } else {
>          freq = imx31_ccm_get_mpll_clk(dev);
> @@ -158,7 +156,8 @@ 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->pdr0, MCU));
> +    freq = imx31_ccm_get_mcu_main_clk(dev)
> +           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
>  
>      DPRINTF("freq = %d\n", freq);
>  
> @@ -170,7 +169,8 @@ 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->pdr0, HSP));
> +    freq = imx31_ccm_get_mcu_main_clk(dev)
> +           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
>  
>      DPRINTF("freq = %d\n", freq);
>  
> @@ -182,7 +182,8 @@ static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
>      uint32_t freq;
>      IMX31CCMState *s = IMX31_CCM(dev);
>  
> -    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
> +    freq = imx31_ccm_get_mcu_main_clk(dev)
> +           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX));
>  
>      DPRINTF("freq = %d\n", freq);
>  
> @@ -194,7 +195,8 @@ static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
>      uint32_t freq;
>      IMX31CCMState *s = IMX31_CCM(dev);
>  
> -    freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
> +    freq = imx31_ccm_get_hclk_clk(dev)
> +           / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG));
>  
>      DPRINTF("freq = %d\n", freq);
>  
> @@ -237,14 +239,24 @@ static void imx31_ccm_reset(DeviceState *dev)
>  
>      DPRINTF("()\n");
>  
> -    s->ccmr   = 0x074b0b7d;
> -    s->pdr0   = 0xff870b48;
> -    s->pdr1   = 0x49fcfe7f;
> -    s->mpctl  = 0x04001800;
> -    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
> -    s->spctl  = 0x04043001;
> -    s->pmcr0  = 0x80209828;
> -    s->pmcr1  = 0x00aa0000;
> +    memset(s->reg, 0, sizeof(uint32_t) * IMX31_CCM_MAX_REG);
> +
> +    s->reg[IMX31_CCM_CCMR_REG]   = 0x074b0b7d;
> +    s->reg[IMX31_CCM_PDR0_REG]   = 0xff870b48;
> +    s->reg[IMX31_CCM_PDR1_REG]   = 0x49fcfe7f;
> +    s->reg[IMX31_CCM_RCSR_REG]   = 0x007f0000;
> +    s->reg[IMX31_CCM_MPCTL_REG]  = 0x04001800;
> +    s->reg[IMX31_CCM_UPCTL_REG]  = 0x04051c03;
> +    s->reg[IMX31_CCM_SPCTL_REG]  = 0x04043001;
> +    s->reg[IMX31_CCM_COSR_REG]   = 0x00000280;
> +    s->reg[IMX31_CCM_CGR0_REG]   = 0xffffffff;
> +    s->reg[IMX31_CCM_CGR1_REG]   = 0xffffffff;
> +    s->reg[IMX31_CCM_CGR2_REG]   = 0xffffffff;
> +    s->reg[IMX31_CCM_WIMR_REG]   = 0xffffffff;
> +    s->reg[IMX31_CCM_LTR1_REG]   = 0x00004040;
> +    s->reg[IMX31_CCM_PMCR0_REG]  = 0x80209828;
> +    s->reg[IMX31_CCM_PMCR1_REG]  = 0x00aa0000;
> +    s->reg[IMX31_CCM_PDR2_REG]   = 0x00000285;
>  }
>  
>  static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
> @@ -252,41 +264,11 @@ static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
>      uint32 value = 0;
>      IMX31CCMState *s = (IMX31CCMState *)opaque;
>  
> -    switch (offset >> 2) {
> -    case 0: /* CCMR */
> -        value = s->ccmr;
> -        break;
> -    case 1:
> -        value = s->pdr0;
> -        break;
> -    case 2:
> -        value = s->pdr1;
> -        break;
> -    case 4:
> -        value = s->mpctl;
> -        break;
> -    case 6:
> -        value = s->spctl;
> -        break;
> -    case 8:
> -        value = s->cgr[0];
> -        break;
> -    case 9:
> -        value = s->cgr[1];
> -        break;
> -    case 10:
> -        value = s->cgr[2];
> -        break;
> -    case 18: /* LTR1 */
> -        value = 0x00004040;
> -        break;
> -    case 23:
> -        value = s->pmcr0;
> -        break;
> -    default:
> +    if ((offset >> 2) < IMX31_CCM_MAX_REG) {
> +        value = s->reg[offset >> 2];
> +    } else {
>          qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
>                        HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
> -        break;
>      }
>  
>      DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
> @@ -304,29 +286,29 @@ static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
>              (uint32_t)value);
>  
>      switch (offset >> 2) {
> -    case 0:
> -        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
> +    case IMX31_CCM_CCMR_REG:
> +        s->reg[IMX31_CCM_CCMR_REG] = CCMR_FPMF | (value & 0x3b6fdfff);
>          break;
> -    case 1:
> -        s->pdr0 = value & 0xff9f3fff;
> +    case IMX31_CCM_PDR0_REG:
> +        s->reg[IMX31_CCM_PDR0_REG] = value & 0xff9f3fff;
>          break;
> -    case 2:
> -        s->pdr1 = value;
> +    case IMX31_CCM_PDR1_REG:
> +        s->reg[IMX31_CCM_PDR1_REG] = value;
>          break;
> -    case 4:
> -        s->mpctl = value & 0xbfff3fff;
> +    case IMX31_CCM_MPCTL_REG:
> +        s->reg[IMX31_CCM_MPCTL_REG] = value & 0xbfff3fff;
>          break;
> -    case 6:
> -        s->spctl = value & 0xbfff3fff;
> +    case IMX31_CCM_SPCTL_REG:
> +        s->reg[IMX31_CCM_SPCTL_REG] = value & 0xbfff3fff;
>          break;
> -    case 8:
> -        s->cgr[0] = value;
> +    case IMX31_CCM_CGR0_REG:
> +        s->reg[IMX31_CCM_CGR0_REG] = value;
>          break;
> -    case 9:
> -        s->cgr[1] = value;
> +    case IMX31_CCM_CGR1_REG:
> +        s->reg[IMX31_CCM_CGR1_REG] = value;
>          break;
> -    case 10:
> -        s->cgr[2] = value;
> +    case IMX31_CCM_CGR2_REG:
> +        s->reg[IMX31_CCM_CGR2_REG] = value;
>          break;
>      default:
>          qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
> diff --git a/include/hw/misc/imx31_ccm.h b/include/hw/misc/imx31_ccm.h
> index fcae36d..c376fad 100644
> --- a/include/hw/misc/imx31_ccm.h
> +++ b/include/hw/misc/imx31_ccm.h
> @@ -13,6 +13,34 @@
>  
>  #include "hw/misc/imx_ccm.h"
>  
> +#define IMX31_CCM_CCMR_REG  0
> +#define IMX31_CCM_PDR0_REG  1
> +#define IMX31_CCM_PDR1_REG  2
> +#define IMX31_CCM_RCSR_REG  3
> +#define IMX31_CCM_MPCTL_REG 4
> +#define IMX31_CCM_UPCTL_REG 5
> +#define IMX31_CCM_SPCTL_REG 6
> +#define IMX31_CCM_COSR_REG  7
> +#define IMX31_CCM_CGR0_REG  8
> +#define IMX31_CCM_CGR1_REG  9
> +#define IMX31_CCM_CGR2_REG  10
> +#define IMX31_CCM_WIMR_REG  11
> +#define IMX31_CCM_LDC_REG   12
> +#define IMX31_CCM_DCVR0_REG 13
> +#define IMX31_CCM_DCVR1_REG 14
> +#define IMX31_CCM_DCVR2_REG 15
> +#define IMX31_CCM_DCVR3_REG 16
> +#define IMX31_CCM_LTR0_REG  17
> +#define IMX31_CCM_LTR1_REG  18
> +#define IMX31_CCM_LTR2_REG  19
> +#define IMX31_CCM_LTR3_REG  20
> +#define IMX31_CCM_LTBR0_REG 21
> +#define IMX31_CCM_LTBR1_REG 22
> +#define IMX31_CCM_PMCR0_REG 23
> +#define IMX31_CCM_PMCR1_REG 24
> +#define IMX31_CCM_PDR2_REG  25
> +#define IMX31_CCM_MAX_REG   26
> +
>  /* CCMR */
>  #define CCMR_FPME    (1<<0)
>  #define CCMR_MPE     (1<<3)
> @@ -53,14 +81,8 @@ typedef struct IMX31CCMState {
>      /* <public> */
>      MemoryRegion iomem;
>  
> -    uint32_t ccmr;
> -    uint32_t pdr0;
> -    uint32_t pdr1;
> -    uint32_t mpctl;
> -    uint32_t spctl;
> -    uint32_t cgr[3];
> -    uint32_t pmcr0;
> -    uint32_t pmcr1;
> +    uint32_t reg[IMX31_CCM_MAX_REG];
> +
>  } IMX31CCMState;
>  
>  #endif /* IMX31_CCM_H */
> -- 
> 2.5.0
> 

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

* Re: [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array.
  2015-12-19 21:27   ` pcrost
@ 2016-01-07 11:36     ` Peter Maydell
  0 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2016-01-07 11:36 UTC (permalink / raw)
  To: pcrost; +Cc: Peter Crosthwaite, QEMU Developers, qemu-arm,
	Jean-Christophe Dubois

On 19 December 2015 at 21:27, pcrost <crosthwaitepeter@gmail.com> wrote:
> On Mon, Dec 07, 2015 at 11:53:42PM +0100, Jean-Christophe Dubois wrote:
>> With this i.MX25 and i.MX31 to have close implementation.
>>
>
> "will have closer implementations".
>
> Otherwise,
>
> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
>
> Peter, do you want to just amend the grammar in the queue?

Yes, I've just applied to target-arm.next with that tweak to
the commit message.

thanks
-- PMM

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

end of thread, other threads:[~2016-01-07 11:36 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-07 22:53 [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver Jean-Christophe Dubois
2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 1/5] i.MX: Fix i.MX31 default/reset configuration Jean-Christophe Dubois
2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 2/5] i.MX: rename i.MX CCM get_clock() function and CLK ID enum names Jean-Christophe Dubois
2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 3/5] i.MX: Split the CCM class into an abstact base class and a concrete class Jean-Christophe Dubois
2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 4/5] i.MX: Add an i.MX25 specific CCM class/instance Jean-Christophe Dubois
2015-12-07 22:53 ` [Qemu-devel] [PATCH v6 5/5] i.MX: move i.MX31 CCM object to register array Jean-Christophe Dubois
2015-12-19 21:27   ` pcrost
2016-01-07 11:36     ` Peter Maydell
2015-12-17 11:30 ` [Qemu-devel] [PATCH v6 0/5] Add an i.MX25 specific CCM driver 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).