qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 02/15] ARM: exynos4210: CMU support


From: Evgeny Voevodin
Subject: [Qemu-devel] [PATCH 02/15] ARM: exynos4210: CMU support
Date: Fri, 09 Dec 2011 17:34:29 +0400

From: Maksim Kozlov <address@hidden>

Add exynos4210 Clock Management Units emulation

Signed-off-by: Evgeny Voevodin <address@hidden>
---
 Makefile.target     |    2 +-
 hw/exynos4210.c     |    7 +
 hw/exynos4210.h     |   22 +
 hw/exynos4210_cmu.c | 1146 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1176 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210_cmu.c

diff --git a/Makefile.target b/Makefile.target
index 624a142..ce4f1f8 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -344,7 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o 
arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
-obj-arm-y += exynos4210.o
+obj-arm-y += exynos4210.o exynos4210_cmu.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
 obj-arm-y += pl061.o
 obj-arm-y += arm-semi.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 1550016..1a6e353 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -60,6 +60,10 @@
 
 #define EXYNOS4210_BASE_BOOT_ADDR           EXYNOS4210_DRAM0_BASE_ADDR
 
+/* SFR Base Address for CMUs */
+#define EXYNOS4210_CMU_BASE_ADDR            0x10030000
+
+
 static struct arm_boot_info exynos4210_binfo = {
         .loader_start     = EXYNOS4210_BASE_BOOT_ADDR,
 };
@@ -172,6 +176,9 @@ static void exynos4210_init(ram_addr_t ram_size,
     memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
             dram0_mem);
 
+    /* CMU */
+    sysbus_create_simple("exynos4210.cmu", EXYNOS4210_CMU_BASE_ADDR, NULL);
+
     /*** Load kernel ***/
 
     exynos4210_binfo.ram_size = ram_size;
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index 7137630..683a4a6 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -31,4 +31,26 @@
 
 #define EXYNOS4210_MAX_CPUS                2
 
+/*
+ * Interface for exynos4210 Clock Management Units (CMUs)
+ */
+
+typedef enum {
+    XXTI,
+    XUSBXTI,
+    APLL,
+    MPLL,
+    SCLK_APLL,
+    SCLK_MPLL,
+    ACLK_100,
+    SCLK_UART0,
+    SCLK_UART1,
+    SCLK_UART2,
+    SCLK_UART3,
+    SCLK_UART4,
+    CLOCKS_NUMBER
+} Exynos4210CmuClock;
+
+uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock);
+
 #endif /* EXYNOS4210_H_ */
