Dear Peter,

> So, how does this compare with the struct clk in hw/omap_clk.c ?
> Is there a useful generalisation we can make rather than having
> every SOC model doing its own thing for clock trees?


  It's definitely good approach to make similar things in one place, but 
existing TI Omap clocks model is too different (both hardware and API) to be 
used in Exynos SoC and refactoring of Omap model is out of our project scope.

  For now Exynos CMU is used only for optional UART support so we could just 
replace it with constant frequencies in components.
  What is your opinion - cut CMU or keep it as base for some future 
functionality development?

Thanks,
     Dmitry Solodkiy,
     Mobile SW PL, Advanced Software Group,
     Moscow R&D center, Samsung Electronics


-----Original Message-----
From: Peter Maydell [mailto:peter.mayd...@linaro.org] 
Sent: Tuesday, December 13, 2011 2:45 AM
To: Evgeny Voevodin
Cc: qemu-devel@nongnu.org; m.koz...@samsung.com; d.solod...@samsung.com
Subject: Re: [Qemu-devel] [PATCH v3 02/14] ARM: exynos4210: CMU support

On 12 December 2011 06:43, Evgeny Voevodin <e.voevo...@samsung.com> wrote:
> From: Maksim Kozlov <m.koz...@samsung.com>
>
> Add exynos4210 Clock Management Units emulation
>
> Signed-off-by: Evgeny Voevodin <e.voevo...@samsung.com>
> ---
>  Makefile.target     |    2 +-
>  hw/exynos4210.c     |    7 +
>  hw/exynos4210.h     |   22 +
>  hw/exynos4210_cmu.c | 1146 
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1176 insertions(+), 1 deletions(-)
>  create mode 100644 hw/exynos4210_cmu.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 624a142..ce4f1f8 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -344,7 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o 
> arm_timer.o
>  obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o 
> pl190.o
>  obj-arm-y += versatile_pci.o
>  obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
> -obj-arm-y += exynos4210.o
> +obj-arm-y += exynos4210.o exynos4210_cmu.o
>  obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
>  obj-arm-y += pl061.o
>  obj-arm-y += arm-semi.o
> diff --git a/hw/exynos4210.c b/hw/exynos4210.c
> index 1550016..1a6e353 100644
> --- a/hw/exynos4210.c
> +++ b/hw/exynos4210.c
> @@ -60,6 +60,10 @@
>
>  #define EXYNOS4210_BASE_BOOT_ADDR           EXYNOS4210_DRAM0_BASE_ADDR
>
> +/* SFR Base Address for CMUs */
> +#define EXYNOS4210_CMU_BASE_ADDR            0x10030000
> +
> +
>  static struct arm_boot_info exynos4210_binfo = {
>         .loader_start     = EXYNOS4210_BASE_BOOT_ADDR,
>  };
> @@ -172,6 +176,9 @@ static void exynos4210_init(ram_addr_t ram_size,
>     memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
>             dram0_mem);
>
> +    /* CMU */
> +    sysbus_create_simple("exynos4210.cmu", EXYNOS4210_CMU_BASE_ADDR, NULL);
> +
>     /*** Load kernel ***/
>
>     exynos4210_binfo.ram_size = ram_size;
> diff --git a/hw/exynos4210.h b/hw/exynos4210.h
> index 7137630..683a4a6 100644
> --- a/hw/exynos4210.h
> +++ b/hw/exynos4210.h
> @@ -31,4 +31,26 @@
>
>  #define EXYNOS4210_MAX_CPUS                2
>
> +/*
> + * Interface for exynos4210 Clock Management Units (CMUs)
> + */
> +
> +typedef enum {
> +    XXTI,
> +    XUSBXTI,
> +    APLL,
> +    MPLL,
> +    SCLK_APLL,
> +    SCLK_MPLL,
> +    ACLK_100,
> +    SCLK_UART0,
> +    SCLK_UART1,
> +    SCLK_UART2,
> +    SCLK_UART3,
> +    SCLK_UART4,
> +    CLOCKS_NUMBER
> +} Exynos4210CmuClock;
> +
> +uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock);
> +
>  #endif /* EXYNOS4210_H_ */
> diff --git a/hw/exynos4210_cmu.c b/hw/exynos4210_cmu.c
> new file mode 100644
> index 0000000..fe4100c
> --- /dev/null
> +++ b/hw/exynos4210_cmu.c
> @@ -0,0 +1,1146 @@
> +/*
> + *  exynos4210 Clock Management Units (CMUs) Emulation
> + *
> + *  Copyright (C) 2011 Samsung Electronics Co Ltd.
> + *    Maksim Kozlov, <m.koz...@samsung.com>
> + *
> + *  Created on: 07.2011
> + *
> + *
> + *  This program is free software; you can redistribute it and/or modify it
> + *  under the terms of the GNU General Public License as published by the
> + *  Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful, but 
> WITHOUT
> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + *  for more details.
> + *
> + *  You should have received a copy of the GNU General Public License along
> + *  with this program; if not, write to the Free Software Foundation, Inc., 
> 51
> + *  Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#include "sysbus.h"
> +
> +#include "exynos4210.h"
> +
> +
> +
> +#undef DEBUG_CMU
> +
> +//#define DEBUG_CMU
> +//#define DEBUG_CMU_EXTEND
> +
> +
> +#define  PRINT_DEBUG(fmt, args...)  \
> +        do {} while (0)
> +#define  PRINT_DEBUG_SIMPLE(fmt, args...)  \
> +        do {} while (0)
> +#define  PRINT_DEBUG_EXTEND(fmt, args...) \
> +        do {} while (0)
> +#define  PRINT_ERROR(fmt, args...) \
> +        do { \
> +            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
> +        } while (0)
> +
> +
> +#ifdef DEBUG_CMU
> +
> +    #undef PRINT_DEBUG
> +    #define  PRINT_DEBUG(fmt, args...)  \
> +        do { \
> +            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
> +        } while (0)
> +
> +    #undef PRINT_DEBUG_SIMPLE
> +    #define  PRINT_DEBUG_SIMPLE(fmt, args...)  \
> +        do { \
> +            fprintf(stderr, fmt, ## args); \
> +        } while (0)
> +
> +#ifdef DEBUG_CMU_EXTEND
> +
> +    #undef PRINT_DEBUG_EXTEND
> +    #define  PRINT_DEBUG_EXTEND(fmt, args...) \
> +        do { \
> +            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
> +        } while (0)
> +
> +#endif /* EXTEND */
> +#endif
> +
> +
> +/*
> + *  Offsets for CMUs registers
> + */
> +
> +/* CMU_LEFTBUS registers */
> +#define     CLK_SRC_LEFTBUS              0x04200
> +#define     CLK_MUX_STAT_LEFTBUS         0x04400
> +#define     CLK_DIV_LEFTBUS              0x04500
> +#define     CLK_DIV_STAT_LEFTBUS         0x04600
> +#define     CLK_GATE_IP_LEFTBUS          0x04800
> +#define     CLKOUT_CMU_LEFTBUS           0x04A00
> +#define     CLKOUT_CMU_LEFTBUS_DIV_STAT  0x04A04
> +/* CMU_RIGHTBUS registers */
> +#define     CLK_SRC_RIGHTBUS             0x08200
> +#define     CLK_MUX_STAT_RIGHTBUS        0x08400
> +#define     CLK_DIV_RIGHTBUS             0x08500
> +#define     CLK_DIV_STAT_RIGHTBUS        0x08600
> +#define     CLK_GATE_IP_RIGHTBUS         0x08800
> +#define     CLKOUT_CMU_RIGHTBUS          0x08A00
> +#define     CLKOUT_CMU_RIGHTBUS_DIV_STAT 0x08A04
> +/* CMU_TOP registers */
> +#define     EPLL_LOCK                    0x0C010
> +#define     VPLL_LOCK                    0x0C020
> +#define     EPLL_CON0                    0x0C110
> +#define     EPLL_CON1                    0x0C114
> +#define     VPLL_CON0                    0x0C120
> +#define     VPLL_CON1                    0x0C124
> +#define     CLK_SRC_TOP0                 0x0C210
> +#define     CLK_SRC_TOP1                 0x0C214
> +#define     CLK_SRC_CAM                  0x0C220
> +#define     CLK_SRC_TV                   0x0C224
> +#define     CLK_SRC_MFC                  0x0C228
> +#define     CLK_SRC_G3D                  0x0C22C
> +#define     CLK_SRC_IMAGE                0x0C230
> +#define     CLK_SRC_LCD0                 0x0C234
> +#define     CLK_SRC_LCD1                 0x0C238
> +#define     CLK_SRC_MAUDIO               0x0C23C
> +#define     CLK_SRC_FSYS                 0x0C240
> +#define     CLK_SRC_PERIL0               0x0C250
> +#define     CLK_SRC_PERIL1               0x0C254
> +#define     CLK_SRC_MASK_TOP             0x0C310
> +#define     CLK_SRC_MASK_CAM             0x0C320
> +#define     CLK_SRC_MASK_TV              0x0C324
> +#define     CLK_SRC_MASK_LCD0            0x0C334
> +#define     CLK_SRC_MASK_LCD1            0x0C338
> +#define     CLK_SRC_MASK_MAUDIO          0x0C33C
> +#define     CLK_SRC_MASK_FSYS            0x0C340
> +#define     CLK_SRC_MASK_PERIL0          0x0C350
> +#define     CLK_SRC_MASK_PERIL1          0x0C354
> +#define     CLK_MUX_STAT_TOP             0x0C410
> +#define     CLK_MUX_STAT_MFC             0x0C428
> +#define     CLK_MUX_STAT_G3D             0x0C42C
> +#define     CLK_MUX_STAT_IMAGE           0x0C430
> +#define     CLK_DIV_TOP                  0x0C510
> +#define     CLK_DIV_CAM                  0x0C520
> +#define     CLK_DIV_TV                   0x0C524
> +#define     CLK_DIV_MFC                  0x0C528
> +#define     CLK_DIV_G3D                  0x0C52C
> +#define     CLK_DIV_IMAGE                0x0C530
> +#define     CLK_DIV_LCD0                 0x0C534
> +#define     CLK_DIV_LCD1                 0x0C538
> +#define     CLK_DIV_MAUDIO               0x0C53C
> +#define     CLK_DIV_FSYS0                0x0C540
> +#define     CLK_DIV_FSYS1                0x0C544
> +#define     CLK_DIV_FSYS2                0x0C548
> +#define     CLK_DIV_FSYS3                0x0C54C
> +#define     CLK_DIV_PERIL0               0x0C550
> +#define     CLK_DIV_PERIL1               0x0C554
> +#define     CLK_DIV_PERIL2               0x0C558
> +#define     CLK_DIV_PERIL3               0x0C55C
> +#define     CLK_DIV_PERIL4               0x0C560
> +#define     CLK_DIV_PERIL5               0x0C564
> +#define     CLKDIV2_RATIO                0x0C580
> +#define     CLK_DIV_STAT_TOP             0x0C610
> +#define     CLK_DIV_STAT_CAM             0x0C620
> +#define     CLK_DIV_STAT_TV              0x0C624
> +#define     CLK_DIV_STAT_MFC             0x0C628
> +#define     CLK_DIV_STAT_G3D             0x0C62C
> +#define     CLK_DIV_STAT_IMAGE           0x0C630
> +#define     CLK_DIV_STAT_LCD0            0x0C634
> +#define     CLK_DIV_STAT_LCD1            0x0C638
> +#define     CLK_DIV_STAT_MAUDIO          0x0C63C
> +#define     CLK_DIV_STAT_FSYS0           0x0C640
> +#define     CLK_DIV_STAT_FSYS1           0x0C644
> +#define     CLK_DIV_STAT_FSYS2           0x0C648
> +#define     CLK_DIV_STAT_FSYS3           0x0C64C
> +#define     CLK_DIV_STAT_PERIL0          0x0C650
> +#define     CLK_DIV_STAT_PERIL1          0x0C654
> +#define     CLK_DIV_STAT_PERIL2          0x0C658
> +#define     CLK_DIV_STAT_PERIL3          0x0C65C
> +#define     CLK_DIV_STAT_PERIL4          0x0C660
> +#define     CLK_DIV_STAT_PERIL5          0x0C664
> +#define     CLKDIV2_STAT                 0x0C680
> +#define     CLK_GATE_SCLK_CAM            0x0C820
> +#define     CLK_GATE_IP_CAM              0x0C920
> +#define     CLK_GATE_IP_TV               0x0C924
> +#define     CLK_GATE_IP_MFC              0x0C928
> +#define     CLK_GATE_IP_G3D              0x0C92C
> +#define     CLK_GATE_IP_IMAGE            0x0C930
> +#define     CLK_GATE_IP_LCD0             0x0C934
> +#define     CLK_GATE_IP_LCD1             0x0C938
> +#define     CLK_GATE_IP_FSYS             0x0C940
> +#define     CLK_GATE_IP_GPS              0x0C94C
> +#define     CLK_GATE_IP_PERIL            0x0C950
> +#define     CLK_GATE_IP_PERIR            0x0C960
> +#define     CLK_GATE_BLOCK               0x0C970
> +#define     CLKOUT_CMU_TOP               0x0CA00
> +#define     CLKOUT_CMU_TOP_DIV_STAT      0x0CA04
> +/* CMU_DMC registers */
> +#define     CLK_SRC_DMC                  0x10200
> +#define     CLK_SRC_MASK_DMC             0x10300
> +#define     CLK_MUX_STAT_DMC             0x10400
> +#define     CLK_DIV_DMC0                 0x10500
> +#define     CLK_DIV_DMC1                 0x10504
> +#define     CLK_DIV_STAT_DMC0            0x10600
> +#define     CLK_DIV_STAT_DMC1            0x10604
> +#define     CLK_GATE_IP_DMC              0x10900
> +#define     CLKOUT_CMU_DMC               0x10A00
> +#define     CLKOUT_CMU_DMC_DIV_STAT      0x10A04
> +#define     DCGIDX_MAP0                  0x11000
> +#define     DCGIDX_MAP1                  0x11004
> +#define     DCGIDX_MAP2                  0x11008
> +#define     DCGPERF_MAP0                 0x11020
> +#define     DCGPERF_MAP1                 0x11024
> +#define     DVCIDX_MAP                   0x11040
> +#define     FREQ_CPU                     0x11060
> +#define     FREQ_DPM                     0x11064
> +#define     DVSEMCLK_EN                  0x11080
> +#define     MAXPERF                      0x11084
> +#define     APLL_LOCK                    0x14000
> +#define     MPLL_LOCK                    0x14008
> +#define     APLL_CON0                    0x14100
> +#define     APLL_CON1                    0x14104
> +#define     MPLL_CON0                    0x14108
> +#define     MPLL_CON1                    0x1410C
> +/* CMU_CPU registers */
> +#define     CLK_SRC_CPU                  0x14200
> +#define     CLK_MUX_STAT_CPU             0x14400
> +#define     CLK_DIV_CPU0                 0x14500
> +#define     CLK_DIV_CPU1                 0x14504
> +#define     CLK_DIV_STAT_CPU0            0x14600
> +#define     CLK_DIV_STAT_CPU1            0x14604
> +#define     CLK_GATE_SCLK_CPU            0x14800
> +#define     CLK_GATE_IP_CPU              0x14900
> +#define     CLKOUT_CMU_CPU               0x14A00
> +#define     CLKOUT_CMU_CPU_DIV_STAT      0x14A04
> +#define     ARMCLK_STOPCTRL              0x15000
> +#define     ATCLK_STOPCTRL               0x15004
> +#define     PARITYFAIL_STATUS            0x15010
> +#define     PARITYFAIL_CLEAR             0x15014
> +#define     PWR_CTRL                     0x15020
> +#define     APLL_CON0_L8                 0x15100
> +#define     APLL_CON0_L7                 0x15104
> +#define     APLL_CON0_L6                 0x15108
> +#define     APLL_CON0_L5                 0x1510C
> +#define     APLL_CON0_L4                 0x15110
> +#define     APLL_CON0_L3                 0x15114
> +#define     APLL_CON0_L2                 0x15118
> +#define     APLL_CON0_L1                 0x1511C
> +#define     IEM_CONTROL                  0x15120
> +#define     APLL_CON1_L8                 0x15200
> +#define     APLL_CON1_L7                 0x15204
> +#define     APLL_CON1_L6                 0x15208
> +#define     APLL_CON1_L5                 0x1520C
> +#define     APLL_CON1_L4                 0x15210
> +#define     APLL_CON1_L3                 0x15214
> +#define     APLL_CON1_L2                 0x15218
> +#define     APLL_CON1_L1                 0x1521C
> +#define     CLKDIV_IEM_L8                0x15300
> +#define     CLKDIV_IEM_L7                0x15304
> +#define     CLKDIV_IEM_L6                0x15308
> +#define     CLKDIV_IEM_L5                0x1530C
> +#define     CLKDIV_IEM_L4                0x15310
> +#define     CLKDIV_IEM_L3                0x15314
> +#define     CLKDIV_IEM_L2                0x15318
> +#define     CLKDIV_IEM_L1                0x1531C
> +
> +
> +typedef struct Exynos4210CmuReg {
> +        const char *name; /* for debugging */
> +        uint32_t    offset;
> +        uint32_t    reset_value;
> +} Exynos4210CmuReg;
> +
> +
> +static Exynos4210CmuReg exynos4210_cmu_regs[] = {
> +    /* CMU_LEFTBUS registers */
> +    {"CLK_SRC_LEFTBUS",              CLK_SRC_LEFTBUS,              
> 0x00000000},
> +    {"CLK_MUX_STAT_LEFTBUS",         CLK_MUX_STAT_LEFTBUS,         
> 0x00000001},
> +    {"CLK_DIV_LEFTBUS",              CLK_DIV_LEFTBUS,              
> 0x00000000},
> +    {"CLK_DIV_STAT_LEFTBUS",         CLK_DIV_STAT_LEFTBUS,         
> 0x00000000},
> +    {"CLK_GATE_IP_LEFTBUS",          CLK_GATE_IP_LEFTBUS,          
> 0xFFFFFFFF},
> +    {"CLKOUT_CMU_LEFTBUS",           CLKOUT_CMU_LEFTBUS,           
> 0x00010000},
> +    {"CLKOUT_CMU_LEFTBUS_DIV_STAT",  CLKOUT_CMU_LEFTBUS_DIV_STAT,  
> 0x00000000},
> +    /* CMU_RIGHTBUS registers */
> +    {"CLK_SRC_RIGHTBUS",             CLK_SRC_RIGHTBUS,             
> 0x00000000},
> +    {"CLK_MUX_STAT_RIGHTBUS",        CLK_MUX_STAT_RIGHTBUS,        
> 0x00000001},
> +    {"CLK_DIV_RIGHTBUS",             CLK_DIV_RIGHTBUS,             
> 0x00000000},
> +    {"CLK_DIV_STAT_RIGHTBUS",        CLK_DIV_STAT_RIGHTBUS,        
> 0x00000000},
> +    {"CLK_GATE_IP_RIGHTBUS",         CLK_GATE_IP_RIGHTBUS,         
> 0xFFFFFFFF},
> +    {"CLKOUT_CMU_RIGHTBUS",          CLKOUT_CMU_RIGHTBUS,          
> 0x00010000},
> +    {"CLKOUT_CMU_RIGHTBUS_DIV_STAT", CLKOUT_CMU_RIGHTBUS_DIV_STAT, 
> 0x00000000},
> +    /* CMU_TOP registers */
> +    {"EPLL_LOCK",               EPLL_LOCK,               0x00000FFF},
> +    {"VPLL_LOCK",               VPLL_LOCK,               0x00000FFF},
> +    {"EPLL_CON0",               EPLL_CON0,               0x00300301},
> +    {"EPLL_CON1",               EPLL_CON1,               0x00000000},
> +    {"VPLL_CON0",               VPLL_CON0,               0x00240201},
> +    {"VPLL_CON1",               VPLL_CON1,               0x66010464},
> +    {"CLK_SRC_TOP0",            CLK_SRC_TOP0,            0x00000000},
> +    {"CLK_SRC_TOP1",            CLK_SRC_TOP1,            0x00000000},
> +    {"CLK_SRC_CAM",             CLK_SRC_CAM,             0x11111111},
> +    {"CLK_SRC_TV",              CLK_SRC_TV,              0x00000000},
> +    {"CLK_SRC_MFC",             CLK_SRC_MFC,             0x00000000},
> +    {"CLK_SRC_G3D",             CLK_SRC_G3D,             0x00000000},
> +    {"CLK_SRC_IMAGE",           CLK_SRC_IMAGE,           0x00000000},
> +    {"CLK_SRC_LCD0",            CLK_SRC_LCD0,            0x00001111},
> +    {"CLK_SRC_LCD1",            CLK_SRC_LCD1,            0x00001111},
> +    {"CLK_SRC_MAUDIO",          CLK_SRC_MAUDIO,          0x00000005},
> +    {"CLK_SRC_FSYS",            CLK_SRC_FSYS,            0x00011111},
> +    {"CLK_SRC_PERIL0",          CLK_SRC_PERIL0,          0x00011111},
> +    {"CLK_SRC_PERIL1",          CLK_SRC_PERIL1,          0x01110055},
> +    {"CLK_SRC_MASK_TOP",        CLK_SRC_MASK_TOP,        0x00000001},
> +    {"CLK_SRC_MASK_CAM",        CLK_SRC_MASK_CAM,        0x11111111},
> +    {"CLK_SRC_MASK_TV",         CLK_SRC_MASK_TV,         0x00000111},
> +    {"CLK_SRC_MASK_LCD0",       CLK_SRC_MASK_LCD0,       0x00001111},
> +    {"CLK_SRC_MASK_LCD1",       CLK_SRC_MASK_LCD1,       0x00001111},
> +    {"CLK_SRC_MASK_MAUDIO",     CLK_SRC_MASK_MAUDIO,     0x00000001},
> +    {"CLK_SRC_MASK_FSYS",       CLK_SRC_MASK_FSYS,       0x01011111},
> +    {"CLK_SRC_MASK_PERIL0",     CLK_SRC_MASK_PERIL0,     0x00011111},
> +    {"CLK_SRC_MASK_PERIL1",     CLK_SRC_MASK_PERIL1,     0x01110111},
> +    {"CLK_MUX_STAT_TOP",        CLK_MUX_STAT_TOP,        0x11111111},
> +    {"CLK_MUX_STAT_MFC",        CLK_MUX_STAT_MFC,        0x00000111},
> +    {"CLK_MUX_STAT_G3D",        CLK_MUX_STAT_G3D,        0x00000111},
> +    {"CLK_MUX_STAT_IMAGE",      CLK_MUX_STAT_IMAGE,      0x00000111},
> +    {"CLK_DIV_TOP",             CLK_DIV_TOP,             0x00000000},
> +    {"CLK_DIV_CAM",             CLK_DIV_CAM,             0x00000000},
> +    {"CLK_DIV_TV",              CLK_DIV_TV,              0x00000000},
> +    {"CLK_DIV_MFC",             CLK_DIV_MFC,             0x00000000},
> +    {"CLK_DIV_G3D",             CLK_DIV_G3D,             0x00000000},
> +    {"CLK_DIV_IMAGE",           CLK_DIV_IMAGE,           0x00000000},
> +    {"CLK_DIV_LCD0",            CLK_DIV_LCD0,            0x00700000},
> +    {"CLK_DIV_LCD1",            CLK_DIV_LCD1,            0x00700000},
> +    {"CLK_DIV_MAUDIO",          CLK_DIV_MAUDIO,          0x00000000},
> +    {"CLK_DIV_FSYS0",           CLK_DIV_FSYS0,           0x00B00000},
> +    {"CLK_DIV_FSYS1",           CLK_DIV_FSYS1,           0x00000000},
> +    {"CLK_DIV_FSYS2",           CLK_DIV_FSYS2,           0x00000000},
> +    {"CLK_DIV_FSYS3",           CLK_DIV_FSYS3,           0x00000000},
> +    {"CLK_DIV_PERIL0",          CLK_DIV_PERIL0,          0x00000000},
> +    {"CLK_DIV_PERIL1",          CLK_DIV_PERIL1,          0x00000000},
> +    {"CLK_DIV_PERIL2",          CLK_DIV_PERIL2,          0x00000000},
> +    {"CLK_DIV_PERIL3",          CLK_DIV_PERIL3,          0x00000000},
> +    {"CLK_DIV_PERIL4",          CLK_DIV_PERIL4,          0x00000000},
> +    {"CLK_DIV_PERIL5",          CLK_DIV_PERIL5,          0x00000000},
> +    {"CLKDIV2_RATIO",           CLKDIV2_RATIO,           0x11111111},
> +    {"CLK_DIV_STAT_TOP",        CLK_DIV_STAT_TOP,        0x00000000},
> +    {"CLK_DIV_STAT_CAM",        CLK_DIV_STAT_CAM,        0x00000000},
> +    {"CLK_DIV_STAT_TV",         CLK_DIV_STAT_TV,         0x00000000},
> +    {"CLK_DIV_STAT_MFC",        CLK_DIV_STAT_MFC,        0x00000000},
> +    {"CLK_DIV_STAT_G3D",        CLK_DIV_STAT_G3D,        0x00000000},
> +    {"CLK_DIV_STAT_IMAGE",      CLK_DIV_STAT_IMAGE,      0x00000000},
> +    {"CLK_DIV_STAT_LCD0",       CLK_DIV_STAT_LCD0,       0x00000000},
> +    {"CLK_DIV_STAT_LCD1",       CLK_DIV_STAT_LCD1,       0x00000000},
> +    {"CLK_DIV_STAT_MAUDIO",     CLK_DIV_STAT_MAUDIO,     0x00000000},
> +    {"CLK_DIV_STAT_FSYS0",      CLK_DIV_STAT_FSYS0,      0x00000000},
> +    {"CLK_DIV_STAT_FSYS1",      CLK_DIV_STAT_FSYS1,      0x00000000},
> +    {"CLK_DIV_STAT_FSYS2",      CLK_DIV_STAT_FSYS2,      0x00000000},
> +    {"CLK_DIV_STAT_FSYS3",      CLK_DIV_STAT_FSYS3,      0x00000000},
> +    {"CLK_DIV_STAT_PERIL0",     CLK_DIV_STAT_PERIL0,     0x00000000},
> +    {"CLK_DIV_STAT_PERIL1",     CLK_DIV_STAT_PERIL1,     0x00000000},
> +    {"CLK_DIV_STAT_PERIL2",     CLK_DIV_STAT_PERIL2,     0x00000000},
> +    {"CLK_DIV_STAT_PERIL3",     CLK_DIV_STAT_PERIL3,     0x00000000},
> +    {"CLK_DIV_STAT_PERIL4",     CLK_DIV_STAT_PERIL4,     0x00000000},
> +    {"CLK_DIV_STAT_PERIL5",     CLK_DIV_STAT_PERIL5,     0x00000000},
> +    {"CLKDIV2_STAT",            CLKDIV2_STAT,            0x00000000},
> +    {"CLK_GATE_SCLK_CAM",       CLK_GATE_SCLK_CAM,       0xFFFFFFFF},
> +    {"CLK_GATE_IP_CAM",         CLK_GATE_IP_CAM,         0xFFFFFFFF},
> +    {"CLK_GATE_IP_TV",          CLK_GATE_IP_TV,          0xFFFFFFFF},
> +    {"CLK_GATE_IP_MFC",         CLK_GATE_IP_MFC,         0xFFFFFFFF},
> +    {"CLK_GATE_IP_G3D",         CLK_GATE_IP_G3D,         0xFFFFFFFF},
> +    {"CLK_GATE_IP_IMAGE",       CLK_GATE_IP_IMAGE,       0xFFFFFFFF},
> +    {"CLK_GATE_IP_LCD0",        CLK_GATE_IP_LCD0,        0xFFFFFFFF},
> +    {"CLK_GATE_IP_LCD1",        CLK_GATE_IP_LCD1,        0xFFFFFFFF},
> +    {"CLK_GATE_IP_FSYS",        CLK_GATE_IP_FSYS,        0xFFFFFFFF},
> +    {"CLK_GATE_IP_GPS",         CLK_GATE_IP_GPS,         0xFFFFFFFF},
> +    {"CLK_GATE_IP_PERIL",       CLK_GATE_IP_PERIL,       0xFFFFFFFF},
> +    {"CLK_GATE_IP_PERIR",       CLK_GATE_IP_PERIR,       0xFFFFFFFF},
> +    {"CLK_GATE_BLOCK",          CLK_GATE_BLOCK,          0xFFFFFFFF},
> +    {"CLKOUT_CMU_TOP",          CLKOUT_CMU_TOP,          0x00010000},
> +    {"CLKOUT_CMU_TOP_DIV_STAT", CLKOUT_CMU_TOP_DIV_STAT, 0x00000000},
> +    /* CMU_DMC registers */
> +    {"CLK_SRC_DMC",             CLK_SRC_DMC,             0x00010000},
> +    {"CLK_SRC_MASK_DMC",        CLK_SRC_MASK_DMC,        0x00010000},
> +    {"CLK_MUX_STAT_DMC",        CLK_MUX_STAT_DMC,        0x11100110},
> +    {"CLK_DIV_DMC0",            CLK_DIV_DMC0,            0x00000000},
> +    {"CLK_DIV_DMC1",            CLK_DIV_DMC1,            0x00000000},
> +    {"CLK_DIV_STAT_DMC0",       CLK_DIV_STAT_DMC0,       0x00000000},
> +    {"CLK_DIV_STAT_DMC1",       CLK_DIV_STAT_DMC1,       0x00000000},
> +    {"CLK_GATE_IP_DMC",         CLK_GATE_IP_DMC,         0xFFFFFFFF},
> +    {"CLKOUT_CMU_DMC",          CLKOUT_CMU_DMC,          0x00010000},
> +    {"CLKOUT_CMU_DMC_DIV_STAT", CLKOUT_CMU_DMC_DIV_STAT, 0x00000000},
> +    {"DCGIDX_MAP0",             DCGIDX_MAP0,             0xFFFFFFFF},
> +    {"DCGIDX_MAP1",             DCGIDX_MAP1,             0xFFFFFFFF},
> +    {"DCGIDX_MAP2",             DCGIDX_MAP2,             0xFFFFFFFF},
> +    {"DCGPERF_MAP0",            DCGPERF_MAP0,            0xFFFFFFFF},
> +    {"DCGPERF_MAP1",            DCGPERF_MAP1,            0xFFFFFFFF},
> +    {"DVCIDX_MAP",              DVCIDX_MAP,              0xFFFFFFFF},
> +    {"FREQ_CPU",                FREQ_CPU,                0x00000000},
> +    {"FREQ_DPM",                FREQ_DPM,                0x00000000},
> +    {"DVSEMCLK_EN",             DVSEMCLK_EN,             0x00000000},
> +    {"MAXPERF",                 MAXPERF,                 0x00000000},
> +    {"APLL_LOCK",               APLL_LOCK,               0x00000FFF},
> +    {"MPLL_LOCK",               MPLL_LOCK,               0x00000FFF},
> +    {"APLL_CON0",               APLL_CON0,               0x00C80601},
> +    {"APLL_CON1",               APLL_CON1,               0x0000001C},
> +    {"MPLL_CON0",               MPLL_CON0,               0x00C80601},
> +    {"MPLL_CON1",               MPLL_CON1,               0x0000001C},
> +    /* CMU_CPU registers */
> +    {"CLK_SRC_CPU",             CLK_SRC_CPU,             0x00000000},
> +    {"CLK_MUX_STAT_CPU",        CLK_MUX_STAT_CPU,        0x00110101},
> +    {"CLK_DIV_CPU0",            CLK_DIV_CPU0,            0x00000000},
> +    {"CLK_DIV_CPU1",            CLK_DIV_CPU1,            0x00000000},
> +    {"CLK_DIV_STAT_CPU0",       CLK_DIV_STAT_CPU0,       0x00000000},
> +    {"CLK_DIV_STAT_CPU1",       CLK_DIV_STAT_CPU1,       0x00000000},
> +    {"CLK_GATE_SCLK_CPU",       CLK_GATE_SCLK_CPU,       0xFFFFFFFF},
> +    {"CLK_GATE_IP_CPU",         CLK_GATE_IP_CPU,         0xFFFFFFFF},
> +    {"CLKOUT_CMU_CPU",          CLKOUT_CMU_CPU,          0x00010000},
> +    {"CLKOUT_CMU_CPU_DIV_STAT", CLKOUT_CMU_CPU_DIV_STAT, 0x00000000},
> +    {"ARMCLK_STOPCTRL",         ARMCLK_STOPCTRL,         0x00000044},
> +    {"ATCLK_STOPCTRL",          ATCLK_STOPCTRL,          0x00000044},
> +    {"PARITYFAIL_STATUS",       PARITYFAIL_STATUS,       0x00000000},
> +    {"PARITYFAIL_CLEAR",        PARITYFAIL_CLEAR,        0x00000000},
> +    {"PWR_CTRL",                PWR_CTRL,                0x00000033},
> +    {"APLL_CON0_L8",            APLL_CON0_L8,            0x00C80601},
> +    {"APLL_CON0_L7",            APLL_CON0_L7,            0x00C80601},
> +    {"APLL_CON0_L6",            APLL_CON0_L6,            0x00C80601},
> +    {"APLL_CON0_L5",            APLL_CON0_L5,            0x00C80601},
> +    {"APLL_CON0_L4",            APLL_CON0_L4,            0x00C80601},
> +    {"APLL_CON0_L3",            APLL_CON0_L3,            0x00C80601},
> +    {"APLL_CON0_L2",            APLL_CON0_L2,            0x00C80601},
> +    {"APLL_CON0_L1",            APLL_CON0_L1,            0x00C80601},
> +    {"IEM_CONTROL",             IEM_CONTROL,             0x00000000},
> +    {"APLL_CON1_L8",            APLL_CON1_L8,            0x00000000},
> +    {"APLL_CON1_L7",            APLL_CON1_L7,            0x00000000},
> +    {"APLL_CON1_L6",            APLL_CON1_L6,            0x00000000},
> +    {"APLL_CON1_L5",            APLL_CON1_L5,            0x00000000},
> +    {"APLL_CON1_L4",            APLL_CON1_L4,            0x00000000},
> +    {"APLL_CON1_L3",            APLL_CON1_L3,            0x00000000},
> +    {"APLL_CON1_L2",            APLL_CON1_L2,            0x00000000},
> +    {"APLL_CON1_L1",            APLL_CON1_L1,            0x00000000},
> +    {"CLKDIV_IEM_L8",           CLKDIV_IEM_L8,           0x00000000},
> +    {"CLKDIV_IEM_L7",           CLKDIV_IEM_L7,           0x00000000},
> +    {"CLKDIV_IEM_L6",           CLKDIV_IEM_L6,           0x00000000},
> +    {"CLKDIV_IEM_L5",           CLKDIV_IEM_L5,           0x00000000},
> +    {"CLKDIV_IEM_L4",           CLKDIV_IEM_L4,           0x00000000},
> +    {"CLKDIV_IEM_L3",           CLKDIV_IEM_L3,           0x00000000},
> +    {"CLKDIV_IEM_L2",           CLKDIV_IEM_L2,           0x00000000},
> +    {"CLKDIV_IEM_L1",           CLKDIV_IEM_L1,           0x00000000},
> +};
> +
> +
> +/*
> + * There are five CMUs:
> + *
> + *  CMU_LEFTBUS
> + *  CMU_RIGHTBUS
> + *  CMU_TOP
> + *  CMU_DMC
> + *  CMU_CPU
> + *
> + *  each of them uses 16KB address space for SFRs
> + *
> + *  + 0x4000 because SFR region for CMUs starts at 0x10030000,
> + *  but the first CMU (CMU_LEFTBUS) starts with this offset
> + *
> + */
> +#define EXYNOS4210_CMU_REGS_MEM_SIZE   (0x4000 * 5 + 0x4000)

