diff mbox

[1/2] ARM: exynos4210: CMU support

Message ID 1340985860-23147-2-git-send-email-m.kozlov@samsung.com
State New
Headers show

Commit Message

Maksim E. Kozlov June 29, 2012, 4:04 p.m. UTC
Add exynos4210 Clock Management Units emulation

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

Comments

Blue Swirl June 29, 2012, 8:26 p.m. UTC | #1
On Fri, Jun 29, 2012 at 4:04 PM, Maksim Kozlov <m.kozlov@samsung.com> wrote:
> Add exynos4210 Clock Management Units emulation
>
> Signed-off-by: Maksim Kozlov <m.kozlov@samsung.com>
> ---
>  hw/arm/Makefile.objs |    1 +
>  hw/exynos4210.c      |   16 +
>  hw/exynos4210.h      |   42 ++
>  hw/exynos4210_cmu.c  | 1462 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1521 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..f3e5a30
> --- /dev/null
> +++ b/hw/exynos4210_cmu.c
> @@ -0,0 +1,1462 @@
> +/*
> + *  exynos4210 Clock Management Units (CMUs) Emulation
> + *
> + *  Copyright (C) 2011 Samsung Electronics Co Ltd.
> + *    Maksim Kozlov, <m.kozlov@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         0
> +#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 Exynos4210CmuReg exynos4210_cmu_leftbus_regs[] = {

All these structures could be const, I think. I hope you are not
attempting to use static state for the actual register contents.

> +    /* 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 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 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 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 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 */
> +    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 Exynos4210ClockState xxti = {
> +    .name       = "XXTI",
> +    .id         = EXYNOS4210_XXTI,
> +    .rate       = 24000000,
> +    .cmu_id     = UNSPECIFIED_CMU,
> +};
> +
> +/* Main source. XOM_0 is 1 */
> +static Exynos4210ClockState xusbxti = {
> +    .name       = "XUSBXTI",
> +    .id         = EXYNOS4210_XUSBXTI,
> +    .rate       = 24000000,
> +    .cmu_id     = UNSPECIFIED_CMU,
> +};
> +
> +/* PLLs */
> +
> +static 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 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 Exynos4210ClockState sclk_hdmi24m = {
> +    .name    = "SCLK_HDMI24M",
> +    .id      = EXYNOS4210_SCLK_HDMI24M,
> +    .rate    = 24000000,
> +    .cmu_id  = UNSPECIFIED_CMU,
> +};
> +
> +static Exynos4210ClockState sclk_usbphy0 = {
> +    .name    = "SCLK_USBPHY0",
> +    .id      = EXYNOS4210_SCLK_USBPHY0,
> +    .rate    = 24000000,
> +    .cmu_id  = UNSPECIFIED_CMU,
> +};
> +
> +static 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 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 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 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 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 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 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 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 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 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 char exynos4210_cmu_path[][13] = {

'const'

> +    "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  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 = (Exynos4210CmuState *)opaque;

Useless cast.

> +    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", __FUNCTION__, 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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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;
> +        }
> +    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
>
>
Maksim E. Kozlov July 2, 2012, 9:53 a.m. UTC | #2
Ok. Thanks for your remarks.

