Skip to contentSkip to conversation

Add an i.MX25 specific CCM driver

PATCH Superseded Jean-Christophe Dubois 2 participants 11 messages Nov 19, 2015 – Nov 27, 2015 Arm i.MX and Layerscape

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v3 cover Add an i.MX25 specific CCM driver

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 (3):
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.

 hw/arm/fsl-imx25.c          |   2 +-
 hw/arm/fsl-imx31.c          |   2 +-
 hw/misc/Makefile.objs       |   2 +
 hw/misc/imx25_ccm.c         | 243 ++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx31_ccm.c         | 252 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx_ccm.c           | 226 +++------------------------------------
 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 |  59 +++++++++++
 include/hw/misc/imx31_ccm.h |  64 +++++++++++
 include/hw/misc/imx_ccm.h   |  78 ++++++--------
 13 files changed, 690 insertions(+), 282 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
Signature · 1 line
2.5.0
#1 lore raw

PATCH v3 1/3 i.MX: rename i.MX CCM get_clock() function and CLK ID enum names

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.

---

Changes since v1:
* Not present

Changes since v2:
* Not present

 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(-)
hw/misc/imx_ccm.c +4 -4
index 4cc2bbc..415937f 100644
@@ -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;
hw/timer/imx_epit.c +9 -11
index 967be4a..50bf83c 100644
@@ -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);
     }
 }
 
hw/timer/imx_gpt.c +8 -8
index 7257f42..b1893b8 100644
@@ -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);
     }
 }
 
include/hw/misc/imx_ccm.h +4 -4
index 0f2e469..09f6248 100644
@@ -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 */
Signature · 1 line
2.5.0
Reviewed-by on v3 1/3
#2 lore raw

Re: [Qemu-devel] [PATCH v3 1/3] i.MX: rename i.MX CCM get_clock() function and CLK ID enum names

Quoted 7 lines · 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.

Quoted 145 lines

---

Changes since v1:
* Not present

Changes since v2:
* Not present

 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(-)
hw/misc/imx_ccm.c +4 -4
index 4cc2bbc..415937f 100644
@@ -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;
hw/timer/imx_epit.c +9 -11
index 967be4a..50bf83c 100644
@@ -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);
     }
 }
 
hw/timer/imx_gpt.c +8 -8
index 7257f42..b1893b8 100644
@@ -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);
     }
 }
 
include/hw/misc/imx_ccm.h +4 -5
index 0f2e469..09f6248 100644
@@ -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

#3 lore raw

PATCH v3 2/3 i.MX: Split the CCM class into an abstact base class and a concrete class.

In reply to 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

---

Changes since v1:
* None

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

 hw/arm/fsl-imx25.c          |   2 +-
 hw/arm/fsl-imx31.c          |   2 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx31_ccm.c         | 252 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx_ccm.c           | 224 +++------------------------------------
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/arm/fsl-imx31.h  |   4 +-
 include/hw/misc/imx31_ccm.h |  64 +++++++++++
 include/hw/misc/imx_ccm.h   |  70 +++++-------
 9 files changed, 365 insertions(+), 258 deletions(-)
 create mode 100644 hw/misc/imx31_ccm.c
 create mode 100644 include/hw/misc/imx31_ccm.h
hw/arm/fsl-imx25.c +1 -1
index e1cadac..5526c22 100644
@@ -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++) {
hw/arm/fsl-imx31.c +1 -1
index 53d4473..bcc71dc 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index aeb6b7d..c77f3e3 100644
@@ -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
hw/misc/imx31_ccm.c new file +252 -0

252 lines not on this page · raw message

hw/misc/imx_ccm.c +16 -208

268 lines not on this page · raw message

include/hw/arm/fsl-imx25.h +2 -2
index 73f50c6..5c62fde 100644
@@ -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];
include/hw/arm/fsl-imx31.h +2 -2
index 5e8f795..d408abb 100644
@@ -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];
include/hw/misc/imx31_ccm.h new file +64 -0

64 lines not on this page · raw message

include/hw/misc/imx_ccm.h +26 -44

103 lines not on this page · raw message

Signature · 1 line
2.5.0
Reviewed-by on v3 2/3
#4 lore raw

Re: [Qemu-devel] [PATCH v3 2/3] i.MX: Split the CCM class into an abstact base class and a concrete class.

Quoted 815 lines · 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

---

Changes since v1:
* None

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

 hw/arm/fsl-imx25.c          |   2 +-
 hw/arm/fsl-imx31.c          |   2 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx31_ccm.c         | 252 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/imx_ccm.c           | 224 +++------------------------------------
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/arm/fsl-imx31.h  |   4 +-
 include/hw/misc/imx31_ccm.h |  64 +++++++++++
 include/hw/misc/imx_ccm.h   |  70 +++++-------
 9 files changed, 365 insertions(+), 258 deletions(-)
 create mode 100644 hw/misc/imx31_ccm.c
 create mode 100644 include/hw/misc/imx31_ccm.h
