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 > >