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