hw/arm/fsl-imx25.c +1 -1
index e1cadac..5526c22 100644
@@ -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++) {
hw/arm/fsl-imx31.c +1 -1
index 53d4473..bcc71dc 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index aeb6b7d..c77f3e3 100644
@@ -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
hw/misc/imx31_ccm.c new file +252 -0
new file mode 100644
index 0000000..f56fa77
@@ -0,0 +1,252 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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 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_ref_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_PRCS) == 2) {
+        return CKIL_FREQ * 1024;
+    } else {
+        return CKIH_FREQ;
+    }
+}
+
+static uint32_t imx31_ccm_get_mcu_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+        return imx31_ccm_get_ref_clk(dev);
+    } else {
+        return imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_ref_clk(dev));
+    }
+}
+
+static uint32_t imx31_ccm_get_hsp_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_hsp_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+    DPRINTF("Clock = %d)\n", clock);
+
+    switch (clock) {
+    case NOCLK:
+        return 0;
+    case CLK_MCU:
+        return imx31_ccm_get_mcu_clk(dev);
+    case CLK_HSP:
+        return imx31_ccm_get_hsp_clk(dev);
+    case CLK_IPG:
+        return imx31_ccm_get_ipg_clk(dev);
+    case CLK_32k:
+        return CKIL_FREQ;
+    default:
+        return 0;
+    }
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    s->ccmr = 0x074b0b7b;
+    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;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__, offset);
+        return 0;
+    }
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__, offset);
+        return;
+    }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+    .read = imx31_ccm_read,
+    .write = imx31_ccm_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+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)
hw/misc/imx_ccm.c +16 -208
index 415937f..e8a917c 100644
@@ -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,59 +25,36 @@
         } \
     } 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)
 {
-    IMXCCMState *s = IMX_CCM(dev);
+    IMXCCMClass *klass = IMX_GET_CLASS(dev);
 
-    switch (clock) {
-    case NOCLK:
+    DPRINTF("Clock = %d)\n", clock);
+
+    if (klass->get_clock_frequency) {
+        return klass->get_clock_frequency(dev, clock);
+    } else {
         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;
     }
-    return 0;
 }
 
 /*
  * 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 mfn = MFN(pllreg);  /* Numerator */
     uint32_t mfi = MFI(pllreg); /* Integer part */
     uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
     uint32_t pd = 1 + PD(pllreg);   /* Pre-divider */
 
+    DPRINTF("pllreg = %d, base_freq = %d)\n", pllreg, base_freq);
+
     if (mfi < 5) {
         mfi = 5;
     }
+
     /* mfn is 10-bit signed twos-complement */
     mfn <<= 32 - 10;
     mfn >>= 32 - 10;
@@ -89,178 +63,12 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
             (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 = 0x074b0b7b;
-    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);
-
-    sbc->init = imx_ccm_init;
-    dc->reset = imx_ccm_reset;
-    dc->vmsd = &vmstate_imx_ccm;
-    dc->desc = "i.MX Clock Control Module";
-}
-
 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)
include/hw/arm/fsl-imx25.h +2 -2
index 73f50c6..5c62fde 100644
@@ -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];
include/hw/arm/fsl-imx31.h +2 -2
index 5e8f795..d408abb 100644
@@ -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];
include/hw/misc/imx31_ccm.h new file +64 -0
new file mode 100644
index 0000000..47be0cd
@@ -0,0 +1,64 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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)
+
+/* 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 */
include/hw/misc/imx_ccm.h +16 -44
index 09f6248..33293e8 100644
@@ -1,5 +1,5 @@
 /*
- * IMX31 Clock Control Module
+ * IMX Clock Control Module base class
  *
  * Copyright (C) 2012 NICTA
  * Updated by Jean-Christophe Dubois <[email protected]>
@@ -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,47 @@
 #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;
 
+typedef struct IMXCCMClass {
+    /* <private> */
+    SysBusDeviceClass parent_class;

include/hw/misc/imx_ccm.h line 70

Quoted 10 lines
include/hw/misc/imx_ccm.h +10 -0
+
+    /* <public> */
+    uint32_t (*get_clock_frequency)(DeviceState *s, IMXClk clk);
+
+} IMXCCMClass;
+
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq);
+
+void imx_ccm_set_ref_clock_frequency(DeviceState *s, uint32_t ref_freq);
+

This looks unused. If implemented will it remove the hardcoded per-implementation crystal freqs?

include/hw/misc/imx_ccm.h line 72

Quoted 2 lines
include/hw/misc/imx_ccm.h +0 -0
 uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock);
 

The Type of the prototype should match the abstract type - IMXCCMState.

Otherwise,

Regards,
Peter

Quoted 4 lines
include/hw/misc/imx_ccm.h +0 -1
 #endif /* IMX_CCM_H */
--

2.5.0

#5 lore raw

Re: [Qemu-devel] [PATCH v3 2/3] i.MX: Split the CCM class into an abstact base class and a concrete class.

Quoted 814 lines · Peter Crosthwaite
Quoted 812 lines · 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

---

Changes since v1:
* None

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

  hw/arm/fsl-imx25.c          |   2 +-
  hw/arm/fsl-imx31.c          |   2 +-
  hw/misc/Makefile.objs       |   1 +
  hw/misc/imx31_ccm.c         | 252 ++++++++++++++++++++++++++++++++++++++++++++
  hw/misc/imx_ccm.c           | 224 +++------------------------------------
  include/hw/arm/fsl-imx25.h  |   4 +-
  include/hw/arm/fsl-imx31.h  |   4 +-
  include/hw/misc/imx31_ccm.h |  64 +++++++++++
  include/hw/misc/imx_ccm.h   |  70 +++++-------
  9 files changed, 365 insertions(+), 258 deletions(-)

create mode 100644 hw/misc/imx31_ccm.c
create mode 100644 include/hw/misc/imx31_ccm.h