It seems a bit unlikely to me that there's really a single indivisble
bit of hardware with 23 kilobytes worth of register area. Is there
a better way of structuring this so that there are a number of
sub-memory-regions that cover this space each with their own
read/write functions (and perhaps with their own state structs)?
(eg instantiate five CPUs at the right locations.)

> +
> +/*
> + * for indexing register in the uint32_t array
> + *
> + * 'reg' - register offset (see offsets definitions above)
> + *
> + */
> +#define I_(reg) (reg / sizeof(uint32_t))
> +
> +#define XOM_0 1 /* Select XXTI (0) or XUSBXTI (1) base clock source */
> +
> +/*
> + *  Offsets in CLK_SRC_CPU register
> + *  for control MUXMPLL and MUXAPLL
> + *
> + *  0 = FINPLL, 1 = MOUTM(A)PLLFOUT
> + */
> +#define MUX_APLL_SEL_SHIFT 0
> +#define MUX_MPLL_SEL_SHIFT 8
> +#define MUX_CORE_SEL_SHIFT 16
> +#define MUX_HPM_SEL_SHIFT  20
> +
> +#define MUX_APLL_SEL  (1 << MUX_APLL_SEL_SHIFT)
> +#define MUX_MPLL_SEL  (1 << MUX_MPLL_SEL_SHIFT)
> +#define MUX_CORE_SEL  (1 << MUX_CORE_SEL_SHIFT)
> +#define MUX_HPM_SEL   (1 << MUX_HPM_SEL_SHIFT)
> +
> +/* Offsets for fields in CLK_MUX_STAT_CPU register */
> +#define APLL_SEL_SHIFT         0
> +#define APLL_SEL_MASK          0x00000007
> +#define MPLL_SEL_SHIFT         8
> +#define MPLL_SEL_MASK          0x00000700
> +#define CORE_SEL_SHIFT         16
> +#define CORE_SEL_MASK          0x00070000
> +#define HPM_SEL_SHIFT          20
> +#define HPM_SEL_MASK           0x00700000
> +
> +
> +/* Offsets for fields in <pll>_CON0 register */
> +#define PLL_ENABLE_SHIFT 31
> +#define PLL_ENABLE_MASK  0x80000000 /* [31] bit */
> +#define PLL_LOCKED_MASK  0x20000000 /* [29] bit */
> +#define PLL_MDIV_SHIFT   16
> +#define PLL_MDIV_MASK    0x03FF0000 /* [25:16] bits */
> +#define PLL_PDIV_SHIFT   8
> +#define PLL_PDIV_MASK    0x00003F00 /* [13:8] bits */
> +#define PLL_SDIV_SHIFT   0
> +#define PLL_SDIV_MASK    0x00000007 /* [2:0] bits */
> +
> +
> +
> +/*
> + *  Offset in CLK_DIV_CPU0 register
> + *  for DIVAPLL clock divider ratio
> + */
> +#define APLL_RATIO_SHIFT 24
> +#define APLL_RATIO_MASK  0x07000000 /* [26:24] bits */
> +
> +/*
> + *  Offset in CLK_DIV_TOP register
> + *  for DIVACLK_100 clock divider ratio
> + */
> +#define ACLK_100_RATIO_SHIFT 4
> +#define ACLK_100_RATIO_MASK  0x000000f0 /* [7:4] bits */
> +
> +/* Offset in CLK_SRC_TOP0 register */
> +#define MUX_ACLK_100_SEL_SHIFT 16
> +
> +/*
> + * Offsets in CLK_SRC_PERIL0 register
> + * for clock sources of UARTs
> + */
> +#define UART0_SEL_SHIFT    0
> +#define UART1_SEL_SHIFT    4
> +#define UART2_SEL_SHIFT    8
> +#define UART3_SEL_SHIFT    12
> +#define UART4_SEL_SHIFT    16
> +/*
> + * Offsets in CLK_DIV_PERIL0 register
> + * for clock divider of UARTs
> + */
> +#define UART0_DIV_SHIFT    0
> +#define UART1_DIV_SHIFT    4
> +#define UART2_DIV_SHIFT    8
> +#define UART3_DIV_SHIFT    12
> +#define UART4_DIV_SHIFT    16
> +
> +typedef struct {
> +        SysBusDevice busdev;
> +        MemoryRegion iomem;
> +
> +        uint32_t reg[EXYNOS4210_CMU_REGS_MEM_SIZE];
> +
> +} Exynos4210CmuState;
> +
> +#define SOURCES_NUMBER    9
> +#define RECIPIENTS_NUMBER 9
> +
> +typedef struct Exynos4210CmuClockState {
> +
> +        const char  *name;
> +        uint64_t     rate;
> +
> +        /* Current source clock */
> +        struct Exynos4210CmuClockState *source;
> +        /*
> +         * Available sources. Their order must correspond to CLK_SRC_ 
> register
> +         */
> +        struct Exynos4210CmuClockState *sources[SOURCES_NUMBER];
> +        /* Who uses this clock? */
> +        struct Exynos4210CmuClockState *recipients[RECIPIENTS_NUMBER];
> +
> +        uint32_t src_reg; /* Offset of CLK_SRC_<*> register */
> +        uint32_t div_reg; /* Offset of CLK_DIV_<*> register */
> +
> +        /*
> +         *  Shift for MUX_<clk>_SEL value which is stored
> +         *  in appropriate CLK_MUX_STAT_<cmu> register
> +         */
> +        uint8_t mux_shift;
> +
> +        /*
> +         *  Shift for <clk>_RATIO value which is stored
> +         *  in appropriate CLK_DIV_<cmu> register
> +         */
> +        uint8_t div_shift;
> +
> +} Exynos4210CmuClockState;

