Add exynos4210 Clock Management Units emulation

Signed-off-by: Maksim Kozlov <m.koz...@samsung.com>
---
 hw/arm/Makefile.objs |    1 +
 hw/exynos4210.c      |   16 +
 hw/exynos4210.h      |   42 ++
 hw/exynos4210_cmu.c  | 1464 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1523 insertions(+), 0 deletions(-)
 create mode 100644 hw/exynos4210_cmu.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 88ff47d..20d19f2 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -11,6 +11,7 @@ obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o 
a9mpcore.o
 obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
 obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
+obj-y += exynos4210_cmu.o
 obj-y += arm_l2x0.o
 obj-y += arm_mptimer.o a15mpcore.o
 obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 9c20b3f..55b8e24 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -30,6 +30,13 @@
 
 #define EXYNOS4210_CHIPID_ADDR         0x10000000
 
+/* CMUs */
+#define EXYNOS4210_CMU_LEFTBUS_BASE_ADDR     0x10034000
+#define EXYNOS4210_CMU_RIGHTBUS_BASE_ADDR    0x10038000
+#define EXYNOS4210_CMU_TOP_BASE_ADDR         0x1003C000
+#define EXYNOS4210_CMU_DMC_BASE_ADDR         0x10040000
+#define EXYNOS4210_CMU_CPU_BASE_ADDR         0x10044000
+
 /* PWM */
 #define EXYNOS4210_PWM_BASE_ADDR       0x139D0000
 
@@ -250,6 +257,15 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
     */
     sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
 
+    /* CMUs */
+    exynos4210_cmu_create(EXYNOS4210_CMU_LEFTBUS_BASE_ADDR,
+                                                       EXYNOS4210_CMU_LEFTBUS);
+    exynos4210_cmu_create(EXYNOS4210_CMU_RIGHTBUS_BASE_ADDR,
+                                                      EXYNOS4210_CMU_RIGHTBUS);
+    exynos4210_cmu_create(EXYNOS4210_CMU_TOP_BASE_ADDR, EXYNOS4210_CMU_TOP);
+    exynos4210_cmu_create(EXYNOS4210_CMU_DMC_BASE_ADDR, EXYNOS4210_CMU_DMC);
+    exynos4210_cmu_create(EXYNOS4210_CMU_CPU_BASE_ADDR, EXYNOS4210_CMU_CPU);
+
     /* PWM */
     sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
                           s->irq_table[exynos4210_get_irq(22, 0)],
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index 9b1ae4c..fbdff7a 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -123,6 +123,48 @@ void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, 
DeviceState *dev,
         int ext);
 
 /*
+ * Interface for exynos4210 Clock Management Units (CMUs)
+ */
+typedef enum {
+    UNSPECIFIED_CMU = -1,
+    EXYNOS4210_CMU_LEFTBUS,
+    EXYNOS4210_CMU_RIGHTBUS,
+    EXYNOS4210_CMU_TOP,
+    EXYNOS4210_CMU_DMC,
+    EXYNOS4210_CMU_CPU,
+    EXYNOS4210_CMU_NUMBER
+} Exynos4210Cmu;
+
+typedef enum {
+    UNSPECIFIED_CLOCK,
+    EXYNOS4210_XXTI,
+    EXYNOS4210_XUSBXTI,
+    EXYNOS4210_APLL,
+    EXYNOS4210_MPLL,
+    EXYNOS4210_SCLK_HDMI24M,
+    EXYNOS4210_SCLK_USBPHY0,
+    EXYNOS4210_SCLK_USBPHY1,
+    EXYNOS4210_SCLK_HDMIPHY,
+    EXYNOS4210_SCLK_APLL,
+    EXYNOS4210_SCLK_MPLL,
+    EXYNOS4210_ACLK_100,
+    EXYNOS4210_SCLK_UART0,
+    EXYNOS4210_SCLK_UART1,
+    EXYNOS4210_SCLK_UART2,
+    EXYNOS4210_SCLK_UART3,
+    EXYNOS4210_SCLK_UART4,
+    EXYNOS4210_CLOCKS_NUMBER
+} Exynos4210Clock;
+
+typedef void ClockChangeHandler(void *opaque);
+
+DeviceState *exynos4210_cmu_create(target_phys_addr_t addr, Exynos4210Cmu cmu);
+uint64_t exynos4210_cmu_get_rate(Exynos4210Clock clock_id);
+void exynos4210_register_clock_handler(ClockChangeHandler *func,
+                                       Exynos4210Clock clock_id,
+                                       void *opaque);
+
+/*
  * exynos4210 UART
  */
 DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