hw/arm/fsl-imx25.c +1 -1
index e1cadac..5526c22 100644
@@ -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++) {
hw/arm/fsl-imx31.c +1 -1
index 53d4473..bcc71dc 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index aeb6b7d..c77f3e3 100644
@@ -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
hw/misc/imx31_ccm.c new file +252 -0
new file mode 100644
index 0000000..f56fa77
@@ -0,0 +1,252 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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 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_ref_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_PRCS) == 2) {
+        return CKIL_FREQ * 1024;
+    } else {
+        return CKIH_FREQ;
+    }
+}
+
+static uint32_t imx31_ccm_get_mcu_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+        return imx31_ccm_get_ref_clk(dev);
+    } else {
+        return imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_ref_clk(dev));
+    }
+}
+
+static uint32_t imx31_ccm_get_hsp_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_hsp_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+    DPRINTF("Clock = %d)\n", clock);
+
+    switch (clock) {
+    case NOCLK:
+        return 0;
+    case CLK_MCU:
+        return imx31_ccm_get_mcu_clk(dev);
+    case CLK_HSP:
+        return imx31_ccm_get_hsp_clk(dev);
+    case CLK_IPG:
+        return imx31_ccm_get_ipg_clk(dev);
+    case CLK_32k:
+        return CKIL_FREQ;
+    default:
+        return 0;
+    }
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    s->ccmr = 0x074b0b7b;
+    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;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__, offset);
+        return 0;
+    }
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__, offset);
+        return;
+    }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+    .read = imx31_ccm_read,
+    .write = imx31_ccm_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+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)
hw/misc/imx_ccm.c +16 -208
index 415937f..e8a917c 100644
@@ -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,59 +25,36 @@
          } \
      } 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)
  {
-    IMXCCMState *s = IMX_CCM(dev);
+    IMXCCMClass *klass = IMX_GET_CLASS(dev);
 
-    switch (clock) {
-    case NOCLK:
+    DPRINTF("Clock = %d)\n", clock);
+
+    if (klass->get_clock_frequency) {
+        return klass->get_clock_frequency(dev, clock);
+    } else {
          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;
      }
-    return 0;
  }
 
  /*
   * 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 mfn = MFN(pllreg);  /* Numerator */
      uint32_t mfi = MFI(pllreg); /* Integer part */
      uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
      uint32_t pd = 1 + PD(pllreg);   /* Pre-divider */
 
+    DPRINTF("pllreg = %d, base_freq = %d)\n", pllreg, base_freq);
+
      if (mfi < 5) {
          mfi = 5;
      }
+
      /* mfn is 10-bit signed twos-complement */
      mfn <<= 32 - 10;
      mfn >>= 32 - 10;
@@ -89,178 +63,12 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
              (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 = 0x074b0b7b;
-    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);
-
-    sbc->init = imx_ccm_init;
-    dc->reset = imx_ccm_reset;
-    dc->vmsd = &vmstate_imx_ccm;
-    dc->desc = "i.MX Clock Control Module";
-}
-
  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)
include/hw/arm/fsl-imx25.h +2 -2
index 73f50c6..5c62fde 100644
@@ -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];
include/hw/arm/fsl-imx31.h +2 -2
index 5e8f795..d408abb 100644
@@ -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];
include/hw/misc/imx31_ccm.h new file +64 -0
new file mode 100644
index 0000000..47be0cd
@@ -0,0 +1,64 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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)
+
+/* 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 */
include/hw/misc/imx_ccm.h +13 -44
index 09f6248..33293e8 100644
@@ -1,5 +1,5 @@
  /*
- * IMX31 Clock Control Module
+ * IMX Clock Control Module base class
   *
   * Copyright (C) 2012 NICTA
   * Updated by Jean-Christophe Dubois <[email protected]>
@@ -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,47 @@
  #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;
 
Quoted 15 lines

include/hw/misc/imx_ccm.h line 70

Quoted 13 lines
include/hw/misc/imx_ccm.h +13 -0
+typedef struct IMXCCMClass {
+    /* <private> */
+    SysBusDeviceClass parent_class;
+
+    /* <public> */
+    uint32_t (*get_clock_frequency)(DeviceState *s, IMXClk clk);
+
+} IMXCCMClass;
+
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq);
+
+void imx_ccm_set_ref_clock_frequency(DeviceState *s, uint32_t ref_freq);
+

This looks unused. If implemented will it remove the hardcoded per-implementation crystal freqs?

That was the idea. But for now it is not implemented so I'll remove it.

i.MX SOC seems to be designed to receive fixed/hardcoded reference clock. There does not seem to be a real need for a variable ref clock.

Quoted 4 lines

include/hw/misc/imx_ccm.h line 72

Quoted 3 lines
include/hw/misc/imx_ccm.h +0 -0
  uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock);
 

The Type of the prototype should match the abstract type - IMXCCMState.

OK, I'll move to IMXCCMState

Quoted 13 lines

Otherwise,

Regards,
Peter

Quoted 4 lines
include/hw/misc/imx_ccm.h +0 -1
  #endif /* IMX_CCM_H */
--

2.5.0

#6 lore raw

Re: [Qemu-devel] [PATCH v3 2/3] i.MX: Split the CCM class into an abstact base class and a concrete class.

Quoted 854 lines · Jean-Christophe DUBOIS
Quoted 835 lines · Peter Crosthwaite
Quoted 832 lines · 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

---

Changes since v1:
* None

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

  hw/arm/fsl-imx25.c          |   2 +-
  hw/arm/fsl-imx31.c          |   2 +-
  hw/misc/Makefile.objs       |   1 +
  hw/misc/imx31_ccm.c         | 252
++++++++++++++++++++++++++++++++++++++++++++
  hw/misc/imx_ccm.c           | 224