So, how does this compare with the struct clk in hw/omap_clk.c ?
Is there a useful generalisation we can make rather than having
every SOC model doing its own thing for clock trees?

> +
> +/* Clocks from Clock Pads */
> +
> +/* It should be used only for testing purposes. XOM_0 is 0 */
> +static Exynos4210CmuClockState xxti = {
> +        .name       = "XXTI",
> +        .rate       = 24000000,
> +};
> +
> +/* Main source. XOM_0 is 1 */
> +static Exynos4210CmuClockState xusbxti = {
> +        .name       = "XUSBXTI",
> +        .rate       = 24000000,
> +};
> +
> +/* PLLs */
> +
> +static Exynos4210CmuClockState mpll = {
> +        .name       = "MPLL",
> +        .source     = (XOM_0 ? &xusbxti : &xxti),
> +};
> +
> +static Exynos4210CmuClockState apll = {
> +        .name       = "APLL",
> +        .source     = (XOM_0 ? &xusbxti : &xxti),
> +};
> +
> +
> +/**/
> +static Exynos4210CmuClockState sclk_mpll = {
> +        .name       = "SCLK_MPLL",
> +        .sources    = {XOM_0 ? &xusbxti : &xxti, &mpll},
> +        .src_reg    = CLK_SRC_CPU,
> +        .mux_shift  = MUX_MPLL_SEL_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_apll = {
> +        .name       = "SCLK_APLL",
> +        .sources    = {XOM_0 ? &xusbxti : &xxti, &apll},
> +        .src_reg    = CLK_SRC_CPU,
> +        .div_reg    = CLK_DIV_CPU0,
> +        .mux_shift  = MUX_APLL_SEL_SHIFT,
> +        .div_shift  = APLL_RATIO_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState aclk_100 = {
> +        .name      = "ACLK_100",
> +        .sources   = {&sclk_mpll, &sclk_apll},
> +        .src_reg   = CLK_SRC_TOP0,
> +        .div_reg   = CLK_DIV_TOP,
> +        .mux_shift = MUX_ACLK_100_SEL_SHIFT,
> +        .div_shift = ACLK_100_RATIO_SHIFT,
> +};
> +
> +
> +/* TODO: add other needed structures for UARTs sources */
> +static Exynos4210CmuClockState sclk_uart0 = {
> +        .name      = "SCLK_UART0",
> +        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> +        .src_reg   = CLK_SRC_PERIL0,
> +        .div_reg   = CLK_DIV_PERIL0,
> +        .mux_shift = UART0_SEL_SHIFT,
> +        .div_shift = UART0_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart1 = {
> +        .name      = "SCLK_UART1",
> +        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> +        .src_reg   = CLK_SRC_PERIL0,
> +        .div_reg   = CLK_DIV_PERIL0,
> +        .mux_shift = UART1_SEL_SHIFT,
> +        .div_shift = UART1_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart2 = {
> +        .name      = "SCLK_UART2",
> +        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> +        .src_reg   = CLK_SRC_PERIL0,
> +        .div_reg   = CLK_DIV_PERIL0,
> +        .mux_shift = UART2_SEL_SHIFT,
> +        .div_shift = UART2_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart3 = {
> +        .name      = "SCLK_UART3",
> +        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> +        .src_reg   = CLK_SRC_PERIL0,
> +        .div_reg   = CLK_DIV_PERIL0,
> +        .mux_shift = UART3_SEL_SHIFT,
> +        .div_shift = UART3_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart4 = {
> +        .name      = "SCLK_UART4",
> +        .sources   = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> +        .src_reg   = CLK_SRC_PERIL0,
> +        .div_reg   = CLK_DIV_PERIL0,
> +        .mux_shift = UART4_SEL_SHIFT,
> +        .div_shift = UART4_DIV_SHIFT,
> +};
> +
> +/*
> + *  This array must correspond to Exynos4210CmuClock enumerator
> + *  which is defined in exynos4210.h file
> + *
> + */
> +static Exynos4210CmuClockState *exynos4210_clock[] = {
> +        &xxti,
> +        &xusbxti,
> +        &apll,
> +        &mpll,
> +        &sclk_apll,
> +        &sclk_mpll,
> +        &aclk_100,
> +        &sclk_uart0,
> +        &sclk_uart1,
> +        &sclk_uart2,
> +        &sclk_uart3,
> +        &sclk_uart4,
> +        NULL
> +};
> +
> +
> +uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock)
> +{
> +    return exynos4210_clock[clock]->rate;
> +}
> +
> +#ifdef DEBUG_CMU
> +/* The only meaning of life - debugging. This functions should be only used
> + * inside PRINT_DEBUG_... macroses

"This function".
"macros".

> + */
> +static const char *exynos4210_cmu_regname(target_phys_addr_t  offset)
> +{
> +
> +    int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);

int regs_number = ARRAY_SIZE(exynos4210_cmu_regs);

> +    int i;
> +
> +    for (i = 0; i < regs_number; i++) {
> +        if (offset == exynos4210_cmu_regs[i].offset) {
> +            return exynos4210_cmu_regs[i].name;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +#endif
> +
> +static void exynos4210_cmu_set_pll(void *opaque, target_phys_addr_t offset)
> +{
> +    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +    uint32_t pdiv, mdiv, sdiv, enable;
> +
> +    /*
> +     * FOUT = MDIV * FIN / (PDIV * 2^(SDIV-1))
> +     */
> +
> +    enable = (s->reg[I_(offset)] & PLL_ENABLE_MASK) >> PLL_ENABLE_SHIFT;
> +    mdiv   = (s->reg[I_(offset)] & PLL_MDIV_MASK)   >> PLL_MDIV_SHIFT;
> +    pdiv   = (s->reg[I_(offset)] & PLL_PDIV_MASK)   >> PLL_PDIV_SHIFT;
> +    sdiv   = (s->reg[I_(offset)] & PLL_SDIV_MASK)   >> PLL_SDIV_SHIFT;
> +
> +    switch (offset) {
> +
> +    case MPLL_CON0:
> +        if (mpll.source) {
> +            if (enable) {
> +                mpll.rate = mdiv * mpll.source->rate / (pdiv * (1 << 
> (sdiv-1)));
> +            } else {
> +                mpll.rate = 0;
> +            }
> +        } else {
> +            hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
> +                     mpll.name);
> +        }
> +        PRINT_DEBUG("mpll.rate: %llu\n", (unsigned long long int)mpll.rate);
> +        break;
> +
> +    case APLL_CON0:
> +        if (apll.source) {
> +            if (enable) {
> +                apll.rate = mdiv * apll.source->rate / (pdiv * (1 << 
> (sdiv-1)));
> +            } else {
> +                apll.rate = 0;
> +            }
> +        } else {
> +            hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
> +                     apll.name);
> +        }
> +        PRINT_DEBUG("apll.rate: %llu\n", (unsigned long long int)apll.rate);
> +        break;
> +
> +    default:
> +        hw_error("exynos4210_cmu_set_pll: Bad offset 0x%x\n", (int)offset);
> +    }
> +
> +    s->reg[I_(offset)] |= PLL_LOCKED_MASK;
> +}
> +
> +
> +static void
> +exynos4210_cmu_set_rate(void *opaque, Exynos4210CmuClockState *clock)
> +{
> +    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +    int i;
> +
> +    /* Rates of PLLs are calculated differently than ordinary clocks rates */
> +    if (clock == &mpll) {
> +        exynos4210_cmu_set_pll(s, MPLL_CON0);
> +    } else if (clock == &apll) {
> +        exynos4210_cmu_set_pll(s, APLL_CON0);
> +    } else if ((clock != &xxti) && (clock != &xusbxti)) {
> +        /*
> +         *  Not root clock. We don't need calculating rate
> +         *  of root clock because it is hard coded.
> +         */
> +        uint32_t src_index = I_(clock->src_reg);
> +        uint32_t div_index = I_(clock->div_reg);
> +        clock->source = clock->sources[(s->reg[src_index] >>
> +                clock->mux_shift) & 0xf];
> +        clock->rate = muldiv64(clock->source->rate, 1,
> +                               (((s->reg[div_index] >> clock->div_shift) & 
> 0xf)
> +                                       + 1));
> +
> +        PRINT_DEBUG_EXTEND("SRC: <0x%05x> %s, SHIFT: %d\n",
> +                           clock->src_reg,
> +                           exynos4210_cmu_regname(clock->src_reg),
> +                           clock->mux_shift);
> +
> +        PRINT_DEBUG("%s [%s:%llu]: %llu\n",
> +                    clock->name,
> +                    clock->source->name,
> +                    (uint64_t)clock->source->rate,
> +                    (uint64_t)clock->rate);
> +    }
> +
> +    /* Visit all recipients for given clock */
> +    i = 0;
> +    do {
> +
> +        Exynos4210CmuClockState *recipient_clock = clock->recipients[i];
> +
> +        if (recipient_clock == NULL) {
> +            PRINT_DEBUG_EXTEND("%s have %d recipients\n", clock->name, i);
> +            break;
> +        }
> +
> +        uint32_t src_index = recipient_clock->src_reg / sizeof(uint32_t);
> +        int source_index = s->reg[src_index] >>
> +                recipient_clock->mux_shift & 0xf;
> +        recipient_clock->source = recipient_clock->sources[source_index];
> +
> +        if (recipient_clock->source != clock) {
> +            break;
> +        }
> +
> +        exynos4210_cmu_set_rate(s, recipient_clock);
> +
> +        i++;
> +    } while (i < RECIPIENTS_NUMBER);
> +}
> +
> +
> +static uint64_t exynos4210_cmu_read(void *opaque, target_phys_addr_t offset,
> +                                  unsigned size)
> +{
> +    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +
> +    if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
> +        hw_error("exynos4210_cmu_read: Bad offset 0x%x\n", (int)offset);
> +    }
> +
> +    PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n", offset,
> +                       exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
> +
> +    return s->reg[I_(offset)];
> +}
> +
> +
> +static void exynos4210_cmu_write(void *opaque, target_phys_addr_t offset,
> +                               uint64_t val, unsigned size)
> +{
> +    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +    uint32_t pre_val;
> +
> +    if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
> +        hw_error("exynos4210_cmu_write: Bad offset 0x%x\n", (int)offset);
> +    }

General rule: don't hw_error() for things a guest can trigger, like writing
to bogus offsets in a device.

> +
> +    pre_val = s->reg[I_(offset)];
> +    s->reg[I_(offset)] = val;
> +
> +    PRINT_DEBUG_EXTEND("<0x%05x> %s <- %08x\n", offset,
> +                       exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
> +
> +    switch (offset) {
> +
> +    case APLL_CON0:
> +        val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
> +        s->reg[I_(offset)] = val;
> +        exynos4210_cmu_set_rate(s, &apll);
> +        break;
> +    case MPLL_CON0:
> +        val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
> +        s->reg[I_(offset)] = val;
> +        exynos4210_cmu_set_rate(s, &mpll);
> +        break;
> +    case CLK_SRC_CPU:
> +    {
> +        if (val & MUX_APLL_SEL) {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(APLL_SEL_MASK)) |
> +                    (2 << APLL_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_APLL_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_APLL_SEL)) {
> +                exynos4210_cmu_set_rate(s, &apll);
> +            }
> +
> +        } else {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(APLL_SEL_MASK)) |
> +                    (1 << APLL_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_APLL_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_APLL_SEL)) {
> +                exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +            }
> +        }
> +
> +
> +        if (val & MUX_MPLL_SEL) {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(MPLL_SEL_MASK)) |
> +                    (2 << MPLL_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_MPLL_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
> +                exynos4210_cmu_set_rate(s, &mpll);
> +            }
> +
> +        } else {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(MPLL_SEL_MASK)) |
> +                    (1 << MPLL_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_MPLL_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
> +                exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +            }
> +        }
> +
> +        if (val & MUX_CORE_SEL) {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(CORE_SEL_MASK)) |
> +                    (2 << CORE_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_CORE_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_CORE_SEL)) {
> +                exynos4210_cmu_set_rate(s, &sclk_mpll);
> +            }
> +
> +        } else {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(CORE_SEL_MASK)) |
> +                    (1 << CORE_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_CORE_SEL) !=
> +                    (s->reg[I_(offset)] & MUX_CORE_SEL)) {
> +                exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +            }
> +        }
> +
> +        if (val & MUX_HPM_SEL) {
> +            exynos4210_cmu_set_rate(s, &sclk_mpll);
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(HPM_SEL_MASK)) |
> +                    (2 << HPM_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_HPM_SEL) != (s->reg[I_(offset)] & 
> MUX_HPM_SEL)) {
> +                exynos4210_cmu_set_rate(s, &sclk_mpll);
> +            }
> +
> +        } else {
> +            s->reg[I_(CLK_MUX_STAT_CPU)] =
> +                    (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(HPM_SEL_MASK)) |
> +                    (1 << HPM_SEL_SHIFT);
> +
> +            if ((pre_val & MUX_HPM_SEL) != (s->reg[I_(offset)] & 
> MUX_HPM_SEL)) {
> +                exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +            }
> +        }
> +    }
> +    break;
> +    case CLK_DIV_CPU0:
> +        exynos4210_cmu_set_rate(s, &sclk_apll);
> +        exynos4210_cmu_set_rate(s, &sclk_mpll);
> +        break;
> +    case CLK_SRC_TOP0:
> +    case CLK_DIV_TOP:
> +        exynos4210_cmu_set_rate(s, &aclk_100);
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +
> +static const MemoryRegionOps exynos4210_cmu_ops = {
> +        .read = exynos4210_cmu_read,
> +        .write = exynos4210_cmu_write,
> +        .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +
> +static void exynos4210_cmu_reset(void *opaque)
> +{
> +    Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +    int i = 0, j = 0, n = 0;
> +    int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);
> +    uint32_t index = 0;
> +
> +    /* Set default values for registers */
> +    for (i = 0; i < regs_number; i++) {
> +        index = (exynos4210_cmu_regs[i].offset) / sizeof(uint32_t);
> +        s->reg[index] = exynos4210_cmu_regs[i].reset_value;
> +    }
> +
> +    /* clear recipients array from previous reset */
> +    for (i = 0; i < CLOCKS_NUMBER; i++) {
> +        bzero(exynos4210_clock[i]->recipients,
> +              RECIPIENTS_NUMBER * sizeof(Exynos4210CmuClockState *));

bzero() is deprecated. Use memset().

> +    }
> +
> +    /*
> +     * Here we fill '.recipients' fields in all clocks. Also we fill empty
> +     * 'sources[]' arrays by values of 'source' fields (it is necessary
> +     * for set rate, for example). If 'sources[]' array and 'source' field
> +     * is empty simultaneously we get hw_error.
> +     *
> +     */
> +    for (i = 0; i < CLOCKS_NUMBER; i++) {
> +
> +        /* visit all clocks in the exynos4210_clock */
> +
> +        PRINT_DEBUG("[SOURCES] %s: ", exynos4210_clock[i]->name);
> +
> +        j = 0;
> +        do { /* visit all sources for current clock (exynos4210_clock[i]) */
> +
> +            if ((exynos4210_clock[i]->sources[j] == NULL)) {
> +
> +                if (j == 0) { /* check if we have empty '.sources[]' array */
> +                    if (exynos4210_clock[i]->source != NULL) {
> +                        exynos4210_clock[i]->sources[j] =
> +                                exynos4210_clock[i]->source;
> +                    } else {
> +                        /*
> +                         * We haven't any defined sources for this clock. 
> Error
> +                         * during definition of appropriate clock structure
> +                         *
> +                         */
> +                        if ((exynos4210_clock[i] != &xusbxti) &&
> +                            (exynos4210_clock[i] != &xxti)) {
> +
> +                            hw_error("exynos4210_cmu_reset:"
> +                                    "There aren't any sources for %s 
> clock!\n",
> +                                     exynos4210_clock[i]->name);
> +                        } else {
> +                            /*
> +                             * we don't need any sources for this clock
> +                             * because it's a root clock
> +                             */
> +                            break;
> +                        }
> +                    }
> +                } else {
> +                    break; /* leave because there are no more sources */
> +                }
> +
> +            }
> +
> +            PRINT_DEBUG_SIMPLE(" %s", exynos4210_clock[i]->sources[j]->name);
> +
> +            /*
> +             *  find first empty place in 'recipients[]' array of
> +             *  current 'sources' element and put current clock there
> +             */
> +            n = 0;
> +            do {
> +                if ((exynos4210_clock[i]->sources[j]->recipients[n]) == 
> NULL) {
> +                    exynos4210_clock[i]->sources[j]->recipients[n] =
> +                            exynos4210_clock[i];
> +                    break;
> +                }
> +                n++;
> +            } while (n < RECIPIENTS_NUMBER);

What's wrong with "for (n = 0; n < RECIPIENTS_NUMBER; n++)" ?
(Ditto the loop with j, for that matter.)

> +
> +            j++;
> +
> +        } while (j < SOURCES_NUMBER);
> +
> +        PRINT_DEBUG_SIMPLE("\n");
> +
> +    } /* CLOCKS_NUMBER */
> +
> +#ifdef DEBUG_CMU
> +    for (i = 0; i < CLOCKS_NUMBER; i++) {
> +        PRINT_DEBUG("[RECIPIENTS] %s: ", exynos4210_clock[i]->name);
> +        for (j = 0;
> +             (j < RECIPIENTS_NUMBER) &&
> +             ((exynos4210_clock[i]->recipients[j]) != NULL);
> +             j++) {
> +            PRINT_DEBUG_SIMPLE("%s ", 
> exynos4210_clock[i]->recipients[j]->name);
> +        }
> +        PRINT_DEBUG_SIMPLE("\n");
> +    }
> +#endif
> +
> +    exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +}
> +
> +static const VMStateDescription vmstate_exynos4210_cmu = {
> +        .name = "exynos4210.cmu",
> +        .version_id = 1,
> +        .minimum_version_id = 1,
> +        .minimum_version_id_old = 1,
> +        .fields = (VMStateField[]) {
> +        /*
> +         * TODO: Maybe we should save Exynos4210CmuClockState structs as well
> +         */

If there's anything in them that can change at runtime (as opposed
to being statically determined at board init time) then yes, it needs
to be saved/restored.

> +            VMSTATE_UINT32_ARRAY(reg, Exynos4210CmuState,
> +                                 EXYNOS4210_CMU_REGS_MEM_SIZE),
> +            VMSTATE_END_OF_LIST()
> +        }
> +};
> +
> +static int exynos4210_cmu_init(SysBusDevice *dev)
> +{
> +    Exynos4210CmuState *s = FROM_SYSBUS(Exynos4210CmuState, dev);
> +
> +    /* memory mapping */
> +    memory_region_init_io(&s->iomem, &exynos4210_cmu_ops, s, 
> "exynos4210.cmu",
> +                          EXYNOS4210_CMU_REGS_MEM_SIZE);
> +    sysbus_init_mmio(dev, &s->iomem);
> +
> +    qemu_register_reset(exynos4210_cmu_reset, s);
> +
> +    vmstate_register(&dev->qdev, -1, &vmstate_exynos4210_cmu, s);

If you use sysbus_register_withprop() rather than sysbus_register_dev()
you can pass the VMState and reset functions as .qdev.vmsd and .qdev.reset
fields in the SysBusDeviceInfo struct.
> +
> +    exynos4210_cmu_reset(s);

You don't need to explicitly call reset here, qdev will do it for you.

> +
> +    return 0;
> +}
> +
> +
> +static void exynos4210_cmu_register(void)
> +{
> +    sysbus_register_dev("exynos4210.cmu",
> +                        sizeof(Exynos4210CmuState),
> +                        exynos4210_cmu_init);
> +}
> +
> +
> +device_init(exynos4210_cmu_register)
> --
> 1.7.4.1
>
>


Reply via email to