diff --git a/hw/exynos4210_cmu.c b/hw/exynos4210_cmu.c
new file mode 100644
index 0000000..792a90b
--- /dev/null
+++ b/hw/exynos4210_cmu.c
@@ -0,0 +1,1464 @@
+/*
+ *  exynos4210 Clock Management Units (CMUs) Emulation
+ *
+ *  Copyright (C) 2011 Samsung Electronics Co Ltd.
+ *    Maksim Kozlov, <m.koz...@samsung.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysbus.h"
+
+#include "exynos4210.h"
+
+
+#define DEBUG_CMU         1
+#define DEBUG_CMU_EXTEND  0
+
+#if DEBUG_CMU || DEBUG_CMU_EXTEND
+
+    #define  PRINT_DEBUG(fmt, args...)  \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+    #define  PRINT_DEBUG_SIMPLE(fmt, args...)  \
+        do { \
+            fprintf(stderr, fmt, ## args); \
+        } while (0)
+
+#if DEBUG_CMU_EXTEND
+
+    #define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+#else
+    #define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do {} while (0)
+#endif /* EXTEND */
+
+#else
+    #define  PRINT_DEBUG(fmt, args...) \
+        do {} while (0)
+    #define  PRINT_DEBUG_SIMPLE(fmt, args...) \
+        do {} while (0)
+    #define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do {} while (0)
+#endif
+
+#define  PRINT_ERROR(fmt, args...)                                          \
+        do {                                                                \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+
+
+/* function blocks */
+#define LEFTBUS_BLK   0x0
+#define RIGHTBUS_BLK  0x0
+#define TOP_BLK       0x10
+#define TOP0_BLK      0x10
+#define TOP1_BLK      0x14
+#define DMC_BLK       0x0
+#define DMC0_BLK      0x0
+#define DMC1_BLK      0x4
+#define CPU_BLK       0x0
+#define CPU0_BLK      0x0
+#define CPU1_BLK      0x4
+
+#define CAM_BLK       0x20
+#define TV_BLK        0x24
+#define MFC_BLK       0x28
+#define G3D_BLK       0x2C
+#define IMAGE_BLK     0x30
+#define LCD0_BLK      0x34
+#define LCD1_BLK      0x38
+#define MAUDIO_BLK    0x3C
+#define FSYS_BLK      0x40
+#define FSYS0_BLK     0x40
+#define FSYS1_BLK     0x44
+#define FSYS2_BLK     0x48
+#define FSYS3_BLK     0x4C
+#define GPS_BLK       0x4C /*  CLK_GATE_IP_GPS in CMU_TOP */
+#define PERIL_BLK     0x50
+#define PERIL0_BLK    0x50
+#define PERIL1_BLK    0x54
+#define PERIL2_BLK    0x58
+#define PERIL3_BLK    0x5C
+#define PERIL4_BLK    0x60
+#define PERIR_BLK     0x60 /* CLK_GATE_IP_PERIR in CMU_TOP */
+#define PERIL5_BLK    0x64
+
+#define BLOCK_MASK    0xFF
+
+/* PLLs */
+/* located in CMU_CPU block */
+#define APLL  0x00
+#define MPLL  0x08
+/* located in CMU_TOP block */
+#define EPLL  0x10
+#define VPLL  0x20
+
+/* groups of registers */
+#define PLL_LOCK       0x000
+#define PLL_CON        0x100
+#define CLK_SRC        0x200
+#define CLK_SRC_MASK   0x300
+#define CLK_MUX_STAT   0x400
+#define CLK_DIV        0x500
+#define CLK_DIV_STAT   0x600
+#define CLK_GATE_SCLK  0x800
+#define CLK_GATE_IP    0x900
+
+#define GROUP_MASK     0xF00
+
+#define PLL_LOCK_(pll)  (PLL_LOCK + pll)
+#define PLL_CON0_(pll)  (PLL_CON  + pll)
+#define PLL_CON1_(pll)  (PLL_CON  + pll + 4)
+
+#define CLK_SRC_(block)         (CLK_SRC       + block)
+#define CLK_SRC_MASK_(block)    (CLK_SRC_MASK  + block)
+#define CLK_MUX_STAT_(block)    (CLK_MUX_STAT  + block)
+#define CLK_DIV_(block)         (CLK_DIV       + block)
+#define CLKDIV2_RATIO           0x580 /* described for CMU_TOP only */
+#define CLK_DIV_STAT_(block)    (CLK_DIV_STAT  + block)
+#define CLKDIV2_STAT            0x680 /* described for CMU_TOP only */
+#define CLK_GATE_SCLK_(block)   (CLK_GATE_SCLK + block)
+/* For CMU_LEFTBUS and CMU_RIGHTBUS, CLK_GATE_IP_XXX
+   registers are located at 0x800. */
+#define CLK_GATE_IP_LR_BUS      0x800
+#define CLK_GATE_IP_(block)     (CLK_GATE_IP + block)
+#define CLK_GATE_BLOCK          0x970 /* described for CMU_TOP only */
+#define CLKOUT_CMU              0xA00
+#define CLKOUT_CMU_DIV_STAT     0xA04
+
+/*
+ * registers which are located outside of 0xAFF region
+ */
+/* CMU_DMC */
+#define DCGIDX_MAP0        0x01000
+#define DCGIDX_MAP1        0x01004
+#define DCGIDX_MAP2        0x01008
+#define DCGPERF_MAP0       0x01020
+#define DCGPERF_MAP1       0x01024
+#define DVCIDX_MAP         0x01040
+#define FREQ_CPU           0x01060
+#define FREQ_DPM           0x01064
+#define DVSEMCLK_EN        0x01080
+#define MAXPERF            0x01084
+/* CMU_CPU */
+#define ARMCLK_STOPCTRL    0x01000
+#define ATCLK_STOPCTRL     0x01004
+#define PARITYFAIL_STATUS  0x01010
+#define PARITYFAIL_CLEAR   0x01014
+#define PWR_CTRL           0x01020
+#define APLL_CON0_L8       0x01100
+#define APLL_CON0_L7       0x01104
+#define APLL_CON0_L6       0x01108
+#define APLL_CON0_L5       0x0110C
+#define APLL_CON0_L4       0x01110
+#define APLL_CON0_L3       0x01114
+#define APLL_CON0_L2       0x01118
+#define APLL_CON0_L1       0x0111C
+#define IEM_CONTROL        0x01120
+#define APLL_CON1_L8       0x01200
+#define APLL_CON1_L7       0x01204
+#define APLL_CON1_L6       0x01208
+#define APLL_CON1_L5       0x0120C
+#define APLL_CON1_L4       0x01210
+#define APLL_CON1_L3       0x01214
+#define APLL_CON1_L2       0x01218
+#define APLL_CON1_L1       0x0121C
+#define CLKDIV_IEM_L8      0x01300
+#define CLKDIV_IEM_L7      0x01304
+#define CLKDIV_IEM_L6      0x01308
+#define CLKDIV_IEM_L5      0x0130C
+#define CLKDIV_IEM_L4      0x01310
+#define CLKDIV_IEM_L3      0x01314
+#define CLKDIV_IEM_L2      0x01318
+#define CLKDIV_IEM_L1      0x0131C
+
+#define EXTENDED_REGION_MASK    0x1000
+#define EXTENDED_REGISTER_MASK  0xFFF
+
+typedef struct {
+    const char *name; /* for debugging */
+    uint32_t    offset;
+    uint32_t    reset_value;
+} Exynos4210CmuReg;
+
+
+static const Exynos4210CmuReg exynos4210_cmu_leftbus_regs[] = {
+    /* CMU_LEFTBUS registers */
+    { "CLK_SRC_LEFTBUS",             CLK_SRC_(LEFTBUS_BLK),        0x00000000 
},
+    { "CLK_MUX_STAT_LEFTBUS",        CLK_MUX_STAT_(LEFTBUS_BLK),   0x00000001 
},
+    { "CLK_DIV_LEFTBUS",             CLK_DIV_(LEFTBUS_BLK),        0x00000000 
},
+    { "CLK_DIV_STAT_LEFTBUS",        CLK_DIV_STAT_(LEFTBUS_BLK),   0x00000000 
},
+    { "CLK_GATE_IP_LEFTBUS",         CLK_GATE_IP_LR_BUS,           0xFFFFFFFF 
},
+    { "CLKOUT_CMU_LEFTBUS",          CLKOUT_CMU,                   0x00010000 
},
+    { "CLKOUT_CMU_LEFTBUS_DIV_STAT", CLKOUT_CMU_DIV_STAT,          0x00000000 
},
+};
+
+static const Exynos4210CmuReg exynos4210_cmu_rightbus_regs[] = {
+    /* CMU_RIGHTBUS registers */
+    { "CLK_SRC_RIGHTBUS",             CLK_SRC_(RIGHTBUS_BLK),      0x00000000 
},
+    { "CLK_MUX_STAT_RIGHTBUS",        CLK_MUX_STAT_(RIGHTBUS_BLK), 0x00000001 
},
+    { "CLK_DIV_RIGHTBUS",             CLK_DIV_(RIGHTBUS_BLK),      0x00000000 
},
+    { "CLK_DIV_STAT_RIGHTBUS",        CLK_DIV_STAT_(RIGHTBUS_BLK), 0x00000000 
},
+    { "CLK_GATE_IP_RIGHTBUS",         CLK_GATE_IP_LR_BUS,          0xFFFFFFFF 
},
+    { "CLKOUT_CMU_RIGHTBUS",          CLKOUT_CMU,                  0x00010000 
},
+    { "CLKOUT_CMU_RIGHTBUS_DIV_STAT", CLKOUT_CMU_DIV_STAT,         0x00000000 
},
+};
+
+static const Exynos4210CmuReg exynos4210_cmu_top_regs[] = {
+    /* CMU_TOP registers */
+    { "EPLL_LOCK",               PLL_LOCK_(EPLL),              0x00000FFF },
+    { "VPLL_LOCK",               PLL_LOCK_(VPLL),              0x00000FFF },
+    { "EPLL_CON0",               PLL_CON0_(EPLL),              0x00300301 },
+    { "EPLL_CON1",               PLL_CON1_(EPLL),              0x00000000 },
+    { "VPLL_CON0",               PLL_CON0_(VPLL),              0x00240201 },
+    { "VPLL_CON1",               PLL_CON1_(VPLL),              0x66010464 },
+    { "CLK_SRC_TOP0",            CLK_SRC_(TOP0_BLK),           0x00000000 },
+    { "CLK_SRC_TOP1",            CLK_SRC_(TOP1_BLK),           0x00000000 },
+    { "CLK_SRC_CAM",             CLK_SRC_(CAM_BLK),            0x11111111 },
+    { "CLK_SRC_TV",              CLK_SRC_(TV_BLK),             0x00000000 },
+    { "CLK_SRC_MFC",             CLK_SRC_(MFC_BLK),            0x00000000 },
+    { "CLK_SRC_G3D",             CLK_SRC_(G3D_BLK),            0x00000000 },
+    { "CLK_SRC_IMAGE",           CLK_SRC_(IMAGE_BLK),          0x00000000 },
+    { "CLK_SRC_LCD0",            CLK_SRC_(LCD0_BLK),           0x00001111 },
+    { "CLK_SRC_LCD1",            CLK_SRC_(LCD1_BLK),           0x00001111 },
+    { "CLK_SRC_MAUDIO",          CLK_SRC_(MAUDIO_BLK),         0x00000005 },
+    { "CLK_SRC_FSYS",            CLK_SRC_(FSYS_BLK),           0x00011111 },
+    { "CLK_SRC_PERIL0",          CLK_SRC_(PERIL0_BLK),         0x00011111 },
+    { "CLK_SRC_PERIL1",          CLK_SRC_(PERIL1_BLK),         0x01110055 },
+    { "CLK_SRC_MASK_TOP",        CLK_SRC_MASK_(TOP_BLK),       0x00000001 },
+    { "CLK_SRC_MASK_CAM",        CLK_SRC_MASK_(CAM_BLK),       0x11111111 },
+    { "CLK_SRC_MASK_TV",         CLK_SRC_MASK_(TV_BLK),        0x00000111 },
+    { "CLK_SRC_MASK_LCD0",       CLK_SRC_MASK_(LCD0_BLK),      0x00001111 },
+    { "CLK_SRC_MASK_LCD1",       CLK_SRC_MASK_(LCD1_BLK),      0x00001111 },
+    { "CLK_SRC_MASK_MAUDIO",     CLK_SRC_MASK_(MAUDIO_BLK),    0x00000001 },
+    { "CLK_SRC_MASK_FSYS",       CLK_SRC_MASK_(FSYS_BLK),      0x01011111 },
+    { "CLK_SRC_MASK_PERIL0",     CLK_SRC_MASK_(PERIL0_BLK),    0x00011111 },
+    { "CLK_SRC_MASK_PERIL1",     CLK_SRC_MASK_(PERIL1_BLK),    0x01110111 },
+    { "CLK_MUX_STAT_TOP",        CLK_MUX_STAT_(TOP_BLK),       0x11111111 },
+    { "CLK_MUX_STAT_MFC",        CLK_MUX_STAT_(MFC_BLK),       0x00000111 },
+    { "CLK_MUX_STAT_G3D",        CLK_MUX_STAT_(G3D_BLK),       0x00000111 },
+    { "CLK_MUX_STAT_IMAGE",      CLK_MUX_STAT_(IMAGE_BLK),     0x00000111 },
+    { "CLK_DIV_TOP",             CLK_DIV_(TOP_BLK),            0x00000000 },
+    { "CLK_DIV_CAM",             CLK_DIV_(CAM_BLK),            0x00000000 },
+    { "CLK_DIV_TV",              CLK_DIV_(TV_BLK),             0x00000000 },
+    { "CLK_DIV_MFC",             CLK_DIV_(MFC_BLK),            0x00000000 },
+    { "CLK_DIV_G3D",             CLK_DIV_(G3D_BLK),            0x00000000 },
+    { "CLK_DIV_IMAGE",           CLK_DIV_(IMAGE_BLK),          0x00000000 },
+    { "CLK_DIV_LCD0",            CLK_DIV_(LCD0_BLK),           0x00700000 },
+    { "CLK_DIV_LCD1",            CLK_DIV_(LCD1_BLK),           0x00700000 },
+    { "CLK_DIV_MAUDIO",          CLK_DIV_(MAUDIO_BLK),         0x00000000 },
+    { "CLK_DIV_FSYS0",           CLK_DIV_(FSYS0_BLK),          0x00B00000 },
+    { "CLK_DIV_FSYS1",           CLK_DIV_(FSYS1_BLK),          0x00000000 },
+    { "CLK_DIV_FSYS2",           CLK_DIV_(FSYS2_BLK),          0x00000000 },
+    { "CLK_DIV_FSYS3",           CLK_DIV_(FSYS3_BLK),          0x00000000 },
+    { "CLK_DIV_PERIL0",          CLK_DIV_(PERIL0_BLK),         0x00000000 },
+    { "CLK_DIV_PERIL1",          CLK_DIV_(PERIL1_BLK),         0x00000000 },
+    { "CLK_DIV_PERIL2",          CLK_DIV_(PERIL2_BLK),         0x00000000 },
+    { "CLK_DIV_PERIL3",          CLK_DIV_(PERIL3_BLK),         0x00000000 },
+    { "CLK_DIV_PERIL4",          CLK_DIV_(PERIL4_BLK),         0x00000000 },
+    { "CLK_DIV_PERIL5",          CLK_DIV_(PERIL5_BLK),         0x00000000 },
+    { "CLKDIV2_RATIO",           CLKDIV2_RATIO,                0x11111111 },
+    { "CLK_DIV_STAT_TOP",        CLK_DIV_STAT_(TOP_BLK),       0x00000000 },
+    { "CLK_DIV_STAT_CAM",        CLK_DIV_STAT_(CAM_BLK),       0x00000000 },
+    { "CLK_DIV_STAT_TV",         CLK_DIV_STAT_(TV_BLK),        0x00000000 },
+    { "CLK_DIV_STAT_MFC",        CLK_DIV_STAT_(MFC_BLK),       0x00000000 },
+    { "CLK_DIV_STAT_G3D",        CLK_DIV_STAT_(G3D_BLK),       0x00000000 },
+    { "CLK_DIV_STAT_IMAGE",      CLK_DIV_STAT_(IMAGE_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_LCD0",       CLK_DIV_STAT_(LCD0_BLK),      0x00000000 },
+    { "CLK_DIV_STAT_LCD1",       CLK_DIV_STAT_(LCD1_BLK),      0x00000000 },
+    { "CLK_DIV_STAT_MAUDIO",     CLK_DIV_STAT_(MAUDIO_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_FSYS0",      CLK_DIV_STAT_(FSYS0_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_FSYS1",      CLK_DIV_STAT_(FSYS1_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_FSYS2",      CLK_DIV_STAT_(FSYS2_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_FSYS3",      CLK_DIV_STAT_(FSYS3_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_PERIL0",     CLK_DIV_STAT_(PERIL0_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_PERIL1",     CLK_DIV_STAT_(PERIL1_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_PERIL2",     CLK_DIV_STAT_(PERIL2_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_PERIL3",     CLK_DIV_STAT_(PERIL3_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_PERIL4",     CLK_DIV_STAT_(PERIL4_BLK),    0x00000000 },
+    { "CLK_DIV_STAT_PERIL5",     CLK_DIV_STAT_(PERIL5_BLK),    0x00000000 },
+    { "CLKDIV2_STAT",            CLKDIV2_STAT,                 0x00000000 },
+    { "CLK_GATE_SCLK_CAM",       CLK_GATE_SCLK_(CAM_BLK),      0xFFFFFFFF },
+    { "CLK_GATE_IP_CAM",         CLK_GATE_IP_(CAM_BLK),        0xFFFFFFFF },
+    { "CLK_GATE_IP_TV",          CLK_GATE_IP_(TV_BLK),         0xFFFFFFFF },
+    { "CLK_GATE_IP_MFC",         CLK_GATE_IP_(MFC_BLK),        0xFFFFFFFF },
+    { "CLK_GATE_IP_G3D",         CLK_GATE_IP_(G3D_BLK),        0xFFFFFFFF },
+    { "CLK_GATE_IP_IMAGE",       CLK_GATE_IP_(IMAGE_BLK),      0xFFFFFFFF },
+    { "CLK_GATE_IP_LCD0",        CLK_GATE_IP_(LCD0_BLK),       0xFFFFFFFF },
+    { "CLK_GATE_IP_LCD1",        CLK_GATE_IP_(LCD1_BLK),       0xFFFFFFFF },
+    { "CLK_GATE_IP_FSYS",        CLK_GATE_IP_(FSYS_BLK),       0xFFFFFFFF },
+    { "CLK_GATE_IP_GPS",         CLK_GATE_IP_(GPS_BLK),        0xFFFFFFFF },
+    { "CLK_GATE_IP_PERIL",       CLK_GATE_IP_(PERIL_BLK),      0xFFFFFFFF },
+    { "CLK_GATE_IP_PERIR",       CLK_GATE_IP_(PERIR_BLK),      0xFFFFFFFF },
+    { "CLK_GATE_BLOCK",          CLK_GATE_BLOCK,               0xFFFFFFFF },
+    { "CLKOUT_CMU_TOP",          CLKOUT_CMU,                   0x00010000 },
+    { "CLKOUT_CMU_TOP_DIV_STAT", CLKOUT_CMU_DIV_STAT,          0x00000000 },
+};
+
+static const Exynos4210CmuReg exynos4210_cmu_dmc_regs[] = {
+    /* CMU_DMC registers */
+    { "CLK_SRC_DMC",             CLK_SRC_(DMC_BLK),            0x00010000 },
+    { "CLK_SRC_MASK_DMC",        CLK_SRC_MASK_(DMC_BLK),       0x00010000 },
+    { "CLK_MUX_STAT_DMC",        CLK_MUX_STAT_(DMC_BLK),       0x11100110 },
+    { "CLK_DIV_DMC0",            CLK_DIV_(DMC0_BLK),           0x00000000 },
+    { "CLK_DIV_DMC1",            CLK_DIV_(DMC1_BLK),           0x00000000 },
+    { "CLK_DIV_STAT_DMC0",       CLK_DIV_STAT_(DMC0_BLK),      0x00000000 },
+    { "CLK_DIV_STAT_DMC1",       CLK_DIV_STAT_(DMC1_BLK),      0x00000000 },
+    { "CLK_GATE_IP_DMC",         CLK_GATE_IP_(DMC_BLK),        0xFFFFFFFF },
+    { "CLKOUT_CMU_DMC",          CLKOUT_CMU,                   0x00010000 },
+    { "CLKOUT_CMU_DMC_DIV_STAT", CLKOUT_CMU_DIV_STAT,          0x00000000 },
+    { "DCGIDX_MAP0",             DCGIDX_MAP0,                  0xFFFFFFFF },
+    { "DCGIDX_MAP1",             DCGIDX_MAP1,                  0xFFFFFFFF },
+    { "DCGIDX_MAP2",             DCGIDX_MAP2,                  0xFFFFFFFF },
+    { "DCGPERF_MAP0",            DCGPERF_MAP0,                 0xFFFFFFFF },
+    { "DCGPERF_MAP1",            DCGPERF_MAP1,                 0xFFFFFFFF },
+    { "DVCIDX_MAP",              DVCIDX_MAP,                   0xFFFFFFFF },
+    { "FREQ_CPU",                FREQ_CPU,                     0x00000000 },
+    { "FREQ_DPM",                FREQ_DPM,                     0x00000000 },
+    { "DVSEMCLK_EN",             DVSEMCLK_EN,                  0x00000000 },
+    { "MAXPERF",                 MAXPERF,                      0x00000000 },
+};
+
+static const Exynos4210CmuReg exynos4210_cmu_cpu_regs[] = {
+    /* CMU_CPU registers */
+    { "APLL_LOCK",               PLL_LOCK_(APLL),             0x00000FFF },
+    { "MPLL_LOCK",               PLL_LOCK_(MPLL),             0x00000FFF },
+    { "APLL_CON0",               PLL_CON0_(APLL),             0x00C80601 },
+    { "APLL_CON1",               PLL_CON1_(APLL),             0x0000001C },
+    { "MPLL_CON0",               PLL_CON0_(MPLL),             0x00C80601 },
+    { "MPLL_CON1",               PLL_CON1_(MPLL),             0x0000001C },
+    { "CLK_SRC_CPU",             CLK_SRC_(CPU_BLK),           0x00000000 },
+    { "CLK_MUX_STAT_CPU",        CLK_MUX_STAT_(CPU_BLK),      0x00110101 },
+    { "CLK_DIV_CPU0",            CLK_DIV_(CPU0_BLK),          0x00000000 },
+    { "CLK_DIV_CPU1",            CLK_DIV_(CPU1_BLK),          0x00000000 },
+    { "CLK_DIV_STAT_CPU0",       CLK_DIV_STAT_(CPU0_BLK),     0x00000000 },
+    { "CLK_DIV_STAT_CPU1",       CLK_DIV_STAT_(CPU1_BLK),     0x00000000 },
+    { "CLK_GATE_SCLK_CPU",       CLK_GATE_SCLK_(CPU_BLK),     0xFFFFFFFF },
+    { "CLK_GATE_IP_CPU",         CLK_GATE_IP_(CPU_BLK),       0xFFFFFFFF },
+    { "CLKOUT_CMU_CPU",          CLKOUT_CMU,                  0x00010000 },
+    { "CLKOUT_CMU_CPU_DIV_STAT", CLKOUT_CMU_DIV_STAT,         0x00000000 },
+    { "ARMCLK_STOPCTRL",         ARMCLK_STOPCTRL,             0x00000044 },
+    { "ATCLK_STOPCTRL",          ATCLK_STOPCTRL,              0x00000044 },
+    { "PARITYFAIL_STATUS",       PARITYFAIL_STATUS,           0x00000000 },
+    { "PARITYFAIL_CLEAR",        PARITYFAIL_CLEAR,            0x00000000 },
+    { "PWR_CTRL",                PWR_CTRL,                    0x00000033 },
+    { "APLL_CON0_L8",            APLL_CON0_L8,                0x00C80601 },
+    { "APLL_CON0_L7",            APLL_CON0_L7,                0x00C80601 },
+    { "APLL_CON0_L6",            APLL_CON0_L6,                0x00C80601 },
+    { "APLL_CON0_L5",            APLL_CON0_L5,                0x00C80601 },
+    { "APLL_CON0_L4",            APLL_CON0_L4,                0x00C80601 },
+    { "APLL_CON0_L3",            APLL_CON0_L3,                0x00C80601 },
+    { "APLL_CON0_L2",            APLL_CON0_L2,                0x00C80601 },
+    { "APLL_CON0_L1",            APLL_CON0_L1,                0x00C80601 },
+    { "IEM_CONTROL",             IEM_CONTROL,                 0x00000000 },
+    { "APLL_CON1_L8",            APLL_CON1_L8,                0x00000000 },
+    { "APLL_CON1_L7",            APLL_CON1_L7,                0x00000000 },
+    { "APLL_CON1_L6",            APLL_CON1_L6,                0x00000000 },
+    { "APLL_CON1_L5",            APLL_CON1_L5,                0x00000000 },
+    { "APLL_CON1_L4",            APLL_CON1_L4,                0x00000000 },
+    { "APLL_CON1_L3",            APLL_CON1_L3,                0x00000000 },
+    { "APLL_CON1_L2",            APLL_CON1_L2,                0x00000000 },
+    { "APLL_CON1_L1",            APLL_CON1_L1,                0x00000000 },
+    { "CLKDIV_IEM_L8",           CLKDIV_IEM_L8,               0x00000000 },
+    { "CLKDIV_IEM_L7",           CLKDIV_IEM_L7,               0x00000000 },
+    { "CLKDIV_IEM_L6",           CLKDIV_IEM_L6,               0x00000000 },
+    { "CLKDIV_IEM_L5",           CLKDIV_IEM_L5,               0x00000000 },
+    { "CLKDIV_IEM_L4",           CLKDIV_IEM_L4,               0x00000000 },
+    { "CLKDIV_IEM_L3",           CLKDIV_IEM_L3,               0x00000000 },
+    { "CLKDIV_IEM_L2",           CLKDIV_IEM_L2,               0x00000000 },
+    { "CLKDIV_IEM_L1",           CLKDIV_IEM_L1,               0x00000000 },
+};
+
+#define EXYNOS4210_CMU_REGS_MEM_SIZE     0x4000
+
+/*
+ * for indexing register in the uint32_t array
+ *
+ * 'reg' - register offset (see offsets definitions above)
+ *
+ */
+#define I_(reg) (reg / sizeof(uint32_t))
+
+#define XOM_0 1 /* Select XXTI (0) or XUSBXTI (1) base clock source */
+
+/*
+ *  Offsets in CLK_SRC_CPU register
+ *  for control MUXMPLL and MUXAPLL
+ *
+ *  0 = FINPLL, 1 = MOUTM(A)PLLFOUT
+ */
+#define MUX_APLL_SEL_SHIFT 0
+#define MUX_MPLL_SEL_SHIFT 8
+#define MUX_CORE_SEL_SHIFT 16
+#define MUX_HPM_SEL_SHIFT  20
+
+#define MUX_APLL_SEL  (1 << MUX_APLL_SEL_SHIFT)
+#define MUX_MPLL_SEL  (1 << MUX_MPLL_SEL_SHIFT)
+#define MUX_CORE_SEL  (1 << MUX_CORE_SEL_SHIFT)
+#define MUX_HPM_SEL   (1 << MUX_HPM_SEL_SHIFT)
+
+/* Offsets for fields in CLK_MUX_STAT_CPU register */
+#define APLL_SEL_SHIFT         0
+#define APLL_SEL_MASK          0x00000007
+#define MPLL_SEL_SHIFT         8
+#define MPLL_SEL_MASK          0x00000700
+#define CORE_SEL_SHIFT         16
+#define CORE_SEL_MASK          0x00070000
+#define HPM_SEL_SHIFT          20
+#define HPM_SEL_MASK           0x00700000
+
+
+/* Offsets for fields in <pll>_CON0 register */
+#define PLL_ENABLE_SHIFT 31
+#define PLL_ENABLE_MASK  0x80000000 /* [31] bit */
+#define PLL_LOCKED_MASK  0x20000000 /* [29] bit */
+#define PLL_MDIV_SHIFT   16
+#define PLL_MDIV_MASK    0x03FF0000 /* [25:16] bits */
+#define PLL_PDIV_SHIFT   8
+#define PLL_PDIV_MASK    0x00003F00 /* [13:8] bits */
+#define PLL_SDIV_SHIFT   0
+#define PLL_SDIV_MASK    0x00000007 /* [2:0] bits */
+
+/*
+ *  Offset in CLK_DIV_CPU0 register
+ *  for DIVAPLL clock divider ratio
+ */
+#define APLL_RATIO_SHIFT 24
+#define APLL_RATIO_MASK  0x07000000 /* [26:24] bits */
+
+/*
+ *  Offset in CLK_DIV_TOP register
+ *  for DIVACLK_100 clock divider ratio
+ */
+#define ACLK_100_RATIO_SHIFT 4
+#define ACLK_100_RATIO_MASK  0x000000f0 /* [7:4] bits */
+
+/* Offset in CLK_SRC_TOP0 register */
+#define MUX_ACLK_100_SEL_SHIFT 16
+
+/*
+ * Offsets in CLK_SRC_PERIL0 register
+ * for clock sources of UARTs
+ */
+#define UART0_SEL_SHIFT  0
+#define UART1_SEL_SHIFT  4
+#define UART2_SEL_SHIFT  8
+#define UART3_SEL_SHIFT  12
+#define UART4_SEL_SHIFT  16
+/*
+ * Offsets in CLK_DIV_PERIL0 register
+ * for clock divider of UARTs
+ */
+#define UART0_DIV_SHIFT  0
+#define UART1_DIV_SHIFT  4
+#define UART2_DIV_SHIFT  8
+#define UART3_DIV_SHIFT  12
+#define UART4_DIV_SHIFT  16
+
+#define SOURCES_NUMBER   9
+
+typedef struct ClockChangeEntry {
+    QTAILQ_ENTRY(ClockChangeEntry) entry;
+    ClockChangeHandler *func;
+    void *opaque;
+} ClockChangeEntry;
+
+#define TYPE_EXYNOS4210_CMU   "exynos4210.cmu"
+#define TYPE_EXYNOS4210_CLOCK "exynos4210.clock"
+
+typedef struct {
+    const char      *name;
+    int32_t          id;
+    uint64_t         rate;
+
+    /* Current source clock */
+    int32_t  src_id;
+    /*
+     * Available sources. Their order must correspond to CLK_SRC_ register
+     */
+    int32_t  src_ids[SOURCES_NUMBER];
+
+    uint32_t src_reg; /* Offset of CLK_SRC_<*> register */
+    uint32_t div_reg; /* Offset of CLK_DIV_<*> register */
+
+    /*
+     *  Shift for MUX_<clk>_SEL value which is stored
+     *  in appropriate CLK_MUX_STAT_<cmu> register
+     */
+    uint8_t mux_shift;
+
+    /*
+     *  Shift for <clk>_RATIO value which is stored
+     *  in appropriate CLK_DIV_<cmu> register
+     */
+    uint8_t div_shift;
+
+    /* Which CMU controls this clock */
+    int32_t cmu_id;
+
+    QTAILQ_HEAD(, ClockChangeEntry) clock_change_handler;
+} Exynos4210ClockState;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    /* registers values */
+    uint32_t reg[EXYNOS4210_CMU_REGS_MEM_SIZE / sizeof(uint32_t)];
+
+    /* which CMU it is */
+    Exynos4210Cmu cmu_id;
+
+    /* registers information for debugging and resetting */
+    const Exynos4210CmuReg *regs;
+    int regs_number;
+
+    Exynos4210ClockState *clock;
+    int clock_number; /* how many clocks are controlled by given CMU */
+} Exynos4210CmuState;
+
+
+/* Clocks from Clock Pads */
+/*
+ *  Two following clocks aren't controlled by any CMUs. These structures are
+ *  used directly from global space and their fields  shouldn't be modified.
+ */
+
+/* It should be used only for testing purposes. XOM_0 is 0 */
+static const Exynos4210ClockState xxti = {
+    .name       = "XXTI",
+    .id         = EXYNOS4210_XXTI,
+    .rate       = 24000000,
+    .cmu_id     = UNSPECIFIED_CMU,
+};
+
+/* Main source. XOM_0 is 1 */
+static const Exynos4210ClockState xusbxti = {
+    .name       = "XUSBXTI",
+    .id         = EXYNOS4210_XUSBXTI,
+    .rate       = 24000000,
+    .cmu_id     = UNSPECIFIED_CMU,
+};
+
+/* PLLs */
+
+static const Exynos4210ClockState mpll = {
+    .name       = "MPLL",
+    .id         = EXYNOS4210_MPLL,
+    .src_id     = (XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI),
+    .div_reg    = PLL_CON0_(MPLL),
+    .cmu_id     = EXYNOS4210_CMU_CPU,
+};
+
+static const Exynos4210ClockState apll = {
+    .name       = "APLL",
+    .id         = EXYNOS4210_APLL,
+    .src_id     = (XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI),
+    .div_reg    = PLL_CON0_(APLL),
+    .cmu_id     = EXYNOS4210_CMU_CPU,
+};
+
+
+/**/
+
+static const Exynos4210ClockState sclk_hdmi24m = {
+    .name    = "SCLK_HDMI24M",
+    .id      = EXYNOS4210_SCLK_HDMI24M,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static const Exynos4210ClockState sclk_usbphy0 = {
+    .name    = "SCLK_USBPHY0",
+    .id      = EXYNOS4210_SCLK_USBPHY0,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static const Exynos4210ClockState sclk_usbphy1 = {
+    .name    = "SCLK_USBPHY1",
+    .id      = EXYNOS4210_SCLK_USBPHY1,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_hdmiphy = {
+    .name    = "SCLK_HDMIPHY",
+    .id      = EXYNOS4210_SCLK_HDMIPHY,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static const Exynos4210ClockState sclk_mpll = {
+    .name       = "SCLK_MPLL",
+    .id         = EXYNOS4210_SCLK_MPLL,
+    .src_ids    = {XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI,
+                   EXYNOS4210_MPLL},
+    .src_reg    = CLK_SRC_(CPU_BLK),
+    .mux_shift  = MUX_MPLL_SEL_SHIFT,
+    .cmu_id     = EXYNOS4210_CMU_CPU,
+};
+
+static const Exynos4210ClockState sclk_apll = {
+    .name       = "SCLK_APLL",
+    .id         = EXYNOS4210_SCLK_APLL,
+    .src_ids    = {XOM_0 ? EXYNOS4210_XUSBXTI : EXYNOS4210_XXTI,
+                   EXYNOS4210_APLL},
+    .src_reg    = CLK_SRC_(CPU_BLK),
+    .div_reg    = CLK_DIV_(CPU0_BLK),
+    .mux_shift  = MUX_APLL_SEL_SHIFT,
+    .div_shift  = APLL_RATIO_SHIFT,
+    .cmu_id     = EXYNOS4210_CMU_CPU,
+};
+
+static const Exynos4210ClockState aclk_100 = {
+    .name      = "ACLK_100",
+    .id        = EXYNOS4210_ACLK_100,
+    .src_ids   = {EXYNOS4210_SCLK_MPLL, EXYNOS4210_SCLK_APLL},
+    .src_reg   = CLK_SRC_(TOP0_BLK),
+    .div_reg   = CLK_DIV_(TOP_BLK),
+    .mux_shift = MUX_ACLK_100_SEL_SHIFT,
+    .div_shift = ACLK_100_RATIO_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+static const Exynos4210ClockState sclk_uart0 = {
+    .name      = "SCLK_UART0",
+    .id        = EXYNOS4210_SCLK_UART0,
+    .src_ids   = {EXYNOS4210_XXTI,
+                  EXYNOS4210_XUSBXTI,
+                  EXYNOS4210_SCLK_HDMI24M,
+                  EXYNOS4210_SCLK_USBPHY0,
+                  EXYNOS4210_SCLK_USBPHY1,
+                  EXYNOS4210_SCLK_HDMIPHY,
+                  EXYNOS4210_SCLK_MPLL},
+    .src_reg   = CLK_SRC_(PERIL0_BLK),
+    .div_reg   = CLK_DIV_(PERIL0_BLK),
+    .mux_shift = UART0_SEL_SHIFT,
+    .div_shift = UART0_DIV_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+static const Exynos4210ClockState sclk_uart1 = {
+    .name      = "SCLK_UART1",
+    .id        = EXYNOS4210_SCLK_UART1,
+    .src_ids   = {EXYNOS4210_XXTI,
+                  EXYNOS4210_XUSBXTI,
+                  EXYNOS4210_SCLK_HDMI24M,
+                  EXYNOS4210_SCLK_USBPHY0,
+                  EXYNOS4210_SCLK_USBPHY1,
+                  EXYNOS4210_SCLK_HDMIPHY,
+                  EXYNOS4210_SCLK_MPLL},
+    .src_reg   = CLK_SRC_(PERIL0_BLK),
+    .div_reg   = CLK_DIV_(PERIL0_BLK),
+    .mux_shift = UART1_SEL_SHIFT,
+    .div_shift = UART1_DIV_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+static const Exynos4210ClockState sclk_uart2 = {
+    .name      = "SCLK_UART2",
+    .id        = EXYNOS4210_SCLK_UART2,
+    .src_ids   = {EXYNOS4210_XXTI,
+                  EXYNOS4210_XUSBXTI,
+                  EXYNOS4210_SCLK_HDMI24M,
+                  EXYNOS4210_SCLK_USBPHY0,
+                  EXYNOS4210_SCLK_USBPHY1,
+                  EXYNOS4210_SCLK_HDMIPHY,
+                  EXYNOS4210_SCLK_MPLL},
+    .src_reg   = CLK_SRC_(PERIL0_BLK),
+    .div_reg   = CLK_DIV_(PERIL0_BLK),
+    .mux_shift = UART2_SEL_SHIFT,
+    .div_shift = UART2_DIV_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+static const Exynos4210ClockState sclk_uart3 = {
+    .name      = "SCLK_UART3",
+    .id        = EXYNOS4210_SCLK_UART3,
+    .src_ids   = {EXYNOS4210_XXTI,
+                  EXYNOS4210_XUSBXTI,
+                  EXYNOS4210_SCLK_HDMI24M,
+                  EXYNOS4210_SCLK_USBPHY0,
+                  EXYNOS4210_SCLK_USBPHY1,
+                  EXYNOS4210_SCLK_HDMIPHY,
+                  EXYNOS4210_SCLK_MPLL},
+    .src_reg   = CLK_SRC_(PERIL0_BLK),
+    .div_reg   = CLK_DIV_(PERIL0_BLK),
+    .mux_shift = UART3_SEL_SHIFT,
+    .div_shift = UART3_DIV_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+static const Exynos4210ClockState sclk_uart4 = {
+    .name      = "SCLK_UART4",
+    .id        = EXYNOS4210_SCLK_UART4,
+    .src_ids   = {EXYNOS4210_XXTI,
+                  EXYNOS4210_XUSBXTI,
+                  EXYNOS4210_SCLK_HDMI24M,
+                  EXYNOS4210_SCLK_USBPHY0,
+                  EXYNOS4210_SCLK_USBPHY1,
+                  EXYNOS4210_SCLK_HDMIPHY,
+                  EXYNOS4210_SCLK_MPLL},
+    .src_reg   = CLK_SRC_(PERIL0_BLK),
+    .div_reg   = CLK_DIV_(PERIL0_BLK),
+    .mux_shift = UART4_SEL_SHIFT,
+    .div_shift = UART4_DIV_SHIFT,
+    .cmu_id    = EXYNOS4210_CMU_TOP,
+};
+
+/*
+ *  This array must correspond to Exynos4210Clock enumerator
+ *  which is defined in exynos4210.h file
+ *
+ */
+static const Exynos4210ClockState *exynos4210_clock[] = {
+    NULL,
+    &xxti,
+    &xusbxti,
+    &apll,
+    &mpll,
+    &sclk_hdmi24m,
+    &sclk_usbphy0,
+    &sclk_usbphy1,
+    &sclk_hdmiphy,
+    &sclk_apll,
+    &sclk_mpll,
+    &aclk_100,
+    &sclk_uart0,
+    &sclk_uart1,
+    &sclk_uart2,
+    &sclk_uart3,
+    &sclk_uart4,
+    NULL,
+};
+
+/*
+ * This array must correspond to Exynos4210Cmu enumerator
+ * which is defined in exynos4210.h file
+ *
+ */
+static const char exynos4210_cmu_path[][13] = {
+    "cmu_leftbus",
+    "cmu_rightbus",
+    "cmu_top",
+    "cmu_dmc",
+    "cmu_cpu",
+};
+
+#if DEBUG_CMU_EXTEND
+/* The only meaning of life - debugging. This function should be only used
+ * inside PRINT_DEBUG_EXTEND macros
+ */
+static const char *exynos4210_cmu_regname(Exynos4210CmuState *s,
+                                          target_phys_addr_t  offset)
+{
+    int i;
+
+    for (i = 0; i < s->regs_number; i++) {
+        if (offset == s->regs[i].offset) {
+            return s->regs[i].name;
+        }
+    }
+
+    return NULL;
+}
+#endif
+
+
+static Exynos4210ClockState *exynos4210_clock_find(Exynos4210Clock clock_id)
+{
+    int i;
+    int cmu_id;
+    Object *cmu;
+    Exynos4210CmuState *s;
+
+    cmu_id = exynos4210_clock[clock_id]->cmu_id;
+
+    if (cmu_id == UNSPECIFIED_CMU) {
+        for (i = 1; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+            if (exynos4210_clock[i]->id == clock_id) {
+
+                PRINT_DEBUG("Clock %s [%p] in CMU %d have been found\n",
+                            exynos4210_clock[i]->name,
+                            exynos4210_clock[i],
+                            cmu_id);
+
+                return  (Exynos4210ClockState *)exynos4210_clock[i];
+            }
+        }
+    }
+
+    cmu = object_resolve_path(exynos4210_cmu_path[cmu_id], NULL);
+    s = OBJECT_CHECK(Exynos4210CmuState, cmu, TYPE_EXYNOS4210_CMU);
+
+    for (i = 0; i < s->clock_number; i++) {
+        if (s->clock[i].id == clock_id) {
+
+            PRINT_DEBUG("Clock %s [%p] in CMU %d have been found\n",
+                                        s->clock[i].name,
+                                        &s->clock[i],
+                                        s->clock[i].cmu_id);
+            return  &s->clock[i];
+        }
+    }
+
+    PRINT_ERROR("Clock %d not found\n", clock_id);
+
+    return NULL;
+}
+
+
+void exynos4210_register_clock_handler(ClockChangeHandler *func,
+                                       Exynos4210Clock clock_id, void *opaque)
+{
+    ClockChangeEntry *cce = g_malloc0(sizeof(ClockChangeEntry));
+    Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+
+    if (clock == NULL) {
+        hw_error("We aren't be able to find clock %d\n", clock_id);
+    } else if (clock->cmu_id == UNSPECIFIED_CMU) {
+
+        PRINT_DEBUG("Clock %s never are changed. Handler won't be set.",
+                    exynos4210_clock[clock_id]->name);
+
+        return;
+    }
+
+    cce->func = func;
+    cce->opaque = opaque;
+
+    QTAILQ_INSERT_TAIL(&clock->clock_change_handler, cce, entry);
+
+    PRINT_DEBUG("For %s have been set handler [%p]\n", clock->name, cce->func);
+
+    return;
+}
+
+uint64_t exynos4210_cmu_get_rate(Exynos4210Clock clock_id)
+{
+    Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+
+    if (clock == NULL) {
+        hw_error("We aren't be able to find clock %d\n", clock_id);
+    }
+
+    return clock->rate;
+}
+
+static void exynos4210_cmu_set_pll(void *opaque, Exynos4210ClockState *pll)
+{
+    Exynos4210CmuState *s = opaque;
+    Exynos4210ClockState *source;
+    target_phys_addr_t offset = pll->div_reg;
+    ClockChangeEntry *cce;
+    uint32_t pdiv, mdiv, sdiv, enable;
+
+    source = exynos4210_clock_find(pll->src_id);
+
+    if (source == NULL) {
+        hw_error("We haven't find source clock %d (requested for %s)\n",
+                 pll->src_id, pll->name);
+    }
+
+    /*
+     * FOUT = MDIV * FIN / (PDIV * 2^(SDIV-1))
+     */
+
+    enable = (s->reg[I_(offset)] & PLL_ENABLE_MASK) >> PLL_ENABLE_SHIFT;
+    mdiv   = (s->reg[I_(offset)] & PLL_MDIV_MASK)   >> PLL_MDIV_SHIFT;
+    pdiv   = (s->reg[I_(offset)] & PLL_PDIV_MASK)   >> PLL_PDIV_SHIFT;
+    sdiv   = (s->reg[I_(offset)] & PLL_SDIV_MASK)   >> PLL_SDIV_SHIFT;
+
+    if (source) {
+        if (enable) {
+            pll->rate = mdiv * source->rate / (pdiv * (1 << (sdiv-1)));
+        } else {
+            pll->rate = 0;
+        }
+    } else {
+        hw_error("%s: Source undefined for %s\n", __func__, pll->name);
+    }
+
+    QTAILQ_FOREACH(cce, &pll->clock_change_handler, entry) {
+        cce->func(cce->opaque);
+    }
+
+    PRINT_DEBUG("%s rate: %llu\n", pll->name, pll->rate);
+
+    s->reg[I_(offset)] |= PLL_LOCKED_MASK;
+}
+
+
+static void exynos4210_cmu_set_rate(void *opaque, Exynos4210Clock clock_id)
+{
+    Exynos4210CmuState *s = opaque;
+    Exynos4210ClockState *clock = exynos4210_clock_find(clock_id);
+    ClockChangeEntry *cce;
+
+    if (clock == NULL) {
+        hw_error("We haven't find source clock %d ", clock_id);
+    }
+
+    if ((clock->id == EXYNOS4210_MPLL) || (clock->id == EXYNOS4210_APLL)) {
+
+        exynos4210_cmu_set_pll(s, clock);
+
+    } else if ((clock->cmu_id != UNSPECIFIED_CMU)) {
+
+        Exynos4210ClockState *source;
+
+        uint32_t src_index = I_(clock->src_reg);
+        uint32_t div_index = I_(clock->div_reg);
+
+        clock->src_id = clock->src_ids[(s->reg[src_index] >>
+                                                      clock->mux_shift) & 0xf];
+
+        source = exynos4210_clock_find(clock->src_id);
+        if (source == NULL) {
+            hw_error("We haven't find source clock %d (requested for %s)\n",
+                     clock->src_id, clock->name);
+        }
+
+        clock->rate = muldiv64(source->rate, 1,
+                                 ((((clock->div_reg ? s->reg[div_index] : 0) >>
+                                                clock->div_shift) & 0xf) + 1));
+
+        QTAILQ_FOREACH(cce, &clock->clock_change_handler, entry) {
+            cce->func(cce->opaque);
+        }
+
+        PRINT_DEBUG_EXTEND("SRC: <0x%05x> %s, SHIFT: %d\n",
+                           clock->src_reg,
+                           exynos4210_cmu_regname(s, clock->src_reg),
+                           clock->mux_shift);
+
+        PRINT_DEBUG("%s [%s:%llu]: %llu\n",
+                    clock->name,
+                    source->name,
+                    (long long unsigned int)source->rate,
+                    (long long unsigned int)clock->rate);
+    }
+}
+
+
+static uint64_t exynos4210_cmu_read(void *opaque, target_phys_addr_t offset,
+                                  unsigned size)
+{
+    Exynos4210CmuState *s = opaque;
+
+    if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+        PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+        return 0;
+    }
+
+    if (offset & EXTENDED_REGION_MASK) {
+        if (s->cmu_id == EXYNOS4210_CMU_DMC) {
+            switch (offset & 0xFFF) {
+            case DCGIDX_MAP0:
+            case DCGIDX_MAP1:
+            case DCGIDX_MAP2:
+            case DCGPERF_MAP0:
+            case DCGPERF_MAP1:
+            case DVCIDX_MAP:
+            case FREQ_CPU:
+            case FREQ_DPM:
+            case DVSEMCLK_EN:
+            case MAXPERF:
+                return s->reg[I_(offset)];
+            default:
+                PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+                return 0;
+            }
+        }
+
+        if (s->cmu_id == EXYNOS4210_CMU_CPU) {
+            switch (offset & 0xFFF) {
+            case ARMCLK_STOPCTRL:
+            case ATCLK_STOPCTRL:
+            case PARITYFAIL_STATUS:
+            case PARITYFAIL_CLEAR:
+            case PWR_CTRL:
+            case APLL_CON0_L8:
+            case APLL_CON0_L7:
+            case APLL_CON0_L6:
+            case APLL_CON0_L5:
+            case APLL_CON0_L4:
+            case APLL_CON0_L3:
+            case APLL_CON0_L2:
+            case APLL_CON0_L1:
+            case IEM_CONTROL:
+            case APLL_CON1_L8:
+            case APLL_CON1_L7:
+            case APLL_CON1_L6:
+            case APLL_CON1_L5:
+            case APLL_CON1_L4:
+            case APLL_CON1_L3:
+            case APLL_CON1_L2:
+            case APLL_CON1_L1:
+            case CLKDIV_IEM_L8:
+            case CLKDIV_IEM_L7:
+            case CLKDIV_IEM_L6:
+            case CLKDIV_IEM_L5:
+            case CLKDIV_IEM_L4:
+            case CLKDIV_IEM_L3:
+            case CLKDIV_IEM_L2:
+            case CLKDIV_IEM_L1:
+                return s->reg[I_(offset)];
+            default:
+                PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+                return 0;
+            }
+        }
+    }
+
+    switch (offset & GROUP_MASK) {
+    case PLL_LOCK:
+    case PLL_CON:
+    case CLK_SRC:
+    case CLK_SRC_MASK:
+    case CLK_MUX_STAT:
+    case CLK_DIV:
+    case CLK_DIV_STAT:
+    case 0x700: /* Reserved */
+    case CLK_GATE_SCLK: /* reserved? */
+    case CLK_GATE_IP:
+    case CLKOUT_CMU:
+        return s->reg[I_(offset)];
+    default:
+        PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+        return 0;
+    }
+
+    PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n", offset,
+                       exynos4210_cmu_regname(s, offset), s->reg[I_(offset)]);
+}
+
+
+static void exynos4210_cmu_write(void *opaque, target_phys_addr_t offset,
+                               uint64_t val, unsigned size)
+{
+    Exynos4210CmuState *s = opaque;
+    uint32_t group, block;
+
+    group = offset & GROUP_MASK;
+    block = offset & BLOCK_MASK;
+
+    switch (group) {
+    case PLL_LOCK:
+        /* it's not necessary at this moment
+         * TODO: do it
+         */
+        break;
+    case PLL_CON:
+        switch (block) {
+        case APLL:
+        {
+            uint32_t pre_val = s->reg[I_(offset)];
+            s->reg[I_(offset)] = val;
+            val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+            s->reg[I_(offset)] = val;
+            exynos4210_cmu_set_rate(s, EXYNOS4210_APLL);
+            break;
+        }
+        case MPLL:
+        {
+            uint32_t pre_val = s->reg[I_(offset)];
+            s->reg[I_(offset)] = val;
+            val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+            s->reg[I_(offset)] = val;
+            exynos4210_cmu_set_rate(s, EXYNOS4210_MPLL);
+            break;
+        }
+        }
+        break;
+    case CLK_SRC:
+        switch (block) {
+        case CPU_BLK:
+        {
+            uint32_t pre_val = s->reg[I_(offset)];
+            s->reg[I_(offset)] = val;
+
+            if (val & MUX_APLL_SEL) {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(APLL_SEL_MASK)) 
|
+                       (2 << APLL_SEL_SHIFT);
+
+                if ((pre_val & MUX_APLL_SEL) !=
+                        (s->reg[I_(offset)] & MUX_APLL_SEL)) {
+                    exynos4210_cmu_set_rate(s, EXYNOS4210_APLL);
+                }
+
+            } else {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(APLL_SEL_MASK)) 
|
+                       (1 << APLL_SEL_SHIFT);
+
+                if ((pre_val & MUX_APLL_SEL) !=
+                        (s->reg[I_(offset)] & MUX_APLL_SEL)) {
+                    exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+                                                              EXYNOS4210_XXTI);
+                }
+            }
+
+
+            if (val & MUX_MPLL_SEL) {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(MPLL_SEL_MASK)) 
|
+                       (2 << MPLL_SEL_SHIFT);
+
+                if ((pre_val & MUX_MPLL_SEL) !=
+                        (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
+                    exynos4210_cmu_set_rate(s, EXYNOS4210_MPLL);
+                }
+
+            } else {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(MPLL_SEL_MASK)) 
|
+                       (1 << MPLL_SEL_SHIFT);
+
+                if ((pre_val & MUX_MPLL_SEL) !=
+                        (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
+                    exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+                                                              EXYNOS4210_XXTI);
+                }
+            }
+
+            if (val & MUX_CORE_SEL) {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(CORE_SEL_MASK)) 
|
+                       (2 << CORE_SEL_SHIFT);
+
+                if ((pre_val & MUX_CORE_SEL) !=
+                        (s->reg[I_(offset)] & MUX_CORE_SEL)) {
+                    exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+                }
+
+            } else {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(CORE_SEL_MASK)) 
|
+                        (1 << CORE_SEL_SHIFT);
+
+                if ((pre_val & MUX_CORE_SEL) !=
+                        (s->reg[I_(offset)] & MUX_CORE_SEL)) {
+                    exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+                                                              EXYNOS4210_XXTI);
+                }
+            }
+
+            if (val & MUX_HPM_SEL) {
+                exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                       (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(HPM_SEL_MASK)) |
+                       (2 << HPM_SEL_SHIFT);
+
+                if ((pre_val & MUX_HPM_SEL) !=
+                                          (s->reg[I_(offset)] & MUX_HPM_SEL)) {
+                    exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+                }
+
+            } else {
+                s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] =
+                        (s->reg[I_(CLK_MUX_STAT_(CPU_BLK))] & ~(HPM_SEL_MASK)) 
|
+                        (1 << HPM_SEL_SHIFT);
+
+                if ((pre_val & MUX_HPM_SEL) !=
+                                          (s->reg[I_(offset)] & MUX_HPM_SEL)) {
+                    exynos4210_cmu_set_rate(s, XOM_0 ? EXYNOS4210_XUSBXTI :
+                                                              EXYNOS4210_XXTI);
+                }
+            }
+        }
+            break;
+        case TOP0_BLK:
+            s->reg[I_(offset)] = val;
+            exynos4210_cmu_set_rate(s, EXYNOS4210_ACLK_100);
+            break;
+        default:
+            PRINT_ERROR("Unknown functional block: 0x%x\n", (int)block);
+        }
+        break;
+    case CLK_SRC_MASK:
+        break;
+    case CLK_MUX_STAT:
+        break;
+    case CLK_DIV:
+        switch (block) {
+        case TOP_BLK:
+            s->reg[I_(offset)] = val;
+            exynos4210_cmu_set_rate(s, EXYNOS4210_ACLK_100);
+            break;
+        case CPU0_BLK:
+            s->reg[I_(offset)] = val;
+            exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_APLL);
+            exynos4210_cmu_set_rate(s, EXYNOS4210_SCLK_MPLL);
+            break;
+        }
+    case CLK_DIV_STAT: /* CLK_DIV_STAT */
+    case 0x700: /* Reserved */
+    case CLK_GATE_SCLK: /* reserved? */
+    case CLK_GATE_IP:
+    case CLKOUT_CMU:
+        break;
+    default:
+        PRINT_ERROR("Bad offset: 0x%x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps exynos4210_cmu_ops = {
+    .read = exynos4210_cmu_read,
+    .write = exynos4210_cmu_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void clock_rate_changed(void *opaque)
+{
+    Exynos4210ClockState *cs = (Exynos4210ClockState *)opaque;
+    Object *cmu = object_resolve_path(exynos4210_cmu_path[cs->cmu_id], NULL);
+    Exynos4210CmuState *s = OBJECT_CHECK(Exynos4210CmuState, cmu,
+                                                          TYPE_EXYNOS4210_CMU);
+
+    PRINT_DEBUG("Clock %s was changed\n", cs->name);
+
+    exynos4210_cmu_set_rate(s, cs->id);
+
+}
+
+static void exynos4210_cmu_reset(DeviceState *dev)
+{
+    Exynos4210CmuState *s = OBJECT_CHECK(Exynos4210CmuState, OBJECT(dev),
+                                                          TYPE_EXYNOS4210_CMU);
+    int i, j;
+    uint32_t index = 0;
+
+    for (i = 0; i < s->regs_number; i++) {
+        index = (s->regs[i].offset) / sizeof(uint32_t);
+        s->reg[index] = s->regs[i].reset_value;
+    }
+
+    for (i = 0; i < s->clock_number; i++) {
+
+        for (j = 0; j < SOURCES_NUMBER; j++) {
+
+            if (s->clock[i].src_ids[j] == UNSPECIFIED_CLOCK) {
+
+                if (j == 0) {
+                    /*
+                     * we have empty '.sources[]' array
+                     */
+                    if (s->clock[i].src_id != UNSPECIFIED_CLOCK) {
+
+                        s->clock[i].src_ids[j] = s->clock[i].src_id;
+
+                    } else {
+
+                        if (s->clock[i].cmu_id != UNSPECIFIED_CMU) {
+                            /*
+                             * We haven't any defined sources for this clock.
+                             * Error during definition of appropriate clock
+                             * structure
+                             */
+                            hw_error("exynos4210_cmu_reset:"
+                                     "There aren't any sources for %s 
clock!\n",
+                                     s->clock[i].name);
+                        } else {
+                            /*
+                             * we don't need any sources for this clock
+                             * because it's a root clock
+                             */
+                            break;
+                        }
+                    }
+                } else {
+                    break; /* leave because there are no more sources */
+                }
+            } /* src_ids[j] == UNSPECIFIED_CLOCK */
+
+            Exynos4210ClockState *source =
+                                 exynos4210_clock_find(s->clock[i].src_ids[j]);
+
+            if (source == NULL) {
+                hw_error("We aren't be able to find source clock %d "
+                         "(requested for %s)\n",
+                         s->clock[i].src_ids[j], s->clock[i].name);
+            }
+
+            if (source->cmu_id != UNSPECIFIED_CMU) {
+
+                exynos4210_register_clock_handler(clock_rate_changed,
+                                         s->clock[i].src_ids[j], &s->clock[i]);
+            }
+        } /* SOURCES_NUMBER */
+
+        exynos4210_cmu_set_rate(s, s->clock[i].id);
+    }
+
+    PRINT_DEBUG("CMU %d reset completed\n", s->cmu_id);
+}
+
+static const VMStateDescription vmstate_exynos4210_clock = {
+    .name = TYPE_EXYNOS4210_CLOCK,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(rate, Exynos4210ClockState),
+        VMSTATE_INT32(src_id, Exynos4210ClockState),
+        VMSTATE_INT32_ARRAY(src_ids, Exynos4210ClockState, SOURCES_NUMBER),
+        VMSTATE_UINT32(src_reg, Exynos4210ClockState),
+        VMSTATE_UINT32(div_reg, Exynos4210ClockState),
+        VMSTATE_UINT8(mux_shift, Exynos4210ClockState),
+        VMSTATE_UINT8(div_shift, Exynos4210ClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_exynos4210_cmu = {
+    .name = TYPE_EXYNOS4210_CMU,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(reg, Exynos4210CmuState,
+                              EXYNOS4210_CMU_REGS_MEM_SIZE / sizeof(uint32_t)),
+        VMSTATE_STRUCT_VARRAY_INT32(clock, Exynos4210CmuState,
+                                          clock_number, 0,
+                                          vmstate_exynos4210_clock,
+                                          Exynos4210ClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+DeviceState *exynos4210_cmu_create(target_phys_addr_t addr,
+                                                          Exynos4210Cmu cmu_id)
+{
+    DeviceState  *dev;
+    SysBusDevice *bus;
+
+    dev = qdev_create(NULL, TYPE_EXYNOS4210_CMU);
+
+    qdev_prop_set_int32(dev, "cmu_id", cmu_id);
+
+    bus = sysbus_from_qdev(dev);
+    qdev_init_nofail(dev);
+    if (addr != (target_phys_addr_t)-1) {
+        sysbus_mmio_map(bus, 0, addr);
+    }
+
+    return dev;
+}
+
+static int exynos4210_cmu_init(SysBusDevice *dev)
+{
+    Exynos4210CmuState *s = FROM_SYSBUS(Exynos4210CmuState, dev);
+    int i, n;
+
+    memory_region_init_io(&s->iomem, &exynos4210_cmu_ops, s,
+                          TYPE_EXYNOS4210_CMU, EXYNOS4210_CMU_REGS_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    switch (s->cmu_id) {
+    case EXYNOS4210_CMU_LEFTBUS:
+        s->regs = exynos4210_cmu_leftbus_regs;
+        s->regs_number = ARRAY_SIZE(exynos4210_cmu_leftbus_regs);
+        break;
+    case EXYNOS4210_CMU_RIGHTBUS:
+        s->regs = exynos4210_cmu_rightbus_regs;
+        s->regs_number = ARRAY_SIZE(exynos4210_cmu_rightbus_regs);
+        break;
+    case EXYNOS4210_CMU_TOP:
+        s->regs = exynos4210_cmu_top_regs;
+        s->regs_number = ARRAY_SIZE(exynos4210_cmu_top_regs);
+        break;
+    case EXYNOS4210_CMU_DMC:
+        s->regs = exynos4210_cmu_dmc_regs;
+        s->regs_number = ARRAY_SIZE(exynos4210_cmu_dmc_regs);
+        break;
+    case EXYNOS4210_CMU_CPU:
+        s->regs = exynos4210_cmu_cpu_regs;
+        s->regs_number = ARRAY_SIZE(exynos4210_cmu_cpu_regs);
+        break;
+    default:
+        hw_error("Wrong CMU: %d\n", s->cmu_id);
+    }
+
+    for (i = 1, n = 0; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+        if (s->cmu_id == exynos4210_clock[i]->cmu_id) {
+            n++;
+        }
+    }
+
+    s->clock =
+           (Exynos4210ClockState *)g_malloc0(n * sizeof(Exynos4210ClockState));
+
+    for (i = 1, s->clock_number = 0; i < EXYNOS4210_CLOCKS_NUMBER; i++) {
+
+        if (s->cmu_id == exynos4210_clock[i]->cmu_id) {
+
+            memcpy(&s->clock[s->clock_number], exynos4210_clock[i],
+                   sizeof(Exynos4210ClockState));
+
+            QTAILQ_INIT(&s->clock[s->clock_number].clock_change_handler);
+
+            PRINT_DEBUG("Clock %s was added to \"%s\"\n",
+                        s->clock[s->clock_number].name,
+                        exynos4210_cmu_path[s->cmu_id]);
+
+            s->clock_number++;
+        }
+    }
+
+    object_property_add_child(object_get_root(), 
exynos4210_cmu_path[s->cmu_id],
+                              OBJECT(dev), NULL);
+
+    return 0;
+}
+
+static Property exynos4210_cmu_properties[] = {
+    DEFINE_PROP_INT32("cmu_id", Exynos4210CmuState, cmu_id, UNSPECIFIED_CMU),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_cmu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = exynos4210_cmu_init;
+    dc->reset = exynos4210_cmu_reset;
+    dc->props = exynos4210_cmu_properties;
+    dc->vmsd  = &vmstate_exynos4210_cmu;
+}
+
+static const TypeInfo exynos4210_cmu_info = {
+    .name          = TYPE_EXYNOS4210_CMU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210CmuState),
+    .class_init    = exynos4210_cmu_class_init,
+};
+
+static void exynos4210_cmu_register_types(void)
+{
+    type_register_static(&exynos4210_cmu_info);
+}
+
+type_init(exynos4210_cmu_register_types)
-- 
1.7.5.4


Reply via email to