+++------------------------------------
  include/hw/arm/fsl-imx25.h  |   4 +-
  include/hw/arm/fsl-imx31.h  |   4 +-
  include/hw/misc/imx31_ccm.h |  64 +++++++++++
  include/hw/misc/imx_ccm.h   |  70 +++++-------
  9 files changed, 365 insertions(+), 258 deletions(-)

create mode 100644 hw/misc/imx31_ccm.c
create mode 100644 include/hw/misc/imx31_ccm.h

hw/arm/fsl-imx25.c +1 -1
index e1cadac..5526c22 100644
@@ -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++) {
hw/arm/fsl-imx31.c +1 -1
index 53d4473..bcc71dc 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index aeb6b7d..c77f3e3 100644
@@ -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
hw/misc/imx31_ccm.c new file +7 -0
new file mode 100644
index 0000000..f56fa77
@@ -0,0 +1,252 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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.
+ */

hw/misc/imx31_ccm.c new file +79 -0
+
+#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 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_ref_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_PRCS) == 2) {
+        return CKIL_FREQ * 1024;
+    } else {
+        return CKIH_FREQ;
+    }
+}
+
+static uint32_t imx31_ccm_get_mcu_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
+        return imx31_ccm_get_ref_clk(dev);
+    } else {
+        return imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_ref_clk(dev));
+    }
+}
+
+static uint32_t imx31_ccm_get_hsp_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx31_ccm_get_hsp_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(DeviceState *dev, IMXClk

clock)

hw/misc/imx31_ccm.c new file +35 -0
+{
+    DPRINTF("Clock = %d)\n", clock);
+
+    switch (clock) {
+    case NOCLK:
+        return 0;
+    case CLK_MCU:
+        return imx31_ccm_get_mcu_clk(dev);
+    case CLK_HSP:
+        return imx31_ccm_get_hsp_clk(dev);
+    case CLK_IPG:
+        return imx31_ccm_get_ipg_clk(dev);
+    case CLK_32k:
+        return CKIL_FREQ;
+    default:
+        return 0;
+    }
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+    IMX31CCMState *s = IMX31_CCM(dev);
+
+    DPRINTF("()\n");
+
+    s->ccmr = 0x074b0b7b;
+    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;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned

size)

hw/misc/imx31_ccm.c new file +37 -0
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__,
offset);

hw/misc/imx31_ccm.c new file +40 -0
+        return 0;
+    }
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX31CCMState *s = (IMX31CCMState *)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_IMX31_CCM, __func__,
offset);

hw/misc/imx31_ccm.c new file +47 -0
+        return;
+    }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+    .read = imx31_ccm_read,
+    .write = imx31_ccm_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+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)
hw/misc/imx_ccm.c +0 -0
index 415937f..e8a917c 100644
@@ -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"
hw/misc/imx_ccm.c +10 -40
-#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,59 +25,36 @@
          } \
      } 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)
  {
-    IMXCCMState *s = IMX_CCM(dev);
+    IMXCCMClass *klass = IMX_GET_CLASS(dev);
 
-    switch (clock) {
-    case NOCLK:
+    DPRINTF("Clock = %d)\n", clock);
+
+    if (klass->get_clock_frequency) {
+        return klass->get_clock_frequency(dev, clock);
+    } else {
          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;
      }
-    return 0;
  }
 
  /*
   * 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 mfn = MFN(pllreg);  /* Numerator */
      uint32_t mfi = MFI(pllreg); /* Integer part */
      uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
      uint32_t pd = 1 + PD(pllreg);   /* Pre-divider */
 
+    DPRINTF("pllreg = %d, base_freq = %d)\n", pllreg, base_freq);
+
      if (mfi < 5) {
          mfi = 5;
      }
+
      /* mfn is 10-bit signed twos-complement */
      mfn <<= 32 - 10;
      mfn >>= 32 - 10;
@@ -89,178 +63,12 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t
 base_freq)
              (mfd * pd)) << 10;
  }
 
-static void update_clocks(IMXCCMState *s)
-{
-    /*
-     * If we ever emulate more clocks, this should switch to a

data-driven

hw/misc/imx_ccm.c +0 -80
-     * 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 = 0x074b0b7b;
-    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%"

hw/misc/imx_ccm.c +0 -41
-                      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%"

hw/misc/imx_ccm.c +4 -45
-                      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);
-
-    sbc->init = imx_ccm_init;
-    dc->reset = imx_ccm_reset;
-    dc->vmsd = &vmstate_imx_ccm;
-    dc->desc = "i.MX Clock Control Module";
-}
-
  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)
include/hw/arm/fsl-imx25.h +2 -2
index 73f50c6..5c62fde 100644
@@ -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];
include/hw/arm/fsl-imx31.h +2 -2
index 5e8f795..d408abb 100644
@@ -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];
include/hw/misc/imx31_ccm.h new file +7 -0
new file mode 100644
index 0000000..47be0cd
@@ -0,0 +1,64 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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/hw/misc/imx31_ccm.h new file +36 -0
+
+#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)
+
+/* 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)

include/hw/misc/imx31_ccm.h new file +19 -0
+
+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 */
include/hw/misc/imx_ccm.h +13 -44
index 09f6248..33293e8 100644
@@ -1,5 +1,5 @@
  /*
- * IMX31 Clock Control Module
+ * IMX Clock Control Module base class
   *
   * Copyright (C) 2012 NICTA
   * Updated by Jean-Christophe Dubois <[email protected]>
@@ -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,47 @@
  #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;
 
Quoted 16 lines

include/hw/misc/imx_ccm.h line 70

Quoted 13 lines
include/hw/misc/imx_ccm.h +13 -0
+typedef struct IMXCCMClass {
+    /* <private> */
+    SysBusDeviceClass parent_class;
+
+    /* <public> */
+    uint32_t (*get_clock_frequency)(DeviceState *s, IMXClk clk);
+
+} IMXCCMClass;
+
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq);
+
+void imx_ccm_set_ref_clock_frequency(DeviceState *s, uint32_t ref_freq);
+