30.06.2012 00:26, Blue Swirl пишет:
> On Fri, Jun 29, 2012 at 4:04 PM, Maksim Kozlov<m.kozlov@samsung.com>  wrote:
>> Add exynos4210 Clock Management Units emulation
>>
>> Signed-off-by: Maksim Kozlov<m.kozlov@samsung.com>
>> ---
>>   hw/arm/Makefile.objs |    1 +
>>   hw/exynos4210.c      |   16 +
>>   hw/exynos4210.h      |   42 ++
>>   hw/exynos4210_cmu.c  | 1462 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 1521 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..f3e5a30
>> --- /dev/null
>> +++ b/hw/exynos4210_cmu.c
>> @@ -0,0 +1,1462 @@
>> +/*
>> + *  exynos4210 Clock Management Units (CMUs) Emulation
>> + *
>> + *  Copyright (C) 2011 Samsung Electronics Co Ltd.
>> + *    Maksim Kozlov,<m.kozlov@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         0
>> +#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 Exynos4210CmuReg exynos4210_cmu_leftbus_regs[] = {
>
> All these structures could be const, I think. I hope you are not
> attempting to use static state for the actual register contents.
>
>> +    /* 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 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 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 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 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 */
>> +    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 Exynos4210ClockState xxti = {
>> +    .name       = "XXTI",
>> +    .id         = EXYNOS4210_XXTI,
>> +    .rate       = 24000000,
>> +    .cmu_id     = UNSPECIFIED_CMU,
>> +};
>> +
>> +/* Main source. XOM_0 is 1 */
>> +static Exynos4210ClockState xusbxti = {
>> +    .name       = "XUSBXTI",
>> +    .id         = EXYNOS4210_XUSBXTI,
>> +    .rate       = 24000000,
>> +    .cmu_id     = UNSPECIFIED_CMU,
>> +};
>> +
>> +/* PLLs */
>> +
>> +static 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 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 Exynos4210ClockState sclk_hdmi24m = {
>> +    .name    = "SCLK_HDMI24M",
>> +    .id      = EXYNOS4210_SCLK_HDMI24M,
>> +    .rate    = 24000000,
>> +    .cmu_id  = UNSPECIFIED_CMU,
>> +};
>> +
>> +static Exynos4210ClockState sclk_usbphy0 = {
>> +    .name    = "SCLK_USBPHY0",
>> +    .id      = EXYNOS4210_SCLK_USBPHY0,
>> +    .rate    = 24000000,
>> +    .cmu_id  = UNSPECIFIED_CMU,
>> +};
>> +
>> +static 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 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 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 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 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 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 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 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 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 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 char exynos4210_cmu_path[][13] = {
>
> 'const'
>
>> +    "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  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 = (Exynos4210CmuState *)opaque;
>
> Useless cast.
>
>> +    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", __FUNCTION__, 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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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;
>> +        }
>> +    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
>>
>>
diff mbox

Patch

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..f3e5a30
--- /dev/null
+++ b/hw/exynos4210_cmu.c
@@ -0,0 +1,1462 @@ 
+/*
+ *  exynos4210 Clock Management Units (CMUs) Emulation
+ *
+ *  Copyright (C) 2011 Samsung Electronics Co Ltd.
+ *    Maksim Kozlov, <m.kozlov@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         0
+#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 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 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 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 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 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 */
+    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 Exynos4210ClockState xxti = {
+    .name       = "XXTI",
+    .id         = EXYNOS4210_XXTI,
+    .rate       = 24000000,
+    .cmu_id     = UNSPECIFIED_CMU,
+};
+
+/* Main source. XOM_0 is 1 */
+static Exynos4210ClockState xusbxti = {
+    .name       = "XUSBXTI",
+    .id         = EXYNOS4210_XUSBXTI,
+    .rate       = 24000000,
+    .cmu_id     = UNSPECIFIED_CMU,
+};
+
+/* PLLs */
+
+static 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 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 Exynos4210ClockState sclk_hdmi24m = {
+    .name    = "SCLK_HDMI24M",
+    .id      = EXYNOS4210_SCLK_HDMI24M,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static Exynos4210ClockState sclk_usbphy0 = {
+    .name    = "SCLK_USBPHY0",
+    .id      = EXYNOS4210_SCLK_USBPHY0,
+    .rate    = 24000000,
+    .cmu_id  = UNSPECIFIED_CMU,
+};
+
+static 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 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 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 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 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 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 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 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 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 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 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  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 = (Exynos4210CmuState *)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", __FUNCTION__, 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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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 = (Exynos4210CmuState *)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;
+        }
+    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)