[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3 02/14] ARM: exynos4210: CMU support
From: |
Peter Maydell |
Subject: |
Re: [Qemu-devel] [PATCH v3 02/14] ARM: exynos4210: CMU support |
Date: |
Mon, 12 Dec 2011 22:44:55 +0000 |
On 12 December 2011 06:43, Evgeny Voevodin <address@hidden> wrote:
> From: Maksim Kozlov <address@hidden>
>
> Add exynos4210 Clock Management Units emulation
>
> Signed-off-by: Evgeny Voevodin <address@hidden>
> ---
> Makefile.target | 2 +-
> hw/exynos4210.c | 7 +
> hw/exynos4210.h | 22 +
> hw/exynos4210_cmu.c | 1146
> +++++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1176 insertions(+), 1 deletions(-)
> create mode 100644 hw/exynos4210_cmu.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 624a142..ce4f1f8 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -344,7 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o
> arm_timer.o
> obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o
> pl190.o
> obj-arm-y += versatile_pci.o
> obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
> -obj-arm-y += exynos4210.o
> +obj-arm-y += exynos4210.o exynos4210_cmu.o
> obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
> obj-arm-y += pl061.o
> obj-arm-y += arm-semi.o
> diff --git a/hw/exynos4210.c b/hw/exynos4210.c
> index 1550016..1a6e353 100644
> --- a/hw/exynos4210.c
> +++ b/hw/exynos4210.c
> @@ -60,6 +60,10 @@
>
> #define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR
>
> +/* SFR Base Address for CMUs */
> +#define EXYNOS4210_CMU_BASE_ADDR 0x10030000
> +
> +
> static struct arm_boot_info exynos4210_binfo = {
> .loader_start = EXYNOS4210_BASE_BOOT_ADDR,
> };
> @@ -172,6 +176,9 @@ static void exynos4210_init(ram_addr_t ram_size,
> memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
> dram0_mem);
>
> + /* CMU */
> + sysbus_create_simple("exynos4210.cmu", EXYNOS4210_CMU_BASE_ADDR, NULL);
> +
> /*** Load kernel ***/
>
> exynos4210_binfo.ram_size = ram_size;
> diff --git a/hw/exynos4210.h b/hw/exynos4210.h
> index 7137630..683a4a6 100644
> --- a/hw/exynos4210.h
> +++ b/hw/exynos4210.h
> @@ -31,4 +31,26 @@
>
> #define EXYNOS4210_MAX_CPUS 2
>
> +/*
> + * Interface for exynos4210 Clock Management Units (CMUs)
> + */
> +
> +typedef enum {
> + XXTI,
> + XUSBXTI,
> + APLL,
> + MPLL,
> + SCLK_APLL,
> + SCLK_MPLL,
> + ACLK_100,
> + SCLK_UART0,
> + SCLK_UART1,
> + SCLK_UART2,
> + SCLK_UART3,
> + SCLK_UART4,
> + CLOCKS_NUMBER
> +} Exynos4210CmuClock;
> +
> +uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock);
> +
> #endif /* EXYNOS4210_H_ */
> diff --git a/hw/exynos4210_cmu.c b/hw/exynos4210_cmu.c
> new file mode 100644
> index 0000000..fe4100c
> --- /dev/null
> +++ b/hw/exynos4210_cmu.c
> @@ -0,0 +1,1146 @@
> +/*
> + * exynos4210 Clock Management Units (CMUs) Emulation
> + *
> + * Copyright (C) 2011 Samsung Electronics Co Ltd.
> + * Maksim Kozlov, <address@hidden>
> + *
> + * Created on: 07.2011
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> 51
> + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#include "sysbus.h"
> +
> +#include "exynos4210.h"
> +
> +
> +
> +#undef DEBUG_CMU
> +
> +//#define DEBUG_CMU
> +//#define DEBUG_CMU_EXTEND
> +
> +
> +#define PRINT_DEBUG(fmt, args...) \
> + do {} while (0)
> +#define PRINT_DEBUG_SIMPLE(fmt, args...) \
> + do {} while (0)
> +#define PRINT_DEBUG_EXTEND(fmt, args...) \
> + do {} while (0)
> +#define PRINT_ERROR(fmt, args...) \
> + do { \
> + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
> + } while (0)
> +
> +
> +#ifdef DEBUG_CMU
> +
> + #undef PRINT_DEBUG
> + #define PRINT_DEBUG(fmt, args...) \
> + do { \
> + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
> + } while (0)
> +
> + #undef PRINT_DEBUG_SIMPLE
> + #define PRINT_DEBUG_SIMPLE(fmt, args...) \
> + do { \
> + fprintf(stderr, fmt, ## args); \
> + } while (0)
> +
> +#ifdef DEBUG_CMU_EXTEND
> +
> + #undef PRINT_DEBUG_EXTEND
> + #define PRINT_DEBUG_EXTEND(fmt, args...) \
> + do { \
> + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
> + } while (0)
> +
> +#endif /* EXTEND */
> +#endif
> +
> +
> +/*
> + * Offsets for CMUs registers
> + */
> +
> +/* CMU_LEFTBUS registers */
> +#define CLK_SRC_LEFTBUS 0x04200
> +#define CLK_MUX_STAT_LEFTBUS 0x04400
> +#define CLK_DIV_LEFTBUS 0x04500
> +#define CLK_DIV_STAT_LEFTBUS 0x04600
> +#define CLK_GATE_IP_LEFTBUS 0x04800
> +#define CLKOUT_CMU_LEFTBUS 0x04A00
> +#define CLKOUT_CMU_LEFTBUS_DIV_STAT 0x04A04
> +/* CMU_RIGHTBUS registers */
> +#define CLK_SRC_RIGHTBUS 0x08200
> +#define CLK_MUX_STAT_RIGHTBUS 0x08400
> +#define CLK_DIV_RIGHTBUS 0x08500
> +#define CLK_DIV_STAT_RIGHTBUS 0x08600
> +#define CLK_GATE_IP_RIGHTBUS 0x08800
> +#define CLKOUT_CMU_RIGHTBUS 0x08A00
> +#define CLKOUT_CMU_RIGHTBUS_DIV_STAT 0x08A04
> +/* CMU_TOP registers */
> +#define EPLL_LOCK 0x0C010
> +#define VPLL_LOCK 0x0C020
> +#define EPLL_CON0 0x0C110
> +#define EPLL_CON1 0x0C114
> +#define VPLL_CON0 0x0C120
> +#define VPLL_CON1 0x0C124
> +#define CLK_SRC_TOP0 0x0C210
> +#define CLK_SRC_TOP1 0x0C214
> +#define CLK_SRC_CAM 0x0C220
> +#define CLK_SRC_TV 0x0C224
> +#define CLK_SRC_MFC 0x0C228
> +#define CLK_SRC_G3D 0x0C22C
> +#define CLK_SRC_IMAGE 0x0C230
> +#define CLK_SRC_LCD0 0x0C234
> +#define CLK_SRC_LCD1 0x0C238
> +#define CLK_SRC_MAUDIO 0x0C23C
> +#define CLK_SRC_FSYS 0x0C240
> +#define CLK_SRC_PERIL0 0x0C250
> +#define CLK_SRC_PERIL1 0x0C254
> +#define CLK_SRC_MASK_TOP 0x0C310
> +#define CLK_SRC_MASK_CAM 0x0C320
> +#define CLK_SRC_MASK_TV 0x0C324
> +#define CLK_SRC_MASK_LCD0 0x0C334
> +#define CLK_SRC_MASK_LCD1 0x0C338
> +#define CLK_SRC_MASK_MAUDIO 0x0C33C
> +#define CLK_SRC_MASK_FSYS 0x0C340
> +#define CLK_SRC_MASK_PERIL0 0x0C350
> +#define CLK_SRC_MASK_PERIL1 0x0C354
> +#define CLK_MUX_STAT_TOP 0x0C410
> +#define CLK_MUX_STAT_MFC 0x0C428
> +#define CLK_MUX_STAT_G3D 0x0C42C
> +#define CLK_MUX_STAT_IMAGE 0x0C430
> +#define CLK_DIV_TOP 0x0C510
> +#define CLK_DIV_CAM 0x0C520
> +#define CLK_DIV_TV 0x0C524
> +#define CLK_DIV_MFC 0x0C528
> +#define CLK_DIV_G3D 0x0C52C
> +#define CLK_DIV_IMAGE 0x0C530
> +#define CLK_DIV_LCD0 0x0C534
> +#define CLK_DIV_LCD1 0x0C538
> +#define CLK_DIV_MAUDIO 0x0C53C
> +#define CLK_DIV_FSYS0 0x0C540
> +#define CLK_DIV_FSYS1 0x0C544
> +#define CLK_DIV_FSYS2 0x0C548
> +#define CLK_DIV_FSYS3 0x0C54C
> +#define CLK_DIV_PERIL0 0x0C550
> +#define CLK_DIV_PERIL1 0x0C554
> +#define CLK_DIV_PERIL2 0x0C558
> +#define CLK_DIV_PERIL3 0x0C55C
> +#define CLK_DIV_PERIL4 0x0C560
> +#define CLK_DIV_PERIL5 0x0C564
> +#define CLKDIV2_RATIO 0x0C580
> +#define CLK_DIV_STAT_TOP 0x0C610
> +#define CLK_DIV_STAT_CAM 0x0C620
> +#define CLK_DIV_STAT_TV 0x0C624
> +#define CLK_DIV_STAT_MFC 0x0C628
> +#define CLK_DIV_STAT_G3D 0x0C62C
> +#define CLK_DIV_STAT_IMAGE 0x0C630
> +#define CLK_DIV_STAT_LCD0 0x0C634
> +#define CLK_DIV_STAT_LCD1 0x0C638
> +#define CLK_DIV_STAT_MAUDIO 0x0C63C
> +#define CLK_DIV_STAT_FSYS0 0x0C640
> +#define CLK_DIV_STAT_FSYS1 0x0C644
> +#define CLK_DIV_STAT_FSYS2 0x0C648
> +#define CLK_DIV_STAT_FSYS3 0x0C64C
> +#define CLK_DIV_STAT_PERIL0 0x0C650
> +#define CLK_DIV_STAT_PERIL1 0x0C654
> +#define CLK_DIV_STAT_PERIL2 0x0C658
> +#define CLK_DIV_STAT_PERIL3 0x0C65C
> +#define CLK_DIV_STAT_PERIL4 0x0C660
> +#define CLK_DIV_STAT_PERIL5 0x0C664
> +#define CLKDIV2_STAT 0x0C680
> +#define CLK_GATE_SCLK_CAM 0x0C820
> +#define CLK_GATE_IP_CAM 0x0C920
> +#define CLK_GATE_IP_TV 0x0C924
> +#define CLK_GATE_IP_MFC 0x0C928
> +#define CLK_GATE_IP_G3D 0x0C92C
> +#define CLK_GATE_IP_IMAGE 0x0C930
> +#define CLK_GATE_IP_LCD0 0x0C934
> +#define CLK_GATE_IP_LCD1 0x0C938
> +#define CLK_GATE_IP_FSYS 0x0C940
> +#define CLK_GATE_IP_GPS 0x0C94C
> +#define CLK_GATE_IP_PERIL 0x0C950
> +#define CLK_GATE_IP_PERIR 0x0C960
> +#define CLK_GATE_BLOCK 0x0C970
> +#define CLKOUT_CMU_TOP 0x0CA00
> +#define CLKOUT_CMU_TOP_DIV_STAT 0x0CA04
> +/* CMU_DMC registers */
> +#define CLK_SRC_DMC 0x10200
> +#define CLK_SRC_MASK_DMC 0x10300
> +#define CLK_MUX_STAT_DMC 0x10400
> +#define CLK_DIV_DMC0 0x10500
> +#define CLK_DIV_DMC1 0x10504
> +#define CLK_DIV_STAT_DMC0 0x10600
> +#define CLK_DIV_STAT_DMC1 0x10604
> +#define CLK_GATE_IP_DMC 0x10900
> +#define CLKOUT_CMU_DMC 0x10A00
> +#define CLKOUT_CMU_DMC_DIV_STAT 0x10A04
> +#define DCGIDX_MAP0 0x11000
> +#define DCGIDX_MAP1 0x11004
> +#define DCGIDX_MAP2 0x11008
> +#define DCGPERF_MAP0 0x11020
> +#define DCGPERF_MAP1 0x11024
> +#define DVCIDX_MAP 0x11040
> +#define FREQ_CPU 0x11060
> +#define FREQ_DPM 0x11064
> +#define DVSEMCLK_EN 0x11080
> +#define MAXPERF 0x11084
> +#define APLL_LOCK 0x14000
> +#define MPLL_LOCK 0x14008
> +#define APLL_CON0 0x14100
> +#define APLL_CON1 0x14104
> +#define MPLL_CON0 0x14108
> +#define MPLL_CON1 0x1410C
> +/* CMU_CPU registers */
> +#define CLK_SRC_CPU 0x14200
> +#define CLK_MUX_STAT_CPU 0x14400
> +#define CLK_DIV_CPU0 0x14500
> +#define CLK_DIV_CPU1 0x14504
> +#define CLK_DIV_STAT_CPU0 0x14600
> +#define CLK_DIV_STAT_CPU1 0x14604
> +#define CLK_GATE_SCLK_CPU 0x14800
> +#define CLK_GATE_IP_CPU 0x14900
> +#define CLKOUT_CMU_CPU 0x14A00
> +#define CLKOUT_CMU_CPU_DIV_STAT 0x14A04
> +#define ARMCLK_STOPCTRL 0x15000
> +#define ATCLK_STOPCTRL 0x15004
> +#define PARITYFAIL_STATUS 0x15010
> +#define PARITYFAIL_CLEAR 0x15014
> +#define PWR_CTRL 0x15020
> +#define APLL_CON0_L8 0x15100
> +#define APLL_CON0_L7 0x15104
> +#define APLL_CON0_L6 0x15108
> +#define APLL_CON0_L5 0x1510C
> +#define APLL_CON0_L4 0x15110
> +#define APLL_CON0_L3 0x15114
> +#define APLL_CON0_L2 0x15118
> +#define APLL_CON0_L1 0x1511C
> +#define IEM_CONTROL 0x15120
> +#define APLL_CON1_L8 0x15200
> +#define APLL_CON1_L7 0x15204
> +#define APLL_CON1_L6 0x15208
> +#define APLL_CON1_L5 0x1520C
> +#define APLL_CON1_L4 0x15210
> +#define APLL_CON1_L3 0x15214
> +#define APLL_CON1_L2 0x15218
> +#define APLL_CON1_L1 0x1521C
> +#define CLKDIV_IEM_L8 0x15300
> +#define CLKDIV_IEM_L7 0x15304
> +#define CLKDIV_IEM_L6 0x15308
> +#define CLKDIV_IEM_L5 0x1530C
> +#define CLKDIV_IEM_L4 0x15310
> +#define CLKDIV_IEM_L3 0x15314
> +#define CLKDIV_IEM_L2 0x15318
> +#define CLKDIV_IEM_L1 0x1531C
> +
> +
> +typedef struct Exynos4210CmuReg {
> + const char *name; /* for debugging */
> + uint32_t offset;
> + uint32_t reset_value;
> +} Exynos4210CmuReg;
> +
> +
> +static Exynos4210CmuReg exynos4210_cmu_regs[] = {
> + /* CMU_LEFTBUS registers */
> + {"CLK_SRC_LEFTBUS", CLK_SRC_LEFTBUS,
> 0x00000000},
> + {"CLK_MUX_STAT_LEFTBUS", CLK_MUX_STAT_LEFTBUS,
> 0x00000001},
> + {"CLK_DIV_LEFTBUS", CLK_DIV_LEFTBUS,
> 0x00000000},
> + {"CLK_DIV_STAT_LEFTBUS", CLK_DIV_STAT_LEFTBUS,
> 0x00000000},
> + {"CLK_GATE_IP_LEFTBUS", CLK_GATE_IP_LEFTBUS,
> 0xFFFFFFFF},
> + {"CLKOUT_CMU_LEFTBUS", CLKOUT_CMU_LEFTBUS,
> 0x00010000},
> + {"CLKOUT_CMU_LEFTBUS_DIV_STAT", CLKOUT_CMU_LEFTBUS_DIV_STAT,
> 0x00000000},
> + /* CMU_RIGHTBUS registers */
> + {"CLK_SRC_RIGHTBUS", CLK_SRC_RIGHTBUS,
> 0x00000000},
> + {"CLK_MUX_STAT_RIGHTBUS", CLK_MUX_STAT_RIGHTBUS,
> 0x00000001},
> + {"CLK_DIV_RIGHTBUS", CLK_DIV_RIGHTBUS,
> 0x00000000},
> + {"CLK_DIV_STAT_RIGHTBUS", CLK_DIV_STAT_RIGHTBUS,
> 0x00000000},
> + {"CLK_GATE_IP_RIGHTBUS", CLK_GATE_IP_RIGHTBUS,
> 0xFFFFFFFF},
> + {"CLKOUT_CMU_RIGHTBUS", CLKOUT_CMU_RIGHTBUS,
> 0x00010000},
> + {"CLKOUT_CMU_RIGHTBUS_DIV_STAT", CLKOUT_CMU_RIGHTBUS_DIV_STAT,
> 0x00000000},
> + /* CMU_TOP registers */
> + {"EPLL_LOCK", EPLL_LOCK, 0x00000FFF},
> + {"VPLL_LOCK", VPLL_LOCK, 0x00000FFF},
> + {"EPLL_CON0", EPLL_CON0, 0x00300301},
> + {"EPLL_CON1", EPLL_CON1, 0x00000000},
> + {"VPLL_CON0", VPLL_CON0, 0x00240201},
> + {"VPLL_CON1", VPLL_CON1, 0x66010464},
> + {"CLK_SRC_TOP0", CLK_SRC_TOP0, 0x00000000},
> + {"CLK_SRC_TOP1", CLK_SRC_TOP1, 0x00000000},
> + {"CLK_SRC_CAM", CLK_SRC_CAM, 0x11111111},
> + {"CLK_SRC_TV", CLK_SRC_TV, 0x00000000},
> + {"CLK_SRC_MFC", CLK_SRC_MFC, 0x00000000},
> + {"CLK_SRC_G3D", CLK_SRC_G3D, 0x00000000},
> + {"CLK_SRC_IMAGE", CLK_SRC_IMAGE, 0x00000000},
> + {"CLK_SRC_LCD0", CLK_SRC_LCD0, 0x00001111},
> + {"CLK_SRC_LCD1", CLK_SRC_LCD1, 0x00001111},
> + {"CLK_SRC_MAUDIO", CLK_SRC_MAUDIO, 0x00000005},
> + {"CLK_SRC_FSYS", CLK_SRC_FSYS, 0x00011111},
> + {"CLK_SRC_PERIL0", CLK_SRC_PERIL0, 0x00011111},
> + {"CLK_SRC_PERIL1", CLK_SRC_PERIL1, 0x01110055},
> + {"CLK_SRC_MASK_TOP", CLK_SRC_MASK_TOP, 0x00000001},
> + {"CLK_SRC_MASK_CAM", CLK_SRC_MASK_CAM, 0x11111111},
> + {"CLK_SRC_MASK_TV", CLK_SRC_MASK_TV, 0x00000111},
> + {"CLK_SRC_MASK_LCD0", CLK_SRC_MASK_LCD0, 0x00001111},
> + {"CLK_SRC_MASK_LCD1", CLK_SRC_MASK_LCD1, 0x00001111},
> + {"CLK_SRC_MASK_MAUDIO", CLK_SRC_MASK_MAUDIO, 0x00000001},
> + {"CLK_SRC_MASK_FSYS", CLK_SRC_MASK_FSYS, 0x01011111},
> + {"CLK_SRC_MASK_PERIL0", CLK_SRC_MASK_PERIL0, 0x00011111},
> + {"CLK_SRC_MASK_PERIL1", CLK_SRC_MASK_PERIL1, 0x01110111},
> + {"CLK_MUX_STAT_TOP", CLK_MUX_STAT_TOP, 0x11111111},
> + {"CLK_MUX_STAT_MFC", CLK_MUX_STAT_MFC, 0x00000111},
> + {"CLK_MUX_STAT_G3D", CLK_MUX_STAT_G3D, 0x00000111},
> + {"CLK_MUX_STAT_IMAGE", CLK_MUX_STAT_IMAGE, 0x00000111},
> + {"CLK_DIV_TOP", CLK_DIV_TOP, 0x00000000},
> + {"CLK_DIV_CAM", CLK_DIV_CAM, 0x00000000},
> + {"CLK_DIV_TV", CLK_DIV_TV, 0x00000000},
> + {"CLK_DIV_MFC", CLK_DIV_MFC, 0x00000000},
> + {"CLK_DIV_G3D", CLK_DIV_G3D, 0x00000000},
> + {"CLK_DIV_IMAGE", CLK_DIV_IMAGE, 0x00000000},
> + {"CLK_DIV_LCD0", CLK_DIV_LCD0, 0x00700000},
> + {"CLK_DIV_LCD1", CLK_DIV_LCD1, 0x00700000},
> + {"CLK_DIV_MAUDIO", CLK_DIV_MAUDIO, 0x00000000},
> + {"CLK_DIV_FSYS0", CLK_DIV_FSYS0, 0x00B00000},
> + {"CLK_DIV_FSYS1", CLK_DIV_FSYS1, 0x00000000},
> + {"CLK_DIV_FSYS2", CLK_DIV_FSYS2, 0x00000000},
> + {"CLK_DIV_FSYS3", CLK_DIV_FSYS3, 0x00000000},
> + {"CLK_DIV_PERIL0", CLK_DIV_PERIL0, 0x00000000},
> + {"CLK_DIV_PERIL1", CLK_DIV_PERIL1, 0x00000000},
> + {"CLK_DIV_PERIL2", CLK_DIV_PERIL2, 0x00000000},
> + {"CLK_DIV_PERIL3", CLK_DIV_PERIL3, 0x00000000},
> + {"CLK_DIV_PERIL4", CLK_DIV_PERIL4, 0x00000000},
> + {"CLK_DIV_PERIL5", CLK_DIV_PERIL5, 0x00000000},
> + {"CLKDIV2_RATIO", CLKDIV2_RATIO, 0x11111111},
> + {"CLK_DIV_STAT_TOP", CLK_DIV_STAT_TOP, 0x00000000},
> + {"CLK_DIV_STAT_CAM", CLK_DIV_STAT_CAM, 0x00000000},
> + {"CLK_DIV_STAT_TV", CLK_DIV_STAT_TV, 0x00000000},
> + {"CLK_DIV_STAT_MFC", CLK_DIV_STAT_MFC, 0x00000000},
> + {"CLK_DIV_STAT_G3D", CLK_DIV_STAT_G3D, 0x00000000},
> + {"CLK_DIV_STAT_IMAGE", CLK_DIV_STAT_IMAGE, 0x00000000},
> + {"CLK_DIV_STAT_LCD0", CLK_DIV_STAT_LCD0, 0x00000000},
> + {"CLK_DIV_STAT_LCD1", CLK_DIV_STAT_LCD1, 0x00000000},
> + {"CLK_DIV_STAT_MAUDIO", CLK_DIV_STAT_MAUDIO, 0x00000000},
> + {"CLK_DIV_STAT_FSYS0", CLK_DIV_STAT_FSYS0, 0x00000000},
> + {"CLK_DIV_STAT_FSYS1", CLK_DIV_STAT_FSYS1, 0x00000000},
> + {"CLK_DIV_STAT_FSYS2", CLK_DIV_STAT_FSYS2, 0x00000000},
> + {"CLK_DIV_STAT_FSYS3", CLK_DIV_STAT_FSYS3, 0x00000000},
> + {"CLK_DIV_STAT_PERIL0", CLK_DIV_STAT_PERIL0, 0x00000000},
> + {"CLK_DIV_STAT_PERIL1", CLK_DIV_STAT_PERIL1, 0x00000000},
> + {"CLK_DIV_STAT_PERIL2", CLK_DIV_STAT_PERIL2, 0x00000000},
> + {"CLK_DIV_STAT_PERIL3", CLK_DIV_STAT_PERIL3, 0x00000000},
> + {"CLK_DIV_STAT_PERIL4", CLK_DIV_STAT_PERIL4, 0x00000000},
> + {"CLK_DIV_STAT_PERIL5", CLK_DIV_STAT_PERIL5, 0x00000000},
> + {"CLKDIV2_STAT", CLKDIV2_STAT, 0x00000000},
> + {"CLK_GATE_SCLK_CAM", CLK_GATE_SCLK_CAM, 0xFFFFFFFF},
> + {"CLK_GATE_IP_CAM", CLK_GATE_IP_CAM, 0xFFFFFFFF},
> + {"CLK_GATE_IP_TV", CLK_GATE_IP_TV, 0xFFFFFFFF},
> + {"CLK_GATE_IP_MFC", CLK_GATE_IP_MFC, 0xFFFFFFFF},
> + {"CLK_GATE_IP_G3D", CLK_GATE_IP_G3D, 0xFFFFFFFF},
> + {"CLK_GATE_IP_IMAGE", CLK_GATE_IP_IMAGE, 0xFFFFFFFF},
> + {"CLK_GATE_IP_LCD0", CLK_GATE_IP_LCD0, 0xFFFFFFFF},
> + {"CLK_GATE_IP_LCD1", CLK_GATE_IP_LCD1, 0xFFFFFFFF},
> + {"CLK_GATE_IP_FSYS", CLK_GATE_IP_FSYS, 0xFFFFFFFF},
> + {"CLK_GATE_IP_GPS", CLK_GATE_IP_GPS, 0xFFFFFFFF},
> + {"CLK_GATE_IP_PERIL", CLK_GATE_IP_PERIL, 0xFFFFFFFF},
> + {"CLK_GATE_IP_PERIR", CLK_GATE_IP_PERIR, 0xFFFFFFFF},
> + {"CLK_GATE_BLOCK", CLK_GATE_BLOCK, 0xFFFFFFFF},
> + {"CLKOUT_CMU_TOP", CLKOUT_CMU_TOP, 0x00010000},
> + {"CLKOUT_CMU_TOP_DIV_STAT", CLKOUT_CMU_TOP_DIV_STAT, 0x00000000},
> + /* CMU_DMC registers */
> + {"CLK_SRC_DMC", CLK_SRC_DMC, 0x00010000},
> + {"CLK_SRC_MASK_DMC", CLK_SRC_MASK_DMC, 0x00010000},
> + {"CLK_MUX_STAT_DMC", CLK_MUX_STAT_DMC, 0x11100110},
> + {"CLK_DIV_DMC0", CLK_DIV_DMC0, 0x00000000},
> + {"CLK_DIV_DMC1", CLK_DIV_DMC1, 0x00000000},
> + {"CLK_DIV_STAT_DMC0", CLK_DIV_STAT_DMC0, 0x00000000},
> + {"CLK_DIV_STAT_DMC1", CLK_DIV_STAT_DMC1, 0x00000000},
> + {"CLK_GATE_IP_DMC", CLK_GATE_IP_DMC, 0xFFFFFFFF},
> + {"CLKOUT_CMU_DMC", CLKOUT_CMU_DMC, 0x00010000},
> + {"CLKOUT_CMU_DMC_DIV_STAT", CLKOUT_CMU_DMC_DIV_STAT, 0x00000000},
> + {"DCGIDX_MAP0", DCGIDX_MAP0, 0xFFFFFFFF},
> + {"DCGIDX_MAP1", DCGIDX_MAP1, 0xFFFFFFFF},
> + {"DCGIDX_MAP2", DCGIDX_MAP2, 0xFFFFFFFF},
> + {"DCGPERF_MAP0", DCGPERF_MAP0, 0xFFFFFFFF},
> + {"DCGPERF_MAP1", DCGPERF_MAP1, 0xFFFFFFFF},
> + {"DVCIDX_MAP", DVCIDX_MAP, 0xFFFFFFFF},
> + {"FREQ_CPU", FREQ_CPU, 0x00000000},
> + {"FREQ_DPM", FREQ_DPM, 0x00000000},
> + {"DVSEMCLK_EN", DVSEMCLK_EN, 0x00000000},
> + {"MAXPERF", MAXPERF, 0x00000000},
> + {"APLL_LOCK", APLL_LOCK, 0x00000FFF},
> + {"MPLL_LOCK", MPLL_LOCK, 0x00000FFF},
> + {"APLL_CON0", APLL_CON0, 0x00C80601},
> + {"APLL_CON1", APLL_CON1, 0x0000001C},
> + {"MPLL_CON0", MPLL_CON0, 0x00C80601},
> + {"MPLL_CON1", MPLL_CON1, 0x0000001C},
> + /* CMU_CPU registers */
> + {"CLK_SRC_CPU", CLK_SRC_CPU, 0x00000000},
> + {"CLK_MUX_STAT_CPU", CLK_MUX_STAT_CPU, 0x00110101},
> + {"CLK_DIV_CPU0", CLK_DIV_CPU0, 0x00000000},
> + {"CLK_DIV_CPU1", CLK_DIV_CPU1, 0x00000000},
> + {"CLK_DIV_STAT_CPU0", CLK_DIV_STAT_CPU0, 0x00000000},
> + {"CLK_DIV_STAT_CPU1", CLK_DIV_STAT_CPU1, 0x00000000},
> + {"CLK_GATE_SCLK_CPU", CLK_GATE_SCLK_CPU, 0xFFFFFFFF},
> + {"CLK_GATE_IP_CPU", CLK_GATE_IP_CPU, 0xFFFFFFFF},
> + {"CLKOUT_CMU_CPU", CLKOUT_CMU_CPU, 0x00010000},
> + {"CLKOUT_CMU_CPU_DIV_STAT", CLKOUT_CMU_CPU_DIV_STAT, 0x00000000},
> + {"ARMCLK_STOPCTRL", ARMCLK_STOPCTRL, 0x00000044},
> + {"ATCLK_STOPCTRL", ATCLK_STOPCTRL, 0x00000044},
> + {"PARITYFAIL_STATUS", PARITYFAIL_STATUS, 0x00000000},
> + {"PARITYFAIL_CLEAR", PARITYFAIL_CLEAR, 0x00000000},
> + {"PWR_CTRL", PWR_CTRL, 0x00000033},
> + {"APLL_CON0_L8", APLL_CON0_L8, 0x00C80601},
> + {"APLL_CON0_L7", APLL_CON0_L7, 0x00C80601},
> + {"APLL_CON0_L6", APLL_CON0_L6, 0x00C80601},
> + {"APLL_CON0_L5", APLL_CON0_L5, 0x00C80601},
> + {"APLL_CON0_L4", APLL_CON0_L4, 0x00C80601},
> + {"APLL_CON0_L3", APLL_CON0_L3, 0x00C80601},
> + {"APLL_CON0_L2", APLL_CON0_L2, 0x00C80601},
> + {"APLL_CON0_L1", APLL_CON0_L1, 0x00C80601},
> + {"IEM_CONTROL", IEM_CONTROL, 0x00000000},
> + {"APLL_CON1_L8", APLL_CON1_L8, 0x00000000},
> + {"APLL_CON1_L7", APLL_CON1_L7, 0x00000000},
> + {"APLL_CON1_L6", APLL_CON1_L6, 0x00000000},
> + {"APLL_CON1_L5", APLL_CON1_L5, 0x00000000},
> + {"APLL_CON1_L4", APLL_CON1_L4, 0x00000000},
> + {"APLL_CON1_L3", APLL_CON1_L3, 0x00000000},
> + {"APLL_CON1_L2", APLL_CON1_L2, 0x00000000},
> + {"APLL_CON1_L1", APLL_CON1_L1, 0x00000000},
> + {"CLKDIV_IEM_L8", CLKDIV_IEM_L8, 0x00000000},
> + {"CLKDIV_IEM_L7", CLKDIV_IEM_L7, 0x00000000},
> + {"CLKDIV_IEM_L6", CLKDIV_IEM_L6, 0x00000000},
> + {"CLKDIV_IEM_L5", CLKDIV_IEM_L5, 0x00000000},
> + {"CLKDIV_IEM_L4", CLKDIV_IEM_L4, 0x00000000},
> + {"CLKDIV_IEM_L3", CLKDIV_IEM_L3, 0x00000000},
> + {"CLKDIV_IEM_L2", CLKDIV_IEM_L2, 0x00000000},
> + {"CLKDIV_IEM_L1", CLKDIV_IEM_L1, 0x00000000},
> +};
> +
> +
> +/*
> + * There are five CMUs:
> + *
> + * CMU_LEFTBUS
> + * CMU_RIGHTBUS
> + * CMU_TOP
> + * CMU_DMC
> + * CMU_CPU
> + *
> + * each of them uses 16KB address space for SFRs
> + *
> + * + 0x4000 because SFR region for CMUs starts at 0x10030000,
> + * but the first CMU (CMU_LEFTBUS) starts with this offset
> + *
> + */
> +#define EXYNOS4210_CMU_REGS_MEM_SIZE (0x4000 * 5 + 0x4000)
It seems a bit unlikely to me that there's really a single indivisble
bit of hardware with 23 kilobytes worth of register area. Is there
a better way of structuring this so that there are a number of
sub-memory-regions that cover this space each with their own
read/write functions (and perhaps with their own state structs)?
(eg instantiate five CPUs at the right locations.)
> +
> +/*
> + * for indexing register in the uint32_t array
> + *
> + * 'reg' - register offset (see offsets definitions above)
> + *
> + */
> +#define I_(reg) (reg / sizeof(uint32_t))
> +
> +#define XOM_0 1 /* Select XXTI (0) or XUSBXTI (1) base clock source */
> +
> +/*
> + * Offsets in CLK_SRC_CPU register
> + * for control MUXMPLL and MUXAPLL
> + *
> + * 0 = FINPLL, 1 = MOUTM(A)PLLFOUT
> + */
> +#define MUX_APLL_SEL_SHIFT 0
> +#define MUX_MPLL_SEL_SHIFT 8
> +#define MUX_CORE_SEL_SHIFT 16
> +#define MUX_HPM_SEL_SHIFT 20
> +
> +#define MUX_APLL_SEL (1 << MUX_APLL_SEL_SHIFT)
> +#define MUX_MPLL_SEL (1 << MUX_MPLL_SEL_SHIFT)
> +#define MUX_CORE_SEL (1 << MUX_CORE_SEL_SHIFT)
> +#define MUX_HPM_SEL (1 << MUX_HPM_SEL_SHIFT)
> +
> +/* Offsets for fields in CLK_MUX_STAT_CPU register */
> +#define APLL_SEL_SHIFT 0
> +#define APLL_SEL_MASK 0x00000007
> +#define MPLL_SEL_SHIFT 8
> +#define MPLL_SEL_MASK 0x00000700
> +#define CORE_SEL_SHIFT 16
> +#define CORE_SEL_MASK 0x00070000
> +#define HPM_SEL_SHIFT 20
> +#define HPM_SEL_MASK 0x00700000
> +
> +
> +/* Offsets for fields in <pll>_CON0 register */
> +#define PLL_ENABLE_SHIFT 31
> +#define PLL_ENABLE_MASK 0x80000000 /* [31] bit */
> +#define PLL_LOCKED_MASK 0x20000000 /* [29] bit */
> +#define PLL_MDIV_SHIFT 16
> +#define PLL_MDIV_MASK 0x03FF0000 /* [25:16] bits */
> +#define PLL_PDIV_SHIFT 8
> +#define PLL_PDIV_MASK 0x00003F00 /* [13:8] bits */
> +#define PLL_SDIV_SHIFT 0
> +#define PLL_SDIV_MASK 0x00000007 /* [2:0] bits */
> +
> +
> +
> +/*
> + * Offset in CLK_DIV_CPU0 register
> + * for DIVAPLL clock divider ratio
> + */
> +#define APLL_RATIO_SHIFT 24
> +#define APLL_RATIO_MASK 0x07000000 /* [26:24] bits */
> +
> +/*
> + * Offset in CLK_DIV_TOP register
> + * for DIVACLK_100 clock divider ratio
> + */
> +#define ACLK_100_RATIO_SHIFT 4
> +#define ACLK_100_RATIO_MASK 0x000000f0 /* [7:4] bits */
> +
> +/* Offset in CLK_SRC_TOP0 register */
> +#define MUX_ACLK_100_SEL_SHIFT 16
> +
> +/*
> + * Offsets in CLK_SRC_PERIL0 register
> + * for clock sources of UARTs
> + */
> +#define UART0_SEL_SHIFT 0
> +#define UART1_SEL_SHIFT 4
> +#define UART2_SEL_SHIFT 8
> +#define UART3_SEL_SHIFT 12
> +#define UART4_SEL_SHIFT 16
> +/*
> + * Offsets in CLK_DIV_PERIL0 register
> + * for clock divider of UARTs
> + */
> +#define UART0_DIV_SHIFT 0
> +#define UART1_DIV_SHIFT 4
> +#define UART2_DIV_SHIFT 8
> +#define UART3_DIV_SHIFT 12
> +#define UART4_DIV_SHIFT 16
> +
> +typedef struct {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t reg[EXYNOS4210_CMU_REGS_MEM_SIZE];
> +
> +} Exynos4210CmuState;
> +
> +#define SOURCES_NUMBER 9
> +#define RECIPIENTS_NUMBER 9
> +
> +typedef struct Exynos4210CmuClockState {
> +
> + const char *name;
> + uint64_t rate;
> +
> + /* Current source clock */
> + struct Exynos4210CmuClockState *source;
> + /*
> + * Available sources. Their order must correspond to CLK_SRC_
> register
> + */
> + struct Exynos4210CmuClockState *sources[SOURCES_NUMBER];
> + /* Who uses this clock? */
> + struct Exynos4210CmuClockState *recipients[RECIPIENTS_NUMBER];
> +
> + uint32_t src_reg; /* Offset of CLK_SRC_<*> register */
> + uint32_t div_reg; /* Offset of CLK_DIV_<*> register */
> +
> + /*
> + * Shift for MUX_<clk>_SEL value which is stored
> + * in appropriate CLK_MUX_STAT_<cmu> register
> + */
> + uint8_t mux_shift;
> +
> + /*
> + * Shift for <clk>_RATIO value which is stored
> + * in appropriate CLK_DIV_<cmu> register
> + */
> + uint8_t div_shift;
> +
> +} Exynos4210CmuClockState;
So, how does this compare with the struct clk in hw/omap_clk.c ?
Is there a useful generalisation we can make rather than having
every SOC model doing its own thing for clock trees?
> +
> +/* Clocks from Clock Pads */
> +
> +/* It should be used only for testing purposes. XOM_0 is 0 */
> +static Exynos4210CmuClockState xxti = {
> + .name = "XXTI",
> + .rate = 24000000,
> +};
> +
> +/* Main source. XOM_0 is 1 */
> +static Exynos4210CmuClockState xusbxti = {
> + .name = "XUSBXTI",
> + .rate = 24000000,
> +};
> +
> +/* PLLs */
> +
> +static Exynos4210CmuClockState mpll = {
> + .name = "MPLL",
> + .source = (XOM_0 ? &xusbxti : &xxti),
> +};
> +
> +static Exynos4210CmuClockState apll = {
> + .name = "APLL",
> + .source = (XOM_0 ? &xusbxti : &xxti),
> +};
> +
> +
> +/**/
> +static Exynos4210CmuClockState sclk_mpll = {
> + .name = "SCLK_MPLL",
> + .sources = {XOM_0 ? &xusbxti : &xxti, &mpll},
> + .src_reg = CLK_SRC_CPU,
> + .mux_shift = MUX_MPLL_SEL_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_apll = {
> + .name = "SCLK_APLL",
> + .sources = {XOM_0 ? &xusbxti : &xxti, &apll},
> + .src_reg = CLK_SRC_CPU,
> + .div_reg = CLK_DIV_CPU0,
> + .mux_shift = MUX_APLL_SEL_SHIFT,
> + .div_shift = APLL_RATIO_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState aclk_100 = {
> + .name = "ACLK_100",
> + .sources = {&sclk_mpll, &sclk_apll},
> + .src_reg = CLK_SRC_TOP0,
> + .div_reg = CLK_DIV_TOP,
> + .mux_shift = MUX_ACLK_100_SEL_SHIFT,
> + .div_shift = ACLK_100_RATIO_SHIFT,
> +};
> +
> +
> +/* TODO: add other needed structures for UARTs sources */
> +static Exynos4210CmuClockState sclk_uart0 = {
> + .name = "SCLK_UART0",
> + .sources = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> + .src_reg = CLK_SRC_PERIL0,
> + .div_reg = CLK_DIV_PERIL0,
> + .mux_shift = UART0_SEL_SHIFT,
> + .div_shift = UART0_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart1 = {
> + .name = "SCLK_UART1",
> + .sources = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> + .src_reg = CLK_SRC_PERIL0,
> + .div_reg = CLK_DIV_PERIL0,
> + .mux_shift = UART1_SEL_SHIFT,
> + .div_shift = UART1_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart2 = {
> + .name = "SCLK_UART2",
> + .sources = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> + .src_reg = CLK_SRC_PERIL0,
> + .div_reg = CLK_DIV_PERIL0,
> + .mux_shift = UART2_SEL_SHIFT,
> + .div_shift = UART2_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart3 = {
> + .name = "SCLK_UART3",
> + .sources = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> + .src_reg = CLK_SRC_PERIL0,
> + .div_reg = CLK_DIV_PERIL0,
> + .mux_shift = UART3_SEL_SHIFT,
> + .div_shift = UART3_DIV_SHIFT,
> +};
> +
> +static Exynos4210CmuClockState sclk_uart4 = {
> + .name = "SCLK_UART4",
> + .sources = {&xxti, &xusbxti, NULL, NULL, NULL, NULL, &sclk_mpll},
> + .src_reg = CLK_SRC_PERIL0,
> + .div_reg = CLK_DIV_PERIL0,
> + .mux_shift = UART4_SEL_SHIFT,
> + .div_shift = UART4_DIV_SHIFT,
> +};
> +
> +/*
> + * This array must correspond to Exynos4210CmuClock enumerator
> + * which is defined in exynos4210.h file
> + *
> + */
> +static Exynos4210CmuClockState *exynos4210_clock[] = {
> + &xxti,
> + &xusbxti,
> + &apll,
> + &mpll,
> + &sclk_apll,
> + &sclk_mpll,
> + &aclk_100,
> + &sclk_uart0,
> + &sclk_uart1,
> + &sclk_uart2,
> + &sclk_uart3,
> + &sclk_uart4,
> + NULL
> +};
> +
> +
> +uint64_t exynos4210_cmu_get_rate(Exynos4210CmuClock clock)
> +{
> + return exynos4210_clock[clock]->rate;
> +}
> +
> +#ifdef DEBUG_CMU
> +/* The only meaning of life - debugging. This functions should be only used
> + * inside PRINT_DEBUG_... macroses
"This function".
"macros".
> + */
> +static const char *exynos4210_cmu_regname(target_phys_addr_t offset)
> +{
> +
> + int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);
int regs_number = ARRAY_SIZE(exynos4210_cmu_regs);
> + int i;
> +
> + for (i = 0; i < regs_number; i++) {
> + if (offset == exynos4210_cmu_regs[i].offset) {
> + return exynos4210_cmu_regs[i].name;
> + }
> + }
> +
> + return NULL;
> +}
> +#endif
> +
> +static void exynos4210_cmu_set_pll(void *opaque, target_phys_addr_t offset)
> +{
> + Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> + uint32_t pdiv, mdiv, sdiv, enable;
> +
> + /*
> + * FOUT = MDIV * FIN / (PDIV * 2^(SDIV-1))
> + */
> +
> + enable = (s->reg[I_(offset)] & PLL_ENABLE_MASK) >> PLL_ENABLE_SHIFT;
> + mdiv = (s->reg[I_(offset)] & PLL_MDIV_MASK) >> PLL_MDIV_SHIFT;
> + pdiv = (s->reg[I_(offset)] & PLL_PDIV_MASK) >> PLL_PDIV_SHIFT;
> + sdiv = (s->reg[I_(offset)] & PLL_SDIV_MASK) >> PLL_SDIV_SHIFT;
> +
> + switch (offset) {
> +
> + case MPLL_CON0:
> + if (mpll.source) {
> + if (enable) {
> + mpll.rate = mdiv * mpll.source->rate / (pdiv * (1 <<
> (sdiv-1)));
> + } else {
> + mpll.rate = 0;
> + }
> + } else {
> + hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
> + mpll.name);
> + }
> + PRINT_DEBUG("mpll.rate: %llu\n", (unsigned long long int)mpll.rate);
> + break;
> +
> + case APLL_CON0:
> + if (apll.source) {
> + if (enable) {
> + apll.rate = mdiv * apll.source->rate / (pdiv * (1 <<
> (sdiv-1)));
> + } else {
> + apll.rate = 0;
> + }
> + } else {
> + hw_error("exynos4210_cmu_set_pll: Source undefined for %s\n",
> + apll.name);
> + }
> + PRINT_DEBUG("apll.rate: %llu\n", (unsigned long long int)apll.rate);
> + break;
> +
> + default:
> + hw_error("exynos4210_cmu_set_pll: Bad offset 0x%x\n", (int)offset);
> + }
> +
> + s->reg[I_(offset)] |= PLL_LOCKED_MASK;
> +}
> +
> +
> +static void
> +exynos4210_cmu_set_rate(void *opaque, Exynos4210CmuClockState *clock)
> +{
> + Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> + int i;
> +
> + /* Rates of PLLs are calculated differently than ordinary clocks rates */
> + if (clock == &mpll) {
> + exynos4210_cmu_set_pll(s, MPLL_CON0);
> + } else if (clock == &apll) {
> + exynos4210_cmu_set_pll(s, APLL_CON0);
> + } else if ((clock != &xxti) && (clock != &xusbxti)) {
> + /*
> + * Not root clock. We don't need calculating rate
> + * of root clock because it is hard coded.
> + */
> + uint32_t src_index = I_(clock->src_reg);
> + uint32_t div_index = I_(clock->div_reg);
> + clock->source = clock->sources[(s->reg[src_index] >>
> + clock->mux_shift) & 0xf];
> + clock->rate = muldiv64(clock->source->rate, 1,
> + (((s->reg[div_index] >> clock->div_shift) &
> 0xf)
> + + 1));
> +
> + PRINT_DEBUG_EXTEND("SRC: <0x%05x> %s, SHIFT: %d\n",
> + clock->src_reg,
> + exynos4210_cmu_regname(clock->src_reg),
> + clock->mux_shift);
> +
> + PRINT_DEBUG("%s [%s:%llu]: %llu\n",
> + clock->name,
> + clock->source->name,
> + (uint64_t)clock->source->rate,
> + (uint64_t)clock->rate);
> + }
> +
> + /* Visit all recipients for given clock */
> + i = 0;
> + do {
> +
> + Exynos4210CmuClockState *recipient_clock = clock->recipients[i];
> +
> + if (recipient_clock == NULL) {
> + PRINT_DEBUG_EXTEND("%s have %d recipients\n", clock->name, i);
> + break;
> + }
> +
> + uint32_t src_index = recipient_clock->src_reg / sizeof(uint32_t);
> + int source_index = s->reg[src_index] >>
> + recipient_clock->mux_shift & 0xf;
> + recipient_clock->source = recipient_clock->sources[source_index];
> +
> + if (recipient_clock->source != clock) {
> + break;
> + }
> +
> + exynos4210_cmu_set_rate(s, recipient_clock);
> +
> + i++;
> + } while (i < RECIPIENTS_NUMBER);
> +}
> +
> +
> +static uint64_t exynos4210_cmu_read(void *opaque, target_phys_addr_t offset,
> + unsigned size)
> +{
> + Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> +
> + if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
> + hw_error("exynos4210_cmu_read: Bad offset 0x%x\n", (int)offset);
> + }
> +
> + PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n", offset,
> + exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
> +
> + return s->reg[I_(offset)];
> +}
> +
> +
> +static void exynos4210_cmu_write(void *opaque, target_phys_addr_t offset,
> + uint64_t val, unsigned size)
> +{
> + Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> + uint32_t pre_val;
> +
> + if (offset > (EXYNOS4210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
> + hw_error("exynos4210_cmu_write: Bad offset 0x%x\n", (int)offset);
> + }
General rule: don't hw_error() for things a guest can trigger, like writing
to bogus offsets in a device.
> +
> + pre_val = s->reg[I_(offset)];
> + s->reg[I_(offset)] = val;
> +
> + PRINT_DEBUG_EXTEND("<0x%05x> %s <- %08x\n", offset,
> + exynos4210_cmu_regname(offset), s->reg[I_(offset)]);
> +
> + switch (offset) {
> +
> + case APLL_CON0:
> + val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
> + s->reg[I_(offset)] = val;
> + exynos4210_cmu_set_rate(s, &apll);
> + break;
> + case MPLL_CON0:
> + val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
> + s->reg[I_(offset)] = val;
> + exynos4210_cmu_set_rate(s, &mpll);
> + break;
> + case CLK_SRC_CPU:
> + {
> + if (val & MUX_APLL_SEL) {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(APLL_SEL_MASK)) |
> + (2 << APLL_SEL_SHIFT);
> +
> + if ((pre_val & MUX_APLL_SEL) !=
> + (s->reg[I_(offset)] & MUX_APLL_SEL)) {
> + exynos4210_cmu_set_rate(s, &apll);
> + }
> +
> + } else {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(APLL_SEL_MASK)) |
> + (1 << APLL_SEL_SHIFT);
> +
> + if ((pre_val & MUX_APLL_SEL) !=
> + (s->reg[I_(offset)] & MUX_APLL_SEL)) {
> + exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> + }
> + }
> +
> +
> + if (val & MUX_MPLL_SEL) {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(MPLL_SEL_MASK)) |
> + (2 << MPLL_SEL_SHIFT);
> +
> + if ((pre_val & MUX_MPLL_SEL) !=
> + (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
> + exynos4210_cmu_set_rate(s, &mpll);
> + }
> +
> + } else {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(MPLL_SEL_MASK)) |
> + (1 << MPLL_SEL_SHIFT);
> +
> + if ((pre_val & MUX_MPLL_SEL) !=
> + (s->reg[I_(offset)] & MUX_MPLL_SEL)) {
> + exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> + }
> + }
> +
> + if (val & MUX_CORE_SEL) {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(CORE_SEL_MASK)) |
> + (2 << CORE_SEL_SHIFT);
> +
> + if ((pre_val & MUX_CORE_SEL) !=
> + (s->reg[I_(offset)] & MUX_CORE_SEL)) {
> + exynos4210_cmu_set_rate(s, &sclk_mpll);
> + }
> +
> + } else {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(CORE_SEL_MASK)) |
> + (1 << CORE_SEL_SHIFT);
> +
> + if ((pre_val & MUX_CORE_SEL) !=
> + (s->reg[I_(offset)] & MUX_CORE_SEL)) {
> + exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> + }
> + }
> +
> + if (val & MUX_HPM_SEL) {
> + exynos4210_cmu_set_rate(s, &sclk_mpll);
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(HPM_SEL_MASK)) |
> + (2 << HPM_SEL_SHIFT);
> +
> + if ((pre_val & MUX_HPM_SEL) != (s->reg[I_(offset)] &
> MUX_HPM_SEL)) {
> + exynos4210_cmu_set_rate(s, &sclk_mpll);
> + }
> +
> + } else {
> + s->reg[I_(CLK_MUX_STAT_CPU)] =
> + (s->reg[I_(CLK_MUX_STAT_CPU)] & ~(HPM_SEL_MASK)) |
> + (1 << HPM_SEL_SHIFT);
> +
> + if ((pre_val & MUX_HPM_SEL) != (s->reg[I_(offset)] &
> MUX_HPM_SEL)) {
> + exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> + }
> + }
> + }
> + break;
> + case CLK_DIV_CPU0:
> + exynos4210_cmu_set_rate(s, &sclk_apll);
> + exynos4210_cmu_set_rate(s, &sclk_mpll);
> + break;
> + case CLK_SRC_TOP0:
> + case CLK_DIV_TOP:
> + exynos4210_cmu_set_rate(s, &aclk_100);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +
> +static const MemoryRegionOps exynos4210_cmu_ops = {
> + .read = exynos4210_cmu_read,
> + .write = exynos4210_cmu_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +
> +static void exynos4210_cmu_reset(void *opaque)
> +{
> + Exynos4210CmuState *s = (Exynos4210CmuState *)opaque;
> + int i = 0, j = 0, n = 0;
> + int regs_number = sizeof(exynos4210_cmu_regs)/sizeof(Exynos4210CmuReg);
> + uint32_t index = 0;
> +
> + /* Set default values for registers */
> + for (i = 0; i < regs_number; i++) {
> + index = (exynos4210_cmu_regs[i].offset) / sizeof(uint32_t);
> + s->reg[index] = exynos4210_cmu_regs[i].reset_value;
> + }
> +
> + /* clear recipients array from previous reset */
> + for (i = 0; i < CLOCKS_NUMBER; i++) {
> + bzero(exynos4210_clock[i]->recipients,
> + RECIPIENTS_NUMBER * sizeof(Exynos4210CmuClockState *));
bzero() is deprecated. Use memset().
> + }
> +
> + /*
> + * Here we fill '.recipients' fields in all clocks. Also we fill empty
> + * 'sources[]' arrays by values of 'source' fields (it is necessary
> + * for set rate, for example). If 'sources[]' array and 'source' field
> + * is empty simultaneously we get hw_error.
> + *
> + */
> + for (i = 0; i < CLOCKS_NUMBER; i++) {
> +
> + /* visit all clocks in the exynos4210_clock */
> +
> + PRINT_DEBUG("[SOURCES] %s: ", exynos4210_clock[i]->name);
> +
> + j = 0;
> + do { /* visit all sources for current clock (exynos4210_clock[i]) */
> +
> + if ((exynos4210_clock[i]->sources[j] == NULL)) {
> +
> + if (j == 0) { /* check if we have empty '.sources[]' array */
> + if (exynos4210_clock[i]->source != NULL) {
> + exynos4210_clock[i]->sources[j] =
> + exynos4210_clock[i]->source;
> + } else {
> + /*
> + * We haven't any defined sources for this clock.
> Error
> + * during definition of appropriate clock structure
> + *
> + */
> + if ((exynos4210_clock[i] != &xusbxti) &&
> + (exynos4210_clock[i] != &xxti)) {
> +
> + hw_error("exynos4210_cmu_reset:"
> + "There aren't any sources for %s
> clock!\n",
> + exynos4210_clock[i]->name);
> + } else {
> + /*
> + * we don't need any sources for this clock
> + * because it's a root clock
> + */
> + break;
> + }
> + }
> + } else {
> + break; /* leave because there are no more sources */
> + }
> +
> + }
> +
> + PRINT_DEBUG_SIMPLE(" %s", exynos4210_clock[i]->sources[j]->name);
> +
> + /*
> + * find first empty place in 'recipients[]' array of
> + * current 'sources' element and put current clock there
> + */
> + n = 0;
> + do {
> + if ((exynos4210_clock[i]->sources[j]->recipients[n]) ==
> NULL) {
> + exynos4210_clock[i]->sources[j]->recipients[n] =
> + exynos4210_clock[i];
> + break;
> + }
> + n++;
> + } while (n < RECIPIENTS_NUMBER);
What's wrong with "for (n = 0; n < RECIPIENTS_NUMBER; n++)" ?
(Ditto the loop with j, for that matter.)
> +
> + j++;
> +
> + } while (j < SOURCES_NUMBER);
> +
> + PRINT_DEBUG_SIMPLE("\n");
> +
> + } /* CLOCKS_NUMBER */
> +
> +#ifdef DEBUG_CMU
> + for (i = 0; i < CLOCKS_NUMBER; i++) {
> + PRINT_DEBUG("[RECIPIENTS] %s: ", exynos4210_clock[i]->name);
> + for (j = 0;
> + (j < RECIPIENTS_NUMBER) &&
> + ((exynos4210_clock[i]->recipients[j]) != NULL);
> + j++) {
> + PRINT_DEBUG_SIMPLE("%s ",
> exynos4210_clock[i]->recipients[j]->name);
> + }
> + PRINT_DEBUG_SIMPLE("\n");
> + }
> +#endif
> +
> + exynos4210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
> +}
> +
> +static const VMStateDescription vmstate_exynos4210_cmu = {
> + .name = "exynos4210.cmu",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .fields = (VMStateField[]) {
> + /*
> + * TODO: Maybe we should save Exynos4210CmuClockState structs as well
> + */
If there's anything in them that can change at runtime (as opposed
to being statically determined at board init time) then yes, it needs
to be saved/restored.
> + VMSTATE_UINT32_ARRAY(reg, Exynos4210CmuState,
> + EXYNOS4210_CMU_REGS_MEM_SIZE),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static int exynos4210_cmu_init(SysBusDevice *dev)
> +{
> + Exynos4210CmuState *s = FROM_SYSBUS(Exynos4210CmuState, dev);
> +
> + /* memory mapping */
> + memory_region_init_io(&s->iomem, &exynos4210_cmu_ops, s,
> "exynos4210.cmu",
> + EXYNOS4210_CMU_REGS_MEM_SIZE);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + qemu_register_reset(exynos4210_cmu_reset, s);
> +
> + vmstate_register(&dev->qdev, -1, &vmstate_exynos4210_cmu, s);
If you use sysbus_register_withprop() rather than sysbus_register_dev()
you can pass the VMState and reset functions as .qdev.vmsd and .qdev.reset
fields in the SysBusDeviceInfo struct.
> +
> + exynos4210_cmu_reset(s);
You don't need to explicitly call reset here, qdev will do it for you.
> +
> + return 0;
> +}
> +
> +
> +static void exynos4210_cmu_register(void)
> +{
> + sysbus_register_dev("exynos4210.cmu",
> + sizeof(Exynos4210CmuState),
> + exynos4210_cmu_init);
> +}
> +
> +
> +device_init(exynos4210_cmu_register)
> --
> 1.7.4.1
>
>
- [Qemu-devel] [PATCH v3 09/14] hw/exynos4210.c: Boot secondary CPU., (continued)
- [Qemu-devel] [PATCH v3 09/14] hw/exynos4210.c: Boot secondary CPU., Evgeny Voevodin, 2011/12/12
- [Qemu-devel] [PATCH v3 06/14] ARM: exynos4210: PWM support., Evgeny Voevodin, 2011/12/12
- [Qemu-devel] [PATCH v3 10/14] hw/lan9118: Add basic 16-bit mode support., Evgeny Voevodin, 2011/12/12
- [Qemu-devel] [PATCH v3 07/14] hw/arm_boot.c: Add new secondary CPU bootloader., Evgeny Voevodin, 2011/12/12
- [Qemu-devel] [PATCH v3 03/14] ARM: exynos4210: UART support, Evgeny Voevodin, 2011/12/12
- [Qemu-devel] [PATCH v3 02/14] ARM: exynos4210: CMU support, Evgeny Voevodin, 2011/12/12
- Re: [Qemu-devel] [PATCH v3 02/14] ARM: exynos4210: CMU support, Dmitry Solodkiy, 2011/12/15
[Qemu-devel] [PATCH v3 11/14] hw/exynos4210.c: Add LAN support for SMDKC210., Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 05/14] ARM: exynos4210: IRQ subsystem support., Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 08/14] ARM: exynos4210: MCT support., Evgeny Voevodin, 2011/12/12
[Qemu-devel] [PATCH v3 12/14] hw/sd.c, hw/sd.h: add receive ready query routine to SD/MMC API, Evgeny Voevodin, 2011/12/12