This looks unused. If implemented will it remove the hardcoded per-implementation crystal freqs?

Quoted 8 lines

That was the idea. But for now it is not implemented so I'll remove it.

i.MX SOC seems to be designed to receive fixed/hardcoded reference clock.

There does not seem to be a real need for a variable ref clock.

OK I agree. Can revist that later. Most of the plumbing is there and it is a preservation of existing functionality.

Regards,
Peter

Quoted 23 lines
Quoted 4 lines

include/hw/misc/imx_ccm.h line 72

Quoted 3 lines
include/hw/misc/imx_ccm.h +0 -0
  uint32_t imx_ccm_get_clock_frequency(DeviceState *s, IMXClk clock);
 

The Type of the prototype should match the abstract type - IMXCCMState.

OK, I'll move to IMXCCMState

Quoted 13 lines

Otherwise,

Regards,
Peter

Quoted 4 lines
include/hw/misc/imx_ccm.h +0 -1
  #endif /* IMX_CCM_H */
--

2.5.0

#7 lore raw

PATCH v3 3/3 i.MX: Add an i.MX25 specific CCM class/instance.

In reply to Jean-Christophe Dubois

---

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

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

 hw/arm/fsl-imx25.c          |   2 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx25_ccm.c         | 243 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/misc/imx25_ccm.h |  59 +++++++++++
 5 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/imx25_ccm.c
 create mode 100644 include/hw/misc/imx25_ccm.h
hw/arm/fsl-imx25.c +1 -1
index 5526c22..0aacc91 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index c77f3e3..8a235df 100644
@@ -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
hw/misc/imx25_ccm.c new file +243 -0

243 lines not on this page · raw message

include/hw/arm/fsl-imx25.h +2 -2
index 5c62fde..d0e8e9d 100644
@@ -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];
include/hw/misc/imx25_ccm.h new file +59 -0

59 lines not on this page · raw message

Signature · 1 line
2.5.0
#8 lore raw

Re: [Qemu-devel] [PATCH v3 3/3] i.MX: Add an i.MX25 specific CCM class/instance.

Quoted 146 lines · Jean-Christophe Dubois

---

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

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

 hw/arm/fsl-imx25.c          |   2 +-
 hw/misc/Makefile.objs       |   1 +
 hw/misc/imx25_ccm.c         | 243 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx25.h  |   4 +-
 include/hw/misc/imx25_ccm.h |  59 +++++++++++
 5 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/imx25_ccm.c
 create mode 100644 include/hw/misc/imx25_ccm.h