diff --git a/hw/exynos4210_cmu.c b/hw/exynos4210_cmu.c
new file mode 100644
index 0000000..fe4100c
--- /dev/null
+++ b/hw/exynos4210_cmu.c
@@ -0,0 +1,1146 @@
+/*
+ *  exynos4210 Clock Management Units (CMUs) Emulation
+ *
+ *  Copyright (C) 2011 Samsung Electronics Co Ltd.
+ *    Maksim Kozlov, <address@hidden>
+ *
+ *  Created on: 07.2011
+ *
+ *
+ *  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, write to the Free Software Foundation, Inc., 51
+ *  Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "sysbus.h"
+
+#include "exynos4210.h"
+
+
+
+#undef DEBUG_CMU
+
+//#define DEBUG_CMU
+//#define DEBUG_CMU_EXTEND
+
+
+#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)
+#define  PRINT_ERROR(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+
+#ifdef DEBUG_CMU
+
+    #undef PRINT_DEBUG
+    #define  PRINT_DEBUG(fmt, args...)  \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+    #undef PRINT_DEBUG_SIMPLE
+    #define  PRINT_DEBUG_SIMPLE(fmt, args...)  \
+        do { \
+            fprintf(stderr, fmt, ## args); \
+        } while (0)
+
+#ifdef DEBUG_CMU_EXTEND
+
+    #undef PRINT_DEBUG_EXTEND
+    #define  PRINT_DEBUG_EXTEND(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+
+#endif /* EXTEND */
+#endif
+
+
+/*
+ *  Offsets for CMUs registers
+ */
+
+/* CMU_LEFTBUS registers */
+#define     CLK_SRC_LEFTBUS              0x04200
+#define     CLK_MUX_STAT_LEFTBUS         0x04400
+#define     CLK_DIV_LEFTBUS              0x04500
+#define     CLK_DIV_STAT_LEFTBUS         0x04600
+#define     CLK_GATE_IP_LEFTBUS          0x04800
+#define     CLKOUT_CMU_LEFTBUS           0x04A00
+#define     CLKOUT_CMU_LEFTBUS_DIV_STAT  0x04A04
+/* CMU_RIGHTBUS registers */
+#define     CLK_SRC_RIGHTBUS             0x08200
+#define     CLK_MUX_STAT_RIGHTBUS        0x08400
+#define     CLK_DIV_RIGHTBUS             0x08500
+#define     CLK_DIV_STAT_RIGHTBUS        0x08600
+#define     CLK_GATE_IP_RIGHTBUS         0x08800
+#define     CLKOUT_CMU_RIGHTBUS          0x08A00
+#define     CLKOUT_CMU_RIGHTBUS_DIV_STAT 0x08A04
+/* CMU_TOP registers */
+#define     EPLL_LOCK                    0x0C010
+#define     VPLL_LOCK                    0x0C020
+#define     EPLL_CON0                    0x0C110
+#define     EPLL_CON1                    0x0C114
+#define     VPLL_CON0                    0x0C120
+#define     VPLL_CON1                    0x0C124
+#define     CLK_SRC_TOP0                 0x0C210
+#define     CLK_SRC_TOP1                 0x0C214
+#define     CLK_SRC_CAM                  0x0C220
+#define     CLK_SRC_TV                   0x0C224
+#define     CLK_SRC_MFC                  0x0C228
+#define     CLK_SRC_G3D                  0x0C22C
+#define     CLK_SRC_IMAGE                0x0C230
+#define     CLK_SRC_LCD0                 0x0C234
+#define     CLK_SRC_LCD1                 0x0C238
+#define     CLK_SRC_MAUDIO               0x0C23C
+#define     CLK_SRC_FSYS                 0x0C240
+#define     CLK_SRC_PERIL0               0x0C250
+#define     CLK_SRC_PERIL1               0x0C254
+#define     CLK_SRC_MASK_TOP             0x0C310
+#define     CLK_SRC_MASK_CAM             0x0C320
+#define     CLK_SRC_MASK_TV              0x0C324
+#define     CLK_SRC_MASK_LCD0            0x0C334
+#define     CLK_SRC_MASK_LCD1            0x0C338
+#define     CLK_SRC_MASK_MAUDIO          0x0C33C
+#define     CLK_SRC_MASK_FSYS            0x0C340
+#define     CLK_SRC_MASK_PERIL0          0x0C350
+#define     CLK_SRC_MASK_PERIL1          0x0C354
+#define     CLK_MUX_STAT_TOP             0x0C410
+#define     CLK_MUX_STAT_MFC             0x0C428
+#define     CLK_MUX_STAT_G3D             0x0C42C
+#define     CLK_MUX_STAT_IMAGE           0x0C430
+#define     CLK_DIV_TOP                  0x0C510
+#define     CLK_DIV_CAM                  0x0C520
+#define     CLK_DIV_TV                   0x0C524
+#define     CLK_DIV_MFC                  0x0C528
+#define     CLK_DIV_G3D                  0x0C52C
+#define     CLK_DIV_IMAGE                0x0C530
+#define     CLK_DIV_LCD0                 0x0C534
+#define     CLK_DIV_LCD1                 0x0C538
+#define     CLK_DIV_MAUDIO               0x0C53C
+#define     CLK_DIV_FSYS0                0x0C540
+#define     CLK_DIV_FSYS1                0x0C544
+#define     CLK_DIV_FSYS2                0x0C548
+#define     CLK_DIV_FSYS3                0x0C54C
+#define     CLK_DIV_PERIL0               0x0C550
+#define     CLK_DIV_PERIL1               0x0C554
+#define     CLK_DIV_PERIL2               0x0C558
+#define     CLK_DIV_PERIL3               0x0C55C
+#define     CLK_DIV_PERIL4               0x0C560
+#define     CLK_DIV_PERIL5               0x0C564
+#define     CLKDIV2_RATIO                0x0C580
+#define     CLK_DIV_STAT_TOP             0x0C610
+#define     CLK_DIV_STAT_CAM             0x0C620
+#define     CLK_DIV_STAT_TV              0x0C624
+#define     CLK_DIV_STAT_MFC             0x0C628
+#define     CLK_DIV_STAT_G3D             0x0C62C
+#define     CLK_DIV_STAT_IMAGE           0x0C630
+#define     CLK_DIV_STAT_LCD0            0x0C634
+#define     CLK_DIV_STAT_LCD1            0x0C638
+#define     CLK_DIV_STAT_MAUDIO          0x0C63C
+#define     CLK_DIV_STAT_FSYS0           0x0C640
+#define     CLK_DIV_STAT_FSYS1           0x0C644
+#define     CLK_DIV_STAT_FSYS2           0x0C648
+#define     CLK_DIV_STAT_FSYS3           0x0C64C
+#define     CLK_DIV_STAT_PERIL0          0x0C650
+#define     CLK_DIV_STAT_PERIL1          0x0C654
+#define     CLK_DIV_STAT_PERIL2          0x0C658
+#define     CLK_DIV_STAT_PERIL3          0x0C65C
+#define     CLK_DIV_STAT_PERIL4          0x0C660
+#define     CLK_DIV_STAT_PERIL5          0x0C664
+#define     CLKDIV2_STAT                 0x0C680
+#define     CLK_GATE_SCLK_CAM            0x0C820
+#define     CLK_GATE_IP_CAM              0x0C920
+#define     CLK_GATE_IP_TV               0x0C924
+#define     CLK_GATE_IP_MFC              0x0C928
+#define     CLK_GATE_IP_G3D              0x0C92C
+#define     CLK_GATE_IP_IMAGE            0x0C930
+#define     CLK_GATE_IP_LCD0             0x0C934
+#define     CLK_GATE_IP_LCD1             0x0C938
+#define     CLK_GATE_IP_FSYS             0x0C940
+#define     CLK_GATE_IP_GPS              0x0C94C
+#define     CLK_GATE_IP_PERIL            0x0C950
+#define     CLK_GATE_IP_PERIR            0x0C960
+#define     CLK_GATE_BLOCK               0x0C970
+#define     CLKOUT_CMU_TOP               0x0CA00
+#define     CLKOUT_CMU_TOP_DIV_STAT      0x0CA04
+/* CMU_DMC registers */
+#define     CLK_SRC_DMC                  0x10200
+#define     CLK_SRC_MASK_DMC             0x10300
+#define     CLK_MUX_STAT_DMC             0x10400
+#define     CLK_DIV_DMC0                 0x10500
+#define     CLK_DIV_DMC1                 0x10504
+#define     CLK_DIV_STAT_DMC0            0x10600
+#define     CLK_DIV_STAT_DMC1            0x10604
+#define     CLK_GATE_IP_DMC              0x10900
+#define     CLKOUT_CMU_DMC               0x10A00
+#define     CLKOUT_CMU_DMC_DIV_STAT      0x10A04
+#define     DCGIDX_MAP0                  0x11000
+#define     DCGIDX_MAP1                  0x11004
+#define     DCGIDX_MAP2                  0x11008
+#define     DCGPERF_MAP0                 0x11020
+#define     DCGPERF_MAP1                 0x11024
+#define     DVCIDX_MAP                   0x11040
+#define     FREQ_CPU                     0x11060
+#define     FREQ_DPM                     0x11064
+#define     DVSEMCLK_EN                  0x11080
+#define     MAXPERF                      0x11084
+#define     APLL_LOCK                    0x14000
+#define     MPLL_LOCK                    0x14008
+#define     APLL_CON0                    0x14100
+#define     APLL_CON1                    0x14104
+#define     MPLL_CON0                    0x14108
+#define     MPLL_CON1                    0x1410C
+/* CMU_CPU registers */
+#define     CLK_SRC_CPU                  0x14200
+#define     CLK_MUX_STAT_CPU             0x14400
+#define     CLK_DIV_CPU0                 0x14500
+#define     CLK_DIV_CPU1                 0x14504
+#define     CLK_DIV_STAT_CPU0            0x14600
+#define     CLK_DIV_STAT_CPU1            0x14604
+#define     CLK_GATE_SCLK_CPU            0x14800
+#define     CLK_GATE_IP_CPU              0x14900
+#define     CLKOUT_CMU_CPU               0x14A00
+#define     CLKOUT_CMU_CPU_DIV_STAT      0x14A04
+#define     ARMCLK_STOPCTRL              0x15000
+#define     ATCLK_STOPCTRL               0x15004
+#define     PARITYFAIL_STATUS            0x15010
+#define     PARITYFAIL_CLEAR             0x15014
+#define     PWR_CTRL                     0x15020
+#define     APLL_CON0_L8                 0x15100
+#define     APLL_CON0_L7                 0x15104
+#define     APLL_CON0_L6                 0x15108
+#define     APLL_CON0_L5                 0x1510C
+#define     APLL_CON0_L4                 0x15110
+#define     APLL_CON0_L3                 0x15114
+#define     APLL_CON0_L2                 0x15118
+#define     APLL_CON0_L1                 0x1511C
+#define     IEM_CONTROL                  0x15120
+#define     APLL_CON1_L8                 0x15200
+#define     APLL_CON1_L7                 0x15204
+#define     APLL_CON1_L6                 0x15208
+#define     APLL_CON1_L5                 0x1520C
+#define     APLL_CON1_L4                 0x15210
+#define     APLL_CON1_L3                 0x15214
+#define     APLL_CON1_L2                 0x15218
+#define     APLL_CON1_L1                 0x1521C
+#define     CLKDIV_IEM_L8                0x15300
+#define     CLKDIV_IEM_L7                0x15304
+#define     CLKDIV_IEM_L6                0x15308
+#define     CLKDIV_IEM_L5                0x1530C
+#define     CLKDIV_IEM_L4                0x15310
+#define     CLKDIV_IEM_L3                0x15314
+#define     CLKDIV_IEM_L2                0x15318
+#define     CLKDIV_IEM_L1                0x1531C
+
+
+typedef struct Exynos4210CmuReg {
+        const char *name; /* for debugging */
+        uint32_t    offset;
+        uint32_t    reset_value;
+} Exynos4210CmuReg;
+
+
+static Exynos4210CmuReg exynos4210_cmu_regs[] = {
+    /* CMU_LEFTBUS registers */
+    {"CLK_SRC_LEFTBUS",              CLK_SRC_LEFTBUS,              0x00000000},
+    {"CLK_MUX_STAT_LEFTBUS",         CLK_MUX_STAT_LEFTBUS,         0x00000001},
+    {"CLK_DIV_LEFTBUS",              CLK_DIV_LEFTBUS,              0x00000000},
+    {"CLK_DIV_STAT_LEFTBUS",         CLK_DIV_STAT_LEFTBUS,         0x00000000},
+    {"CLK_GATE_IP_LEFTBUS",          CLK_GATE_IP_LEFTBUS,          0xFFFFFFFF},
+    {"CLKOUT_CMU_LEFTBUS",           CLKOUT_CMU_LEFTBUS,           0x00010000},
+    {"CLKOUT_CMU_LEFTBUS_DIV_STAT",  CLKOUT_CMU_LEFTBUS_DIV_STAT,  0x00000000},
+    /* CMU_RIGHTBUS registers */
+    {"CLK_SRC_RIGHTBUS",             CLK_SRC_RIGHTBUS,             0x00000000},
+    {"CLK_MUX_STAT_RIGHTBUS",        CLK_MUX_STAT_RIGHTBUS,        0x00000001},
+    {"CLK_DIV_RIGHTBUS",             CLK_DIV_RIGHTBUS,             0x00000000},
+    {"CLK_DIV_STAT_RIGHTBUS",        CLK_DIV_STAT_RIGHTBUS,        0x00000000},
+    {"CLK_GATE_IP_RIGHTBUS",         CLK_GATE_IP_RIGHTBUS,         0xFFFFFFFF},
+    {"CLKOUT_CMU_RIGHTBUS",          CLKOUT_CMU_RIGHTBUS,          0x00010000},
+    {"CLKOUT_CMU_RIGHTBUS_DIV_STAT", CLKOUT_CMU_RIGHTBUS_DIV_STAT, 0x00000000},
+    /* CMU_TOP registers */
+    {"EPLL_LOCK",               EPLL_LOCK,               0x00000FFF},
+    {"VPLL_LOCK",               VPLL_LOCK,               0x00000FFF},
+    {"EPLL_CON0",               EPLL_CON0,               0x00300301},
+    {"EPLL_CON1",               EPLL_CON1,               0x00000000},
+    {"VPLL_CON0",               VPLL_CON0,               0x00240201},
+    {"VPLL_CON1",               VPLL_CON1,               0x66010464},
+    {"CLK_SRC_TOP0",            CLK_SRC_TOP0,            0x00000000},
+    {"CLK_SRC_TOP1",            CLK_SRC_TOP1,            0x00000000},
+    {"CLK_SRC_CAM",             CLK_SRC_CAM,             0x11111111},
+    {"CLK_SRC_TV",              CLK_SRC_TV,              0x00000000},
+    {"CLK_SRC_MFC",             CLK_SRC_MFC,             0x00000000},
+    {"CLK_SRC_G3D",             CLK_SRC_G3D,             0x00000000},
+    {"CLK_SRC_IMAGE",           CLK_SRC_IMAGE,           0x00000000},
+    {"CLK_SRC_LCD0",            CLK_SRC_LCD0,            0x00001111},
+    {"CLK_SRC_LCD1",            CLK_SRC_LCD1,            0x00001111},
+    {"CLK_SRC_MAUDIO",          CLK_SRC_MAUDIO,          0x00000005},
+    {"CLK_SRC_FSYS",            CLK_SRC_FSYS,            0x00011111},
+    {"CLK_SRC_PERIL0",          CLK_SRC_PERIL0,          0x00011111},
+    {"CLK_SRC_PERIL1",          CLK_SRC_PERIL1,          0x01110055},
+    {"CLK_SRC_MASK_TOP",        CLK_SRC_MASK_TOP,        0x00000001},
+    {"CLK_SRC_MASK_CAM",        CLK_SRC_MASK_CAM,        0x11111111},
+    {"CLK_SRC_MASK_TV",         CLK_SRC_MASK_TV,         0x00000111},
+    {"CLK_SRC_MASK_LCD0",       CLK_SRC_MASK_LCD0,       0x00001111},
+    {"CLK_SRC_MASK_LCD1",       CLK_SRC_MASK_LCD1,       0x00001111},
+    {"CLK_SRC_MASK_MAUDIO",     CLK_SRC_MASK_MAUDIO,     0x00000001},
+    {"CLK_SRC_MASK_FSYS",       CLK_SRC_MASK_FSYS,       0x01011111},
+    {"CLK_SRC_MASK_PERIL0",     CLK_SRC_MASK_PERIL0,     0x00011111},
+    {"CLK_SRC_MASK_PERIL1",     CLK_SRC_MASK_PERIL1,     0x01110111},
+    {"CLK_MUX_STAT_TOP",        CLK_MUX_STAT_TOP,        0x11111111},
+    {"CLK_MUX_STAT_MFC",        CLK_MUX_STAT_MFC,        0x00000111},
+    {"CLK_MUX_STAT_G3D",        CLK_MUX_STAT_G3D,        0x00000111},
+    {"CLK_MUX_STAT_IMAGE",      CLK_MUX_STAT_IMAGE,      0x00000111},
+    {"CLK_DIV_TOP",             CLK_DIV_TOP,             0x00000000},
+    {"CLK_DIV_CAM",             CLK_DIV_CAM,             0x00000000},
+    {"CLK_DIV_TV",              CLK_DIV_TV,              0x00000000},
+    {"CLK_DIV_MFC",             CLK_DIV_MFC,             0x00000000},
+    {"CLK_DIV_G3D",             CLK_DIV_G3D,             0x00000000},
+    {"CLK_DIV_IMAGE",           CLK_DIV_IMAGE,           0x00000000},
+    {"CLK_DIV_LCD0",            CLK_DIV_LCD0,            0x00700000},
+    {"CLK_DIV_LCD1",            CLK_DIV_LCD1,            0x00700000},
+    {"CLK_DIV_MAUDIO",          CLK_DIV_MAUDIO,          0x00000000},
+    {"CLK_DIV_FSYS0",           CLK_DIV_FSYS0,           0x00B00000},
+    {"CLK_DIV_FSYS1",           CLK_DIV_FSYS1,           0x00000000},
+    {"CLK_DIV_FSYS2",           CLK_DIV_FSYS2,           0x00000000},
+    {"CLK_DIV_FSYS3",           CLK_DIV_FSYS3,           0x00000000},
+    {"CLK_DIV_PERIL0",          CLK_DIV_PERIL0,          0x00000000},
+    {"CLK_DIV_PERIL1",          CLK_DIV_PERIL1,          0x00000000},
+    {"CLK_DIV_PERIL2",          CLK_DIV_PERIL2,          0x00000000},
+    {"CLK_DIV_PERIL3",          CLK_DIV_PERIL3,          0x00000000},
+    {"CLK_DIV_PERIL4",          CLK_DIV_PERIL4,          0x00000000},
+    {"CLK_DIV_PERIL5",          CLK_DIV_PERIL5,          0x00000000},
+    {"CLKDIV2_RATIO",           CLKDIV2_RATIO,           0x11111111},
+    {"CLK_DIV_STAT_TOP",        CLK_DIV_STAT_TOP,        0x00000000},
+    {"CLK_DIV_STAT_CAM",        CLK_DIV_STAT_CAM,        0x00000000},
+    {"CLK_DIV_STAT_TV",         CLK_DIV_STAT_TV,         0x00000000},
+    {"CLK_DIV_STAT_MFC",        CLK_DIV_STAT_MFC,        0x00000000},
+    {"CLK_DIV_STAT_G3D",        CLK_DIV_STAT_G3D,        0x00000000},
+    {"CLK_DIV_STAT_IMAGE",      CLK_DIV_STAT_IMAGE,      0x00000000},
+    {"CLK_DIV_STAT_LCD0",       CLK_DIV_STAT_LCD0,       0x00000000},
+    {"CLK_DIV_STAT_LCD1",       CLK_DIV_STAT_LCD1,       0x00000000},
+    {"CLK_DIV_STAT_MAUDIO",     CLK_DIV_STAT_MAUDIO,     0x00000000},
+    {"CLK_DIV_STAT_FSYS0",      CLK_DIV_STAT_FSYS0,      0x00000000},
+    {"CLK_DIV_STAT_FSYS1",      CLK_DIV_STAT_FSYS1,      0x00000000},
+    {"CLK_DIV_STAT_FSYS2",      CLK_DIV_STAT_FSYS2,      0x00000000},
+    {"CLK_DIV_STAT_FSYS3",      CLK_DIV_STAT_FSYS3,      0x00000000},
+    {"CLK_DIV_STAT_PERIL0",     CLK_DIV_STAT_PERIL0,     0x00000000},
+    {"CLK_DIV_STAT_PERIL1",     CLK_DIV_STAT_PERIL1,     0x00000000},
+    {"CLK_DIV_STAT_PERIL2",     CLK_DIV_STAT_PERIL2,     0x00000000},
+    {"CLK_DIV_STAT_PERIL3",     CLK_DIV_STAT_PERIL3,     0x00000000},
+    {"CLK_DIV_STAT_PERIL4",     CLK_DIV_STAT_PERIL4,     0x00000000},
+    {"CLK_DIV_STAT_PERIL5",     CLK_DIV_STAT_PERIL5,     0x00000000},
+    {"CLKDIV2_STAT",            CLKDIV2_STAT,            0x00000000},
+    {"CLK_GATE_SCLK_CAM",       CLK_GATE_SCLK_CAM,       0xFFFFFFFF},
+    {"CLK_GATE_IP_CAM",         CLK_GATE_IP_CAM,         0xFFFFFFFF},
+    {"CLK_GATE_IP_TV",          CLK_GATE_IP_TV,          0xFFFFFFFF},
+    {"CLK_GATE_IP_MFC",         CLK_GATE_IP_MFC,         0xFFFFFFFF},
+    {"CLK_GATE_IP_G3D",         CLK_GATE_IP_G3D,         0xFFFFFFFF},
+    {"CLK_GATE_IP_IMAGE",       CLK_GATE_IP_IMAGE,       0xFFFFFFFF},
+    {"CLK_GATE_IP_LCD0",        CLK_GATE_IP_LCD0,        0xFFFFFFFF},
+    {"CLK_GATE_IP_LCD1",        CLK_GATE_IP_LCD1,        0xFFFFFFFF},
+    {"CLK_GATE_IP_FSYS",        CLK_GATE_IP_FSYS,        0xFFFFFFFF},
+    {"CLK_GATE_IP_GPS",         CLK_GATE_IP_GPS,         0xFFFFFFFF},
+    {"CLK_GATE_IP_PERIL",       CLK_GATE_IP_PERIL,       0xFFFFFFFF},
+    {"CLK_GATE_IP_PERIR",       CLK_GATE_IP_PERIR,       0xFFFFFFFF},
+    {"CLK_GATE_BLOCK",          CLK_GATE_BLOCK,          0xFFFFFFFF},
+    {"CLKOUT_CMU_TOP",          CLKOUT_CMU_TOP,          0x00010000},
+    {"CLKOUT_CMU_TOP_DIV_STAT", CLKOUT_CMU_TOP_DIV_STAT, 0x00000000},
+    /* CMU_DMC registers */
+    {"CLK_SRC_DMC",             CLK_SRC_DMC,             0x00010000},
+    {"CLK_SRC_MASK_DMC",        CLK_SRC_MASK_DMC,        0x00010000},
+    {"CLK_MUX_STAT_DMC",        CLK_MUX_STAT_DMC,        0x11100110},
+    {"CLK_DIV_DMC0",            CLK_DIV_DMC0,            0x00000000},
+    {"CLK_DIV_DMC1",            CLK_DIV_DMC1,            0x00000000},
+    {"CLK_DIV_STAT_DMC0",       CLK_DIV_STAT_DMC0,       0x00000000},
+    {"CLK_DIV_STAT_DMC1",       CLK_DIV_STAT_DMC1,       0x00000000},
+    {"CLK_GATE_IP_DMC",         CLK_GATE_IP_DMC,         0xFFFFFFFF},
+    {"CLKOUT_CMU_DMC",          CLKOUT_CMU_DMC,          0x00010000},
+    {"CLKOUT_CMU_DMC_DIV_STAT", CLKOUT_CMU_DMC_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},
+    {"APLL_LOCK",               APLL_LOCK,               0x00000FFF},
+    {"MPLL_LOCK",               MPLL_LOCK,               0x00000FFF},
+    {"APLL_CON0",               APLL_CON0,               0x00C80601},
+    {"APLL_CON1",               APLL_CON1,               0x0000001C},
+    {"MPLL_CON0",               MPLL_CON0,               0x00C80601},
+    {"MPLL_CON1",               MPLL_CON1,               0x0000001C},
+    /* CMU_CPU registers */
+    {"CLK_SRC_CPU",             CLK_SRC_CPU,             0x00000000},
+    {"CLK_MUX_STAT_CPU",        CLK_MUX_STAT_CPU,        0x00110101},
+    {"CLK_DIV_CPU0",            CLK_DIV_CPU0,            0x00000000},
+    {"CLK_DIV_CPU1",            CLK_DIV_CPU1,            0x00000000},
+    {"CLK_DIV_STAT_CPU0",       CLK_DIV_STAT_CPU0,       0x00000000},
+    {"CLK_DIV_STAT_CPU1",       CLK_DIV_STAT_CPU1,       0x00000000},
+    {"CLK_GATE_SCLK_CPU",       CLK_GATE_SCLK_CPU,       0xFFFFFFFF},
+    {"CLK_GATE_IP_CPU",         CLK_GATE_IP_CPU,         0xFFFFFFFF},
+    {"CLKOUT_CMU_CPU",          CLKOUT_CMU_CPU,          0x00010000},
+    {"CLKOUT_CMU_CPU_DIV_STAT", CLKOUT_CMU_CPU_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},
+};
+
+
+/*
+ * There are five CMUs:
+ *
+ *  CMU_LEFTBUS
+ *  CMU_RIGHTBUS
+ *  CMU_TOP
+ *  CMU_DMC
+ *  CMU_CPU
+ *
+ *  each of them uses 16KB address space for SFRs
+ *
+ *  + 0x4000 because SFR region for CMUs starts at 0x10030000,
+ *  but the first CMU (CMU_LEFTBUS) starts with this offset
+ *
+ */
+#define EXYNOS4210_CMU_REGS_MEM_SIZE   (0x4000 * 5 + 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
+
+typedef struct {
+        SysBusDevice busdev;
+        MemoryRegion iomem;
+
+        uint32_t reg[EXYNOS4210_CMU_REGS_MEM_SIZE];
+
+} Exynos4210CmuState;
+
+#define SOURCES_NUMBER    9
+#define RECIPIENTS_NUMBER 9
+
+typedef struct Exynos4210CmuClockState {
+
+        const char  *name;
+        uint64_t     rate;
+
+        /* Current source clock */
+        struct Exynos4210CmuClockState *source;
+        /*
+         * Available sources. Their order must correspond to CLK_SRC_ register
+         */
+        struct Exynos4210CmuClockState *sources[SOURCES_NUMBER];
+        /* Who uses this clock? */
+        struct Exynos4210CmuClockState *recipients[RECIPIENTS_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;
+
+} Exynos4210CmuClockState;
+
+
+/* Clocks from Clock Pads */
+
+/* It should be used only for testing purposes. XOM_0 is 0 */
+static Exynos4210CmuClockState xxti = {
+        .name       = "XXTI",
+        .rate       = 24000000,
+};
+
+/* Main source. XOM_0 is 1 */
+static Exynos4210CmuClockState xusbxti = {
+        .name       = "XUSBXTI",
+        .rate       = 24000000,
+};
+
+/* PLLs */
+
+static Exynos4210CmuClockState mpll = {
+        .name       = "MPLL",
+        .source     = (XOM_0 ? &xusbxti : &xxti),
+};
+
+static Exynos4210CmuClockState apll = {
+        .name       = "APLL",
+        .source     = (XOM_0 ? &xusbxti : &xxti),
+};
+
+
+/**/
+static Exynos4210CmuClockState sclk_mpll = {
+        .name       = "SCLK_MPLL",
+        .sources    = {XOM_0 ? &xusbxti : &xxti, &mpll},
+        .src_reg    = CLK_SRC_CPU,
+        .mux_shift  = MUX_MPLL_SEL_SHIFT,
+};
+
+static Exynos4210CmuClockState sclk_apll = {
+        .name       = "SCLK_APLL",
+        .sources    = {XOM_0 ? &xusbxti : &xxti, &apll},
+        .src_reg    = CLK_SRC_CPU,
+        .div_reg    = CLK_DIV_CPU0,
+        .mux_shift  = MUX_APLL_SEL_SHIFT,
+        .div_shift  = APLL_RATIO_SHIFT,
+};
+
+static Exynos4210CmuClockState aclk_100 = {
+        .name      = "ACLK_100",
+        .sources   = {&sclk_mpll, &sclk_apll},
+        .src_reg   = CLK_SRC_TOP0,
+        .div_reg   = CLK_DIV_TOP,
+        .mux_shift = MUX_ACLK_100_SEL_SHIFT,
+        .div_shift = ACLK_100_RATIO_SHIFT,
+};
+
+
+/* TODO: add other needed structures for UARTs sources */
+static Exynos4210CmuClockState sclk_uart0 = {
+        .name      = "SCLK_UART0",
+        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
+        .src_reg   = CLK_SRC_PERIL0,
+        .div_reg   = CLK_DIV_PERIL0,
+        .mux_shift = UART0_SEL_SHIFT,
+        .div_shift = UART0_DIV_SHIFT,
+};
+
+static Exynos4210CmuClockState sclk_uart1 = {
+        .name      = "SCLK_UART1",
+        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
+        .src_reg   = CLK_SRC_PERIL0,
+        .div_reg   = CLK_DIV_PERIL0,
+        .mux_shift = UART1_SEL_SHIFT,
+        .div_shift = UART1_DIV_SHIFT,
+};
+
+static Exynos4210CmuClockState sclk_uart2 = {
+        .name      = "SCLK_UART2",
+        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
+        .src_reg   = CLK_SRC_PERIL0,
+        .div_reg   = CLK_DIV_PERIL0,
+        .mux_shift = UART2_SEL_SHIFT,
+        .div_shift = UART2_DIV_SHIFT,
+};
+
+static Exynos4210CmuClockState sclk_uart3 = {
+        .name      = "SCLK_UART3",
+        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
+        .src_reg   = CLK_SRC_PERIL0,
+        .div_reg   = CLK_DIV_PERIL0,
+        .mux_shift = UART3_SEL_SHIFT,
+        .div_shift = UART3_DIV_SHIFT,
+};
+
+static Exynos4210CmuClockState sclk_uart4 = {
+        .name      = "SCLK_UART4",
+        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
+        .src_reg   = CLK_SRC_PERIL0,
+        .div_reg   = CLK_DIV_PERIL0,
+        .mux_shift = UART4_SEL_SHIFT,
+        .div_shift = UART4_DIV_SHIFT,
+};
+
+/*
+ *  This array must correspond to Exynos4210CmuClock enumerator
+ *  which is defined in exynos4210.h file
+ *
+ */
+static Exynos4210CmuClockState *exynos4210_clock[] = {
+        &xxti,
+        &xusbxti,
+        &apll,
+        &mpll,
+        &sclk_apll,
+        &sclk_mpll,
+        &aclk_100,
+        &sclk_uart0,
+        &sclk_uart1,
+        &sclk_uart2,
+        &sclk_uart3,
+        &sclk_uart4,
+        NULL
+};
+
+
+uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock)
+{
+    return exynos4210_clock[clock]->rate;
+}
+
+#ifdef DEBUG_CMU
+/* The only meaning of life - debugging. This functions should be only used
+ * inside PRINT_DEBUG_... macroses
+ */
+static const char *exynos4210_cmu_regname(target_phys_addr_t  offset)
+{
+
+    int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);
+    int i;
+
+    for (i = 0; i < regs_number; i++) {
+        if (offset == exynos4210_cmu_regs[i].offset) {
+            return exynos4210_cmu_regs[i].name;
+        }
+    }
+
+    return NULL;
+}
+#endif
+
+static void exynos4210_cmu_set_pll(void *opaque, target_phys_addr_t offset)
+{
+    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+    uint32_t pdiv, mdiv, sdiv, enable;
+
+    /*
+     * 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;
+
+    switch (offset) {
+
+    case MPLL_CON0:
+        if (mpll.source) {
+            if (enable) {
+                mpll.rate = mdiv * mpll.source->rate / (pdiv * (1 << 
(sdiv-1)));
+            } else {
+                mpll.rate = 0;
+            }
+        } else {
+            hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
+                     mpll.name);
+        }
+        PRINT_DEBUG("mpll.rate: %llu\n", (unsigned long long int)mpll.rate);
+        break;
+
+    case APLL_CON0:
+        if (apll.source) {
+            if (enable) {
+                apll.rate = mdiv * apll.source->rate / (pdiv * (1 << 
(sdiv-1)));
+            } else {
+                apll.rate = 0;
+            }
+        } else {
+            hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
+                     apll.name);
+        }
+        PRINT_DEBUG("apll.rate: %llu\n", (unsigned long long int)apll.rate);
+        break;
+
+    default:
+        hw_error("exynos4210_cmu_set_pll: Bad offset 0x%x\n", (int)offset);
+    }
+
+    s->reg[I_(offset)] |= PLL_LOCKED_MASK;
+}
+
+
+static void
+exynos4210_cmu_set_rate(void *opaque, Exynos4210CmuClockState *clock)
+{
+    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+    int i;
+
+    /* Rates of PLLs are calculated differently than ordinary clocks rates */
+    if (clock == &mpll) {
+        exynos4210_cmu_set_pll(s, MPLL_CON0);
+    } else if (clock == &apll) {
+        exynos4210_cmu_set_pll(s, APLL_CON0);
+    } else if ((clock != &xxti) && (clock != &xusbxti)) {
+        /*
+         *  Not root clock. We don't need calculating rate
+         *  of root clock because it is hard coded.
+         */
+        uint32_t src_index = I_(clock->src_reg);
+        uint32_t div_index = I_(clock->div_reg);
+        clock->source = clock->sources[(s->reg[src_index] >>
+                clock->mux_shift) & 0xf];
+        clock->rate = muldiv64(clock->source->rate, 1,
+                               (((s->reg[div_index] >> clock->div_shift) & 0xf)
+                                       + 1));
+
+        PRINT_DEBUG_EXTEND("SRC: <0x%05x> %s, SHIFT: %d\n",
+                           clock->src_reg,
+                           exynos4210_cmu_regname(clock->src_reg),
+                           clock->mux_shift);
+
+        PRINT_DEBUG("%s [%s:%llu]: %llu\n",
+                    clock->name,
+                    clock->source->name,
+                    (uint64_t)clock->source->rate,
+                    (uint64_t)clock->rate);
+    }
+
+    /* Visit all recipients for given clock */
+    i = 0;
+    do {
+
+        Exynos4210CmuClockState *recipient_clock = clock->recipients[i];
+
+        if (recipient_clock == NULL) {
+            PRINT_DEBUG_EXTEND("%s have %d recipients\n", clock->name, i);
+            break;
+        }
+
+        uint32_t src_index = recipient_clock->src_reg / sizeof(uint32_t);
+        int source_index = s->reg[src_index] >>
+                recipient_clock->mux_shift & 0xf;
+        recipient_clock->source = recipient_clock->sources[source_index];
+
+        if (recipient_clock->source != clock) {
+            break;
+        }
+
+        exynos4210_cmu_set_rate(s, recipient_clock);
+
+        i++;
+    } while (i < RECIPIENTS_NUMBER);
+}
+
+
+static uint64_t exynos4210_cmu_read(void *opaque, target_phys_addr_t offset,
+                                  unsigned size)
+{
+    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+
+    if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+        hw_error("exynos4210_cmu_read: Bad offset 0x%x\n", (int)offset);
+    }
+
+    PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n", offset,
+                       exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
+
+    return s->reg[I_(offset)];
+}
+
+
+static void exynos4210_cmu_write(void *opaque, target_phys_addr_t offset,
+                               uint64_t val, unsigned size)
+{
+    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+    uint32_t pre_val;
+
+    if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+        hw_error("exynos4210_cmu_write: Bad offset 0x%x\n", (int)offset);
+    }
+
+    pre_val = s->reg[I_(offset)];
+    s->reg[I_(offset)] = val;
+
+    PRINT_DEBUG_EXTEND("<0x%05x> %s <- %08x\n", offset,
+                       exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
+
+    switch (offset) {
+
+    case APLL_CON0:
+        val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+        s->reg[I_(offset)] = val;
+        exynos4210_cmu_set_rate(s, &apll);
+        break;
+    case MPLL_CON0:
+        val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+        s->reg[I_(offset)] = val;
+        exynos4210_cmu_set_rate(s, &mpll);
+        break;
+    case CLK_SRC_CPU:
+    {
+        if (val & MUX_APLL_SEL) {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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, &apll);
+            }
+
+        } else {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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 ? &xusbxti : &xxti);
+            }
+        }
+
+
+        if (val & MUX_MPLL_SEL) {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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, &mpll);
+            }
+
+        } else {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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 ? &xusbxti : &xxti);
+            }
+        }
+
+        if (val & MUX_CORE_SEL) {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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, &sclk_mpll);
+            }
+
+        } else {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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 ? &xusbxti : &xxti);
+            }
+        }
+
+        if (val & MUX_HPM_SEL) {
+            exynos4210_cmu_set_rate(s, &sclk_mpll);
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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, &sclk_mpll);
+            }
+
+        } else {
+            s->reg[I_(CLK_MUX_STAT_CPU)] =
+                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(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 ? &xusbxti : &xxti);
+            }
+        }
+    }
+    break;
+    case CLK_DIV_CPU0:
+        exynos4210_cmu_set_rate(s, &sclk_apll);
+        exynos4210_cmu_set_rate(s, &sclk_mpll);
+        break;
+    case CLK_SRC_TOP0:
+    case CLK_DIV_TOP:
+        exynos4210_cmu_set_rate(s, &aclk_100);
+        break;
+    default:
+        break;
+    }
+}
+
+
+static const MemoryRegionOps exynos4210_cmu_ops = {
+        .read = exynos4210_cmu_read,
+        .write = exynos4210_cmu_write,
+        .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static void exynos4210_cmu_reset(void *opaque)
+{
+    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
+    int i = 0, j = 0, n = 0;
+    int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);
+    uint32_t index = 0;
+
+    /* Set default values for registers */
+    for (i = 0; i < regs_number; i++) {
+        index = (exynos4210_cmu_regs[i].offset) / sizeof(uint32_t);
+        s->reg[index] = exynos4210_cmu_regs[i].reset_value;
+    }
+
+    /* clear recipients array from previous reset */
+    for (i = 0; i < CLOCKS_NUMBER; i++) {
+        bzero(exynos4210_clock[i]->recipients,
+              RECIPIENTS_NUMBER * sizeof(Exynos4210CmuClockState *));
+    }
+
+    /*
+     * Here we fill '.recipients' fields in all clocks. Also we fill empty
+     * 'sources[]' arrays by values of 'source' fields (it is necessary
+     * for set rate, for example). If 'sources[]' array and 'source' field
+     * is empty simultaneously we get hw_error.
+     *
+     */
+    for (i = 0; i < CLOCKS_NUMBER; i++) {
+
+        /* visit all clocks in the exynos4210_clock */
+
+        PRINT_DEBUG("[SOURCES] %s: ", exynos4210_clock[i]->name);
+
+        j = 0;
+        do { /* visit all sources for current clock (exynos4210_clock[i]) */
+
+            if ((exynos4210_clock[i]->sources[j] == NULL)) {
+
+                if (j == 0) { /* check if we have empty '.sources[]' array */
+                    if (exynos4210_clock[i]->source != NULL) {
+                        exynos4210_clock[i]->sources[j] =
+                                exynos4210_clock[i]->source;
+                    } else {
+                        /*
+                         * We haven't any defined sources for this clock. Error
+                         * during definition of appropriate clock structure
+                         *
+                         */
+                        if ((exynos4210_clock[i] != &xusbxti) &&
+                            (exynos4210_clock[i] != &xxti)) {
+
+                            hw_error("exynos4210_cmu_reset:"
+                                    "There aren't any sources for %s clock!\n",
+                                     exynos4210_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 */
+                }
+
+            }
+
+            PRINT_DEBUG_SIMPLE(" %s", exynos4210_clock[i]->sources[j]->name);
+
+            /*
+             *  find first empty place in 'recipients[]' array of
+             *  current 'sources' element and put current clock there
+             */
+            n = 0;
+            do {
+                if ((exynos4210_clock[i]->sources[j]->recipients[n]) == NULL) {
+                    exynos4210_clock[i]->sources[j]->recipients[n] =
+                            exynos4210_clock[i];
+                    break;
+                }
+                n++;
+            } while (n < RECIPIENTS_NUMBER);
+
+            j++;
+
+        } while (j < SOURCES_NUMBER);
+
+        PRINT_DEBUG_SIMPLE("\n");
+
+    } /* CLOCKS_NUMBER */
+
+#ifdef DEBUG_CMU
+    for (i = 0; i < CLOCKS_NUMBER; i++) {
+        PRINT_DEBUG("[RECIPIENTS] %s: ", exynos4210_clock[i]->name);
+        for (j = 0;
+             (j < RECIPIENTS_NUMBER) &&
+             ((exynos4210_clock[i]->recipients[j]) != NULL);
+             j++) {
+            PRINT_DEBUG_SIMPLE("%s ", 
exynos4210_clock[i]->recipients[j]->name);
+        }
+        PRINT_DEBUG_SIMPLE("\n");
+    }
+#endif
+
+    exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
+}
+
+static const VMStateDescription vmstate_exynos4210_cmu = {
+        .name = "exynos4210.cmu",
+        .version_id = 1,
+        .minimum_version_id = 1,
+        .minimum_version_id_old = 1,
+        .fields = (VMStateField[]) {
+        /*
+         * TODO: Maybe we should save Exynos4210CmuClockState structs as well
+         */
+            VMSTATE_UINT32_ARRAY(reg, Exynos4210CmuState,
+                                 EXYNOS4210_CMU_REGS_MEM_SIZE),
+            VMSTATE_END_OF_LIST()
+        }
+};
+
+static int exynos4210_cmu_init(SysBusDevice *dev)
+{
+    Exynos4210CmuState *s = FROM_SYSBUS(Exynos4210CmuState, dev);
+
+    /* memory mapping */
+    memory_region_init_io(&s->iomem, &exynos4210_cmu_ops, s, "exynos4210.cmu",
+                          EXYNOS4210_CMU_REGS_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    qemu_register_reset(exynos4210_cmu_reset, s);
+
+    vmstate_register(&dev->qdev, -1, &vmstate_exynos4210_cmu, s);
+
+    exynos4210_cmu_reset(s);
+
+    return 0;
+}
+
+
+static void exynos4210_cmu_register(void)
+{
+    sysbus_register_dev("exynos4210.cmu",
+                        sizeof(Exynos4210CmuState),
+                        exynos4210_cmu_init);
+}
+
+
+device_init(exynos4210_cmu_register)
-- 
1.7.4.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]