hw/arm/fsl-imx25.c +1 -1
index 5526c22..0aacc91 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index c77f3e3..8a235df 100644
@@ -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
hw/misc/imx25_ccm.c new file +97 -0
new file mode 100644
index 0000000..a6c9bff
@@ -0,0 +1,243 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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)
+
+#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(mpctl, IMX25CCMState),
+        VMSTATE_UINT32(upctl, IMX25CCMState),
+        VMSTATE_UINT32(cctl, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(cgcr, IMX25CCMState, 3),
+        VMSTATE_UINT32_ARRAY(pcdr, IMX25CCMState, 4),
+        VMSTATE_UINT32(rcsr, IMX25CCMState),
+        VMSTATE_UINT32(crdr, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(dcvr, IMX25CCMState, 4),
+        VMSTATE_UINT32_ARRAY(ltr, IMX25CCMState, 4),
+        VMSTATE_UINT32_ARRAY(ltbr, IMX25CCMState, 2),
+        VMSTATE_UINT32_ARRAY(pmcr, IMX25CCMState, 3),
+        VMSTATE_UINT32(mcr, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(lpimr, IMX25CCMState, 2),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if (EXTRACT(s->cctl, MPLL_BYPASS)) {
+        return CKIH_FREQ;
+    } else {
+        return imx_ccm_calc_pll(s->mpctl, CKIH_FREQ);
+    }
+}
+
+static uint32_t imx25_ccm_get_upll_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx_ccm_calc_pll(s->upctl, CKIH_FREQ);
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if (EXTRACT(s->cctl, ARM_SRC)) {
+        return (imx25_ccm_get_mpll_clk(dev) * 3 / 4) /
+               (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+    } else {
+        return imx25_ccm_get_mpll_clk(dev) /
+               (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+    }
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx25_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->cctl, AHB_CLK_DIV));
+}
+

hw/misc/imx25_ccm.c line 107

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+static uint32_t imx25_ccm_get_ipg_clk(DeviceState *dev)
+{
+    DPRINTF("()\n");
+
+    return imx25_ccm_get_ahb_clk(dev) / 2;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+    DPRINTF("Clock = %d)\n", clock);

This would be more useful if it was at the end and grabbed the return value too (similar to what's commonly done for an MMIO read function).

Quoted 8 lines
hw/misc/imx25_ccm.c new file +8 -0
+
+    switch (clock) {
+    case NOCLK:
+        return 0;
+    case CLK_MPLL:
+        return imx25_ccm_get_mpll_clk(dev);
+    case CLK_UPLL:
+        return imx25_ccm_get_upll_clk(dev);

hw/misc/imx25_ccm.c line 125

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+    case CLK_MCU:
+        return imx25_ccm_get_mcu_clk(dev);
+    case CLK_AHB:
+        return imx25_ccm_get_ahb_clk(dev);
+    case CLK_IPG:
+        return imx25_ccm_get_ipg_clk(dev);
+    case CLK_32k:
+        return CKIL_FREQ;
+    default:
+        return 0;

Is this condition guest creatable? If so should it be LOG_GUEST_ERROR? If it is supposed to be unreachable even by guest error, then it should g_assert_not_reached().

Quoted 21 lines
hw/misc/imx25_ccm.c new file +21 -0
+    }
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("\n");
+
+    s->mpctl = 0x800b2c01;
+    s->upctl = 0x84002800;
+    s->cctl = 0x40030000;
+    s->cgcr[0] = 0x028A0100;
+    s->cgcr[1] = 0x04008100;
+    s->cgcr[2] = 0x00000438;
+    s->pcdr[0] = 0x01010101;
+    s->pcdr[1] = 0x01010101;
+    s->pcdr[2] = 0x01010101;
+    s->pcdr[3] = 0x01010101;
+    s->rcsr = 0;
+    s->crdr = 0;

hw/misc/imx25_ccm.c line 156

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+    s->dcvr[0] = 0;
+    s->dcvr[1] = 0;
+    s->dcvr[2] = 0;
+    s->dcvr[3] = 0;
+    s->ltr[0] = 0;
+    s->ltr[1] = 0;
+    s->ltr[2] = 0;
+    s->ltr[3] = 0;
+    s->ltbr[0] = 0;
+    s->ltbr[1] = 0;

With the union-struct approach (see below), you can memset 0 the whole thing then set the non-zeros after for a shorter result.

Quoted 6 lines
hw/misc/imx25_ccm.c new file +6 -0
+    s->pmcr[0] = 0x00A00000;
+    s->pmcr[1] = 0x0000A030;
+    s->pmcr[2] = 0x0000A030;
+    s->mcr = 0x43000000;
+    s->lpimr[0] = 0;
+    s->lpimr[1] = 0;

hw/misc/imx25_ccm.c line 172

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+
+    /* default ROM boot will change the reset values */
+    s->cctl |= INSERT(1, ARM_SRC);
+    s->cctl |= INSERT(1, AHB_CLK_DIV);
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+    uint32_t *reg = &s->mpctl;

If you want the best of both worlds for the register indexing scheme (e.g. array + set of names) an anonymous union-struct would work better. There are some in tree precedents. E.g. from hcd-ehci.h:

    union {
        uint32_t opreg[0x44/sizeof(uint32_t)];
        struct {
            uint32_t usbcmd;
            uint32_t usbsts;
            uint32_t usbintr;
            uint32_t frindex;
            uint32_t ctrldssegment;
            uint32_t periodiclistbase;
            uint32_t asynclistaddr;
            uint32_t notused[9];
            uint32_t configflag;
        };
    };

hw/misc/imx25_ccm.c line 175

Quoted 3 lines
hw/misc/imx25_ccm.c new file +3 -0
+
+    DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
+

Delay to give return value as well.

Quoted 5 lines
hw/misc/imx25_ccm.c new file +5 -0
+    if (offset < 0x70) {
+        return reg[offset >> 2];
+    } else {
+        return 0;
+    }

hw/misc/imx25_ccm.c line 190

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+    uint32_t *reg = &s->mpctl;
+
+    DPRINTF("(offset=0x%" HWADDR_PRIx ", value = %x)\n",
+            offset, (unsigned int)value);

The cast of value should be to an IP meaningful type. As reg's are 32 bit, it should be a uint32_t with PRIx32.

hw/misc/imx25_ccm.c line 195

Quoted 5 lines
hw/misc/imx25_ccm.c new file +5 -0
+
+    if (offset < 0x70) {
+        /*
+         * We will do a better implementation later. In particular some bits
+         * cannot be writen to.

"written"

hw/misc/imx25_ccm.c line 203

Quoted 8 lines
hw/misc/imx25_ccm.c new file +8 -0
+         */
+        reg[offset >> 2] = value;
+    }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+    .read = imx25_ccm_read,
+    .write = imx25_ccm_write,

Your IP doesn't handle sub-word access, so should the size restrictions be here?

We have a new sub-mailing list for ARM related work. [email protected]. You can CC to that (as well as qemu-devel) for the i.MX stuff to get a wider ARM review community.

Thanks.

Regards,
Peter

Quoted 14 lines
hw/misc/imx25_ccm.c new file +14 -0
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+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);
+}
+
#9 lore raw

Re: [Qemu-devel] [PATCH v3 3/3] i.MX: Add an i.MX25 specific CCM class/instance.

Quoted 152 lines · Peter Crosthwaite
Quoted 150 lines · Jean-Christophe Dubois

---

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

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

  hw/arm/fsl-imx25.c          |   2 +-
  hw/misc/Makefile.objs       |   1 +
  hw/misc/imx25_ccm.c         | 243 ++++++++++++++++++++++++++++++++++++++++++++
  include/hw/arm/fsl-imx25.h  |   4 +-
  include/hw/misc/imx25_ccm.h |  59 +++++++++++
  5 files changed, 306 insertions(+), 3 deletions(-)

create mode 100644 hw/misc/imx25_ccm.c
create mode 100644 include/hw/misc/imx25_ccm.h

hw/arm/fsl-imx25.c +1 -1
index 5526c22..0aacc91 100644
@@ -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++) {
hw/misc/Makefile.objs +1 -0
index c77f3e3..8a235df 100644
@@ -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
hw/misc/imx25_ccm.c new file +101 -0
new file mode 100644
index 0000000..a6c9bff
@@ -0,0 +1,243 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <[email protected]>
+ *
+ * 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)
+
+#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(mpctl, IMX25CCMState),
+        VMSTATE_UINT32(upctl, IMX25CCMState),
+        VMSTATE_UINT32(cctl, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(cgcr, IMX25CCMState, 3),
+        VMSTATE_UINT32_ARRAY(pcdr, IMX25CCMState, 4),
+        VMSTATE_UINT32(rcsr, IMX25CCMState),
+        VMSTATE_UINT32(crdr, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(dcvr, IMX25CCMState, 4),
+        VMSTATE_UINT32_ARRAY(ltr, IMX25CCMState, 4),
+        VMSTATE_UINT32_ARRAY(ltbr, IMX25CCMState, 2),
+        VMSTATE_UINT32_ARRAY(pmcr, IMX25CCMState, 3),
+        VMSTATE_UINT32(mcr, IMX25CCMState),
+        VMSTATE_UINT32_ARRAY(lpimr, IMX25CCMState, 2),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if (EXTRACT(s->cctl, MPLL_BYPASS)) {
+        return CKIH_FREQ;
+    } else {
+        return imx_ccm_calc_pll(s->mpctl, CKIH_FREQ);
+    }
+}
+
+static uint32_t imx25_ccm_get_upll_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx_ccm_calc_pll(s->upctl, CKIH_FREQ);
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    if (EXTRACT(s->cctl, ARM_SRC)) {
+        return (imx25_ccm_get_mpll_clk(dev) * 3 / 4) /
+               (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+    } else {
+        return imx25_ccm_get_mpll_clk(dev) /
+               (1 + EXTRACT(s->cctl, ARM_CLK_DIV));
+    }
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("()\n");
+
+    return imx25_ccm_get_mcu_clk(dev) / (1 + EXTRACT(s->cctl, AHB_CLK_DIV));
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(DeviceState *dev)
+{
+    DPRINTF("()\n");
+
Quoted 8 lines

hw/misc/imx25_ccm.c line 107

Quoted 6 lines
hw/misc/imx25_ccm.c new file +6 -0
+    return imx25_ccm_get_ahb_clk(dev) / 2;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(DeviceState *dev, IMXClk clock)
+{
+    DPRINTF("Clock = %d)\n", clock);

This would be more useful if it was at the end and grabbed the return value too (similar to what's commonly done for an MMIO read function).

OK

Quoted 19 lines
Quoted 9 lines
hw/misc/imx25_ccm.c new file +8 -0
+
+    switch (clock) {
+    case NOCLK:
+        return 0;
+    case CLK_MPLL:
+        return imx25_ccm_get_mpll_clk(dev);
+    case CLK_UPLL:
+        return imx25_ccm_get_upll_clk(dev);

hw/misc/imx25_ccm.c line 125

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+    case CLK_MCU:
+        return imx25_ccm_get_mcu_clk(dev);
+    case CLK_AHB:
+        return imx25_ccm_get_ahb_clk(dev);
+    case CLK_IPG:
+        return imx25_ccm_get_ipg_clk(dev);
+    case CLK_32k:
+        return CKIL_FREQ;
+    default:
+        return 0;
Quoted 3 lines

Is this condition guest creatable? If so should it be LOG_GUEST_ERROR? If it is supposed to be unreachable even by guest error, then it should g_assert_not_reached().

OK

Quoted 56 lines
Quoted 22 lines
hw/misc/imx25_ccm.c new file +21 -0
+    }
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+    IMX25CCMState *s = IMX25_CCM(dev);
+
+    DPRINTF("\n");
+
+    s->mpctl = 0x800b2c01;
+    s->upctl = 0x84002800;
+    s->cctl = 0x40030000;
+    s->cgcr[0] = 0x028A0100;
+    s->cgcr[1] = 0x04008100;
+    s->cgcr[2] = 0x00000438;
+    s->pcdr[0] = 0x01010101;
+    s->pcdr[1] = 0x01010101;
+    s->pcdr[2] = 0x01010101;
+    s->pcdr[3] = 0x01010101;
+    s->rcsr = 0;
+    s->crdr = 0;

hw/misc/imx25_ccm.c line 156

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+    s->dcvr[0] = 0;
+    s->dcvr[1] = 0;
+    s->dcvr[2] = 0;
+    s->dcvr[3] = 0;
+    s->ltr[0] = 0;
+    s->ltr[1] = 0;
+    s->ltr[2] = 0;
+    s->ltr[3] = 0;
+    s->ltbr[0] = 0;
+    s->ltbr[1] = 0;

With the union-struct approach (see below), you can memset 0 the whole thing then set the non-zeros after for a shorter result.

Quoted 6 lines
hw/misc/imx25_ccm.c new file +6 -0
+    s->pmcr[0] = 0x00A00000;
+    s->pmcr[1] = 0x0000A030;
+    s->pmcr[2] = 0x0000A030;
+    s->mcr = 0x43000000;
+    s->lpimr[0] = 0;
+    s->lpimr[1] = 0;

hw/misc/imx25_ccm.c line 172

Quoted 10 lines
hw/misc/imx25_ccm.c new file +10 -0
+
+    /* default ROM boot will change the reset values */
+    s->cctl |= INSERT(1, ARM_SRC);
+    s->cctl |= INSERT(1, AHB_CLK_DIV);
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+    uint32_t *reg = &s->mpctl;

If you want the best of both worlds for the register indexing scheme (e.g. array + set of names) an anonymous union-struct would work better. There are some in tree precedents. E.g. from hcd-ehci.h:

Quoted 14 lines
     union {
         uint32_t opreg[0x44/sizeof(uint32_t)];
         struct {
             uint32_t usbcmd;
             uint32_t usbsts;
             uint32_t usbintr;
             uint32_t frindex;
             uint32_t ctrldssegment;
             uint32_t periodiclistbase;
             uint32_t asynclistaddr;
             uint32_t notused[9];
             uint32_t configflag;
         };
     };

Well, I think I'll move later to an uint32_t array only with REG name values as index. But I'll do it for both i.MX25 and i.MX31 at the same time.

Quoted 16 lines

hw/misc/imx25_ccm.c line 175

Quoted 5 lines
hw/misc/imx25_ccm.c new file +3 -0
+
+    DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
+

Delay to give return value as well.

Quoted 9 lines
hw/misc/imx25_ccm.c new file +9 -0
+    if (offset < 0x70) {
+        return reg[offset >> 2];
+    } else {
+        return 0;
+    }
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+                            unsigned size)
Quoted 8 lines

hw/misc/imx25_ccm.c line 190

Quoted 6 lines
hw/misc/imx25_ccm.c new file +6 -0
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+    uint32_t *reg = &s->mpctl;
+
+    DPRINTF("(offset=0x%" HWADDR_PRIx ", value = %x)\n",
+            offset, (unsigned int)value);

The cast of value should be to an IP meaningful type. As reg's are 32 bit, it should be a uint32_t with PRIx32.

OK

Quoted 18 lines

hw/misc/imx25_ccm.c line 195

Quoted 6 lines
hw/misc/imx25_ccm.c new file +5 -0
+
+    if (offset < 0x70) {
+        /*
+         * We will do a better implementation later. In particular some bits
+         * cannot be writen to.

"written"

hw/misc/imx25_ccm.c line 203

Quoted 8 lines
hw/misc/imx25_ccm.c new file +8 -0
+         */
+        reg[offset >> 2] = value;
+    }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+    .read = imx25_ccm_read,
+    .write = imx25_ccm_write,

Your IP doesn't handle sub-word access, so should the size restrictions be here?

Quoted 3 lines

We have a new sub-mailing list for ARM related work. [email protected]. You can CC to that (as well as qemu-devel) for the i.MX stuff to get a wider ARM review community.

OK

Quoted 20 lines

Thanks.

Regards,
Peter

Quoted 14 lines
hw/misc/imx25_ccm.c new file +14 -0
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+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);
+}
+
#10 lore raw

Re: [Qemu-devel] [PATCH v3 3/3] i.MX: Add an i.MX25 specific CCM class/instance.

Quoted 60 lines · Jean-Christophe DUBOIS
Quoted 43 lines · Peter Crosthwaite
Quoted 32 lines · Jean-Christophe Dubois

---

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

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

  hw/arm/fsl-imx25.c          |   2 +-
  hw/misc/Makefile.objs       |   1 +
  hw/misc/imx25_ccm.c         | 243
++++++++++++++++++++++++++++++++++++++++++++
  include/hw/arm/fsl-imx25.h  |   4 +-
  include/hw/misc/imx25_ccm.h |  59 +++++++++++
  5 files changed, 306 insertions(+), 3 deletions(-)

create mode 100644 hw/misc/imx25_ccm.c
create mode 100644 include/hw/misc/imx25_ccm.h

hw/misc/imx25_ccm.c new file +10 -0
+    s->mcr = 0x43000000;
+    s->lpimr[0] = 0;
+    s->lpimr[1] = 0;
+
+    /* default ROM boot will change the reset values */
+    s->cctl |= INSERT(1, ARM_SRC);
+    s->cctl |= INSERT(1, AHB_CLK_DIV);
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned

size)

hw/misc/imx25_ccm.c line 172

Quoted 3 lines
hw/misc/imx25_ccm.c new file +3 -0
+{
+    IMX25CCMState *s = (IMX25CCMState *)opaque;
+    uint32_t *reg = &s->mpctl;

If you want the best of both worlds for the register indexing scheme (e.g. array + set of names) an anonymous union-struct would work better. There are some in tree precedents. E.g. from hcd-ehci.h:

Quoted 14 lines
     union {
         uint32_t opreg[0x44/sizeof(uint32_t)];
         struct {
             uint32_t usbcmd;
             uint32_t usbsts;
             uint32_t usbintr;
             uint32_t frindex;
             uint32_t ctrldssegment;
             uint32_t periodiclistbase;
             uint32_t asynclistaddr;
             uint32_t notused[9];
             uint32_t configflag;
         };
     };
Quoted 4 lines

Well, I think I'll move later to an uint32_t array only with REG name values as index.
But I'll do it for both i.MX25 and i.MX31 at the same time.

So I am having a look at 31, and it seems the register map tightly packed (suitable for arrayification), however the original model only chooses to define a handful a registers interspersed through the memory region. I have a two patch series that adds the missing registers and converts i.MX31 CCM to union-struct. I have tested this, at least booting Linux as far as:

[   20.934484] VFS: Cannot open root device ...
[   20.935476] Please append a correct "root=" boot option; here are

the available partitions:

One problem with cleaning this up later is conversion to array changes the fields as known to VMSD requiring a version bump. I think it is better to lead out with arrayification for i.MX25 if that is the long term solution. My patches for 31 are here:

https://github.com/pcrost/qemu/tree/imx-ccm.next

Regards,
Peter