@@ -344,6 +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 += s5pc210.o s5pc210_cmu.o s5pc210_uart.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
new file mode 100644
@@ -0,0 +1,284 @@
+/*
+ * Samsung s5pc210 (aka Exynos4210) board emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ * Created on: 07.2011
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "boards.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exec-memory.h"
+#include "s5pc210.h"
+
+#undef DEBUG
+
+//#define DEBUG
+
+#ifdef DEBUG
+ #undef PRINT_DEBUG
+ #define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+ #define PRINT_DEBUG(fmt, args...) \
+ do {} while (0)
+#endif
+
+#define S5PC210_DRAM0_BASE_ADDR 0x40000000
+#define S5PC210_DRAM1_BASE_ADDR 0xa0000000
+#define S5PC210_DRAM_MAX_SIZE 0x60000000 /* 1.5 GB */
+
+#define S5PC210_IROM_BASE_ADDR 0x00000000
+#define S5PC210_IROM_SIZE 0x00010000 /* 64 KB */
+#define S5PC210_IROM_MIRROR_BASE_ADDR 0x02000000
+#define S5PC210_IROM_MIRROR_SIZE 0x00010000 /* 64 KB */
+
+#define S5PC210_IRAM_BASE_ADDR 0x02020000
+#define S5PC210_IRAM_SIZE 0x00020000 /* 128 KB */
+
+#define S5PC210_SFR_BASE_ADDR 0x10000000
+
+/* SFR Base Address for CMUs */
+#define S5PC210_CMU_BASE_ADDR 0x10030000
+
+/* UART's definitions */
+#define S5PC210_UART_BASE_ADDR 0x13800000
+#define S5PC210_UART_SHIFT 0x00010000
+
+#define S5PC210_UARTS_NUMBER 4
+
+#define S5PC210_UART_CHANNEL(addr) ((addr >> 16) & 0x7)
+#define S5PC210_UART0_FIFO_SIZE 256
+#define S5PC210_UART1_FIFO_SIZE 64
+#define S5PC210_UART2_FIFO_SIZE 16
+#define S5PC210_UART3_FIFO_SIZE 16
+#define S5PC210_UART4_FIFO_SIZE 64
+
+#define S5PC210_BASE_BOOT_ADDR S5PC210_DRAM0_BASE_ADDR
+
+static struct arm_boot_info s5pc210_binfo = {
+ .loader_start = S5PC210_BASE_BOOT_ADDR,
+};
+
+static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
+ 0x09, 0x00, 0x00, 0x00 };
+
+enum s5pc210_board_type {
+ BOARD_S5PC210_NURI,
+ BOARD_S5PC210_SMDKC210,
+};
+
+enum s5pc210_mach_id {
+ MACH_NURI_ID = 0xD33,
+ MACH_SMDKC210_ID = 0xB16,
+};
+
+
+static void s5pc210_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model,
+ enum s5pc210_board_type board_type)
+{
+ CPUState *env;
+ MemoryRegion *system_mem = get_system_memory();
+ MemoryRegion *chipid_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *iram_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *irom_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *irom_alias_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *dram0_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *dram1_mem = NULL;
+ qemu_irq *irqp;
+ qemu_irq cpu_irq[4];
+ ram_addr_t mem_size;
+ int n;
+
+ switch (board_type) {
+ case BOARD_S5PC210_NURI:
+ s5pc210_binfo.board_id = MACH_NURI_ID;
+ break;
+ case BOARD_S5PC210_SMDKC210:
+ s5pc210_binfo.board_id = MACH_SMDKC210_ID;
+ break;
+ default:
+ break;
+ }
+ if (!cpu_model) {
+ cpu_model = "cortex-a9";
+ }
+
+ for (n = 0; n < smp_cpus; n++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU %d definition\n", n);
+ exit(1);
+ }
+ /* Create PIC controller for each processor instance */
+ irqp = arm_pic_init_cpu(env);
+
+ /*
+ * Get GICs gpio_in cpu_irq to connect a combiner to them later.
+ * Use only IRQ for a while.
+ */
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ /*** Memory ***/
+
+ /* Chip-ID and OMR */
+ memory_region_init_ram_ptr(chipid_mem, NULL, "s5pc210.chipid",
+ sizeof(chipid_and_omr), chipid_and_omr);
+ memory_region_set_readonly(chipid_mem, true);
+ memory_region_add_subregion(system_mem, S5PC210_SFR_BASE_ADDR, chipid_mem);
+
+ /* Internal ROM */
+ memory_region_init_ram(irom_mem, NULL, "s5pc210.irom", S5PC210_IROM_SIZE);
+ memory_region_set_readonly(irom_mem, true);
+ memory_region_add_subregion(system_mem, S5PC210_IROM_BASE_ADDR, irom_mem);
+ /* mirror of 0x0 – 0x10000 */
+ memory_region_init_alias(irom_alias_mem, "s5pc210.irom_alias",
+ irom_mem, 0, S5PC210_IROM_SIZE);
+ memory_region_set_readonly(irom_alias_mem, true);
+ memory_region_add_subregion(system_mem, S5PC210_IROM_MIRROR_BASE_ADDR,
+ irom_alias_mem);
+
+ /* Internal RAM */
+ memory_region_init_ram(iram_mem, NULL, "s5pc210.iram", S5PC210_IRAM_SIZE);
+ memory_region_set_readonly(iram_mem, false);
+ memory_region_add_subregion(system_mem, S5PC210_IRAM_BASE_ADDR, iram_mem);
+
+ /* DRAM */
+ mem_size = ram_size;
+ if (mem_size > S5PC210_DRAM_MAX_SIZE) {
+ dram1_mem = g_new(MemoryRegion, 1);
+ memory_region_init_ram(dram1_mem, NULL, "s5pc210.dram1",
+ mem_size - S5PC210_DRAM_MAX_SIZE);
+ memory_region_add_subregion(system_mem, S5PC210_DRAM1_BASE_ADDR,
+ dram1_mem);
+ mem_size = S5PC210_DRAM_MAX_SIZE;
+ }
+ memory_region_init_ram(dram0_mem, NULL, "s5pc210.dram0", mem_size);
+ memory_region_add_subregion(system_mem, S5PC210_DRAM0_BASE_ADDR,
+ dram0_mem);
+
+ /* CMU */
+ sysbus_create_simple("s5pc210.cmu", S5PC210_CMU_BASE_ADDR, NULL);
+
+ /*** UARTs ***/
+ for (n = 0; n < S5PC210_UARTS_NUMBER; n++) {
+
+ uint32_t addr = S5PC210_UART_BASE_ADDR + S5PC210_UART_SHIFT * n;
+ int channel = S5PC210_UART_CHANNEL(addr);
+ qemu_irq uart_irq;
+ int fifo_size = 0;
+
+ switch (channel) {
+ case 0:
+ fifo_size = S5PC210_UART0_FIFO_SIZE;
+ break;
+ case 1:
+ fifo_size = S5PC210_UART1_FIFO_SIZE;
+ break;
+ case 2:
+ fifo_size = S5PC210_UART2_FIFO_SIZE;
+ break;
+ case 3:
+ fifo_size = S5PC210_UART3_FIFO_SIZE;
+ break;
+ case 4:
+ fifo_size = S5PC210_UART4_FIFO_SIZE;
+ break;
+ default:
+ fifo_size = 0;
+ PRINT_DEBUG("Wrong channel number: %d\n", channel);
+ break;
+ }
+
+ if (fifo_size == 0) {
+ PRINT_DEBUG("Can't create UART%d with fifo size %d\n",
+ channel, fifo_size);
+ continue;
+ }
+
+ uart_irq = NULL;
+
+ s5pc210_uart_create(addr, fifo_size, channel, NULL, uart_irq);
+ }
+
+ /*** Load kernel ***/
+
+ s5pc210_binfo.ram_size = ram_size;
+ s5pc210_binfo.nb_cpus = smp_cpus;
+ s5pc210_binfo.kernel_filename = kernel_filename;
+ s5pc210_binfo.initrd_filename = initrd_filename;
+ s5pc210_binfo.kernel_cmdline = kernel_cmdline;
+
+
+ arm_load_kernel(first_cpu, &s5pc210_binfo);
+}
+
+static void s5pc210_nuri_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ s5pc210_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_S5PC210_NURI);
+}
+
+static void s5pc210_smdkc210_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ s5pc210_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_S5PC210_SMDKC210);
+}
+
+
+static QEMUMachine s5pc210_nuri_machine = {
+ .name = "s5pc210-nuri",
+ .desc = "Samsung Exynos4210 NURI board",
+ .init = s5pc210_nuri_init,
+ .max_cpus = S5PC210_MAX_CPUS,
+};
+
+static QEMUMachine s5pc210_smdkc210_machine = {
+ .name = "s5pc210-smdkc210",
+ .desc = "Samsung Exynos4210 SMDKC210 board",
+ .init = s5pc210_smdkc210_init,
+ .max_cpus = S5PC210_MAX_CPUS,
+};
+
+static void s5pc210_machine_init(void)
+{
+ qemu_register_machine(&s5pc210_nuri_machine);
+ qemu_register_machine(&s5pc210_smdkc210_machine);
+}
+
+machine_init(s5pc210_machine_init);
new file mode 100644
@@ -0,0 +1,66 @@
+/*
+ * Samsung s5pc210 (aka Exynos4210) board emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@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, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+
+#ifndef S5PC210_H_
+#define S5PC210_H_
+
+#include "qemu-common.h"
+
+#define S5PC210_MAX_CPUS 2
+
+/*
+ * Interface for s5pc210 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
+} S5pc210CmuClock;
+
+uint64_t s5pc210_cmu_get_rate(S5pc210CmuClock clock);
+
+/*
+ * s5pc210 UART
+ */
+
+DeviceState *s5pc210_uart_create(target_phys_addr_t addr,
+ int fifo_size,
+ int channel,
+ CharDriverState *chr,
+ qemu_irq irq);
+
+#endif /* S5PC210_H_ */
new file mode 100644
@@ -0,0 +1,1144 @@
+/*
+ * s5pc210 Clock Management Units (CMUs) Emulation
+ *
+ * Copyright (C) 2011 Samsung Electronics Co Ltd.
+ * Maksim Kozlov, <m.kozlov@samsung.com>
+ *
+ * Created on: 07.2011
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "sysbus.h"
+
+#include "s5pc210.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 S5pc210CmuReg {
+ const char *name; /* for debugging */
+ uint32_t offset;
+ uint32_t reset_value;
+} S5pc210CmuReg;
+
+
+static S5pc210CmuReg s5pc210_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 S5PC210_CMU_REGS_MEM_SIZE (0x4000 * 5 + 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
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg[S5PC210_CMU_REGS_MEM_SIZE];
+
+} S5pc210CmuState;
+
+#define SOURCES_NUMBER 9
+#define RECIPIENTS_NUMBER 9
+
+typedef struct S5pc210CmuClockState {
+
+ const char *name;
+ uint64_t rate;
+
+ /* Current source clock */
+ struct S5pc210CmuClockState *source;
+ /*
+ * Available sources. Their order must correspond to CLK_SRC_ register
+ */
+ struct S5pc210CmuClockState *sources[SOURCES_NUMBER];
+ /* Who uses this clock? */
+ struct S5pc210CmuClockState *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;
+
+} S5pc210CmuClockState;
+
+
+/* Clocks from Clock Pads */
+
+/* It should be used only for testing purposes. XOM_0 is 0 */
+static S5pc210CmuClockState xxti = {
+ .name = "XXTI",
+ .rate = 24000000,
+};
+
+/* Main source. XOM_0 is 1 */
+static S5pc210CmuClockState xusbxti = {
+ .name = "XUSBXTI",
+ .rate = 24000000,
+};
+
+/* PLLs */
+
+static S5pc210CmuClockState mpll = {
+ .name = "MPLL",
+ .source = (XOM_0 ? &xusbxti : &xxti),
+};
+
+static S5pc210CmuClockState apll = {
+ .name = "APLL",
+ .source = (XOM_0 ? &xusbxti : &xxti),
+};
+
+
+/**/
+static S5pc210CmuClockState sclk_mpll = {
+ .name = "SCLK_MPLL",
+ .sources = {XOM_0 ? &xusbxti : &xxti, &mpll},
+ .src_reg = CLK_SRC_CPU,
+ .mux_shift = MUX_MPLL_SEL_SHIFT,
+};
+
+static S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClockState 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 S5pc210CmuClock enumerator
+ * which is defined in s5pc210.h file
+ *
+ */
+static S5pc210CmuClockState *s5pc210_clock[] = {
+ &xxti,
+ &xusbxti,
+ &apll,
+ &mpll,
+ &sclk_apll,
+ &sclk_mpll,
+ &aclk_100,
+ &sclk_uart0,
+ &sclk_uart1,
+ &sclk_uart2,
+ &sclk_uart3,
+ &sclk_uart4,
+ NULL
+};
+
+
+uint64_t s5pc210_cmu_get_rate(S5pc210CmuClock clock)
+{
+ return s5pc210_clock[clock]->rate;
+}
+
+#ifdef DEBUG_CMU
+/* The only meaning of life - debugging. This functions should be only used
+ * inside PRINT_DEBUG_... macroses
+ */
+static const char *s5pc210_cmu_regname(target_phys_addr_t offset)
+{
+
+ int regs_number = sizeof(s5pc210_cmu_regs)/sizeof(S5pc210CmuReg);
+ int i;
+
+ for (i = 0; i < regs_number; i++) {
+ if (offset == s5pc210_cmu_regs[i].offset) {
+ return s5pc210_cmu_regs[i].name;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+static void s5pc210_cmu_set_pll(void *opaque, target_phys_addr_t offset)
+{
+ S5pc210CmuState *s = (S5pc210CmuState *)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("s5pc210_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("s5pc210_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("s5pc210_cmu_set_pll: Bad offset 0x%x\n", (int)offset);
+ }
+
+ s->reg[I_(offset)] |= PLL_LOCKED_MASK;
+}
+
+
+static void s5pc210_cmu_set_rate(void *opaque, S5pc210CmuClockState *clock)
+{
+ S5pc210CmuState *s = (S5pc210CmuState *)opaque;
+ int i;
+
+ /* Rates of PLLs are calculated differently than ordinary clocks rates */
+ if (clock == &mpll) {
+ s5pc210_cmu_set_pll(s, MPLL_CON0);
+ } else if (clock == &apll) {
+ s5pc210_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,
+ s5pc210_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 {
+
+ S5pc210CmuClockState *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;
+ }
+
+ s5pc210_cmu_set_rate(s, recipient_clock);
+
+ i++;
+ } while (i < RECIPIENTS_NUMBER);
+}
+
+
+static uint64_t s5pc210_cmu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ S5pc210CmuState *s = (S5pc210CmuState *)opaque;
+
+ if (offset > (S5PC210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ hw_error("s5pc210_cmu_read: Bad offset 0x%x\n", (int)offset);
+ }
+
+ PRINT_DEBUG_EXTEND("<0x%05x> %s -> %08x\n",
+ offset, s5pc210_cmu_regname(offset), s->reg[I_(offset)]);
+
+ return s->reg[I_(offset)];
+}
+
+
+static void s5pc210_cmu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ S5pc210CmuState *s = (S5pc210CmuState *)opaque;
+ uint32_t pre_val;
+
+ if (offset > (S5PC210_CMU_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ hw_error("s5pc210_cmu_write: Bad offset 0x%x\n", (int)offset);
+ }
+
+ pre_val = s->reg[I_(offset)];
+ s->reg[I_(offset)] = val;
+
+ PRINT_DEBUG_EXTEND("<0x%05x> %s <- %08x\n",
+ offset, s5pc210_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;
+ s5pc210_cmu_set_rate(s, &apll);
+ break;
+ case MPLL_CON0:
+ val = (val & ~PLL_LOCKED_MASK) | (pre_val & PLL_LOCKED_MASK);
+ s->reg[I_(offset)] = val;
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
+ }
+ }
+
+ if (val & MUX_HPM_SEL) {
+ s5pc210_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)) {
+ s5pc210_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)) {
+ s5pc210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
+ }
+ }
+ }
+ break;
+ case CLK_DIV_CPU0:
+ s5pc210_cmu_set_rate(s, &sclk_apll);
+ s5pc210_cmu_set_rate(s, &sclk_mpll);
+ break;
+ case CLK_SRC_TOP0:
+ case CLK_DIV_TOP:
+ s5pc210_cmu_set_rate(s, &aclk_100);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static const MemoryRegionOps s5pc210_cmu_ops = {
+ .read = s5pc210_cmu_read,
+ .write = s5pc210_cmu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static void s5pc210_cmu_reset(void *opaque)
+{
+ S5pc210CmuState *s = (S5pc210CmuState *)opaque;
+ int i = 0, j = 0, n = 0;
+ int regs_number = sizeof(s5pc210_cmu_regs)/sizeof(S5pc210CmuReg);
+ uint32_t index = 0;
+
+ /* Set default values for registers */
+ for (i = 0; i < regs_number; i++) {
+ index = (s5pc210_cmu_regs[i].offset) / sizeof(uint32_t);
+ s->reg[index] = s5pc210_cmu_regs[i].reset_value;
+ }
+
+ /* clear recipients array from previous reset */
+ for (i = 0; i < CLOCKS_NUMBER; i++) {
+ bzero(s5pc210_clock[i]->recipients,
+ RECIPIENTS_NUMBER * sizeof(S5pc210CmuClockState *));
+ }
+
+ /*
+ * 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 s5pc210_clock */
+
+ PRINT_DEBUG("[SOURCES] %s: ", s5pc210_clock[i]->name);
+
+ j = 0;
+ do { /* visit all sources for current clock (s5pc210_clock[i]) */
+
+ if ((s5pc210_clock[i]->sources[j] == NULL)) {
+
+ if (j == 0) { /* check if we have empty '.sources[]' array */
+ if (s5pc210_clock[i]->source != NULL) {
+ s5pc210_clock[i]->sources[j] = s5pc210_clock[i]->source;
+ } else {
+ /*
+ * We haven't any defined sources for this clock. Error
+ * during definition of appropriate clock structure
+ *
+ */
+ if ((s5pc210_clock[i] != &xusbxti) &&
+ (s5pc210_clock[i] != &xxti)) {
+
+ hw_error("s5pc210_cmu_reset:"
+ "There aren't any sources for %s clock!\n",
+ s5pc210_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", s5pc210_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 ((s5pc210_clock[i]->sources[j]->recipients[n]) == NULL) {
+ s5pc210_clock[i]->sources[j]->recipients[n] =
+ s5pc210_clock[i];
+ break;
+ }
+ n++;
+ } while (n < RECIPIENTS_NUMBER);
+
+ 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: ", s5pc210_clock[i]->name);
+ for (j = 0;
+ (j < RECIPIENTS_NUMBER) &&
+ ((s5pc210_clock[i]->recipients[j]) != NULL);
+ j++) {
+ PRINT_DEBUG_SIMPLE("%s ", s5pc210_clock[i]->recipients[j]->name);
+ }
+ PRINT_DEBUG_SIMPLE("\n");
+ }
+#endif
+
+ s5pc210_cmu_set_rate(s, XOM_0 ? &xusbxti : &xxti);
+}
+
+static const VMStateDescription vmstate_s5pc210_cmu = {
+ .name = "s5pc210.cmu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ /*
+ * TODO: Maybe we should save S5pc210CmuClockState structs as well
+ */
+ VMSTATE_UINT32_ARRAY(reg, S5pc210CmuState,
+ S5PC210_CMU_REGS_MEM_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int s5pc210_cmu_init(SysBusDevice *dev)
+{
+ S5pc210CmuState *s = FROM_SYSBUS(S5pc210CmuState, dev);
+
+ /* memory mapping */
+ memory_region_init_io(&s->iomem, &s5pc210_cmu_ops, s, "s5pc210.cmu",
+ S5PC210_CMU_REGS_MEM_SIZE);
+ sysbus_init_mmio_region(dev, &s->iomem);
+
+ qemu_register_reset(s5pc210_cmu_reset, s);
+
+ vmstate_register(&dev->qdev, -1, &vmstate_s5pc210_cmu, s);
+
+ s5pc210_cmu_reset(s);
+
+ return 0;
+}
+
+
+static void s5pc210_cmu_register(void)
+{
+ sysbus_register_dev("s5pc210.cmu",
+ sizeof(S5pc210CmuState),
+ s5pc210_cmu_init);
+}
+
+
+device_init(s5pc210_cmu_register)
new file mode 100644
@@ -0,0 +1,677 @@
+/*
+ * s5pc210 UART Emulation
+ *
+ * Copyright (C) 2011 Samsung Electronics Co Ltd.
+ * Maksim Kozlov, <m.kozlov@samsung.com>
+ *
+ * Created on: 07.2011
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+
+#include "s5pc210.h"
+
+#undef DEBUG_UART
+#undef DEBUG_UART_EXTEND
+#undef DEBUG_IRQ
+#undef DEBUG_Rx_DATA
+#undef DEBUG_Tx_DATA
+
+
+//#define DEBUG_UART
+//#define DEBUG_UART_EXTEND
+//#define DEBUG_IRQ
+//#define DEBUG_Rx_DATA
+//#define DEBUG_Tx_DATA
+
+
+#define PRINT_DEBUG(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_UART
+
+#undef PRINT_DEBUG
+#define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+#ifdef DEBUG_UART_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 UART registers relative to SFR base address
+ * for UARTn
+ *
+ */
+#define ULCON 0x0000 /* Line Control */
+#define UCON 0x0004 /* Control */
+#define UFCON 0x0008 /* FIFO Control */
+#define UMCON 0x000C /* Modem Control */
+#define UTRSTAT 0x0010 /* Tx/Rx Status */
+#define UERSTAT 0x0014 /* Rx Error Status */
+#define UFSTAT 0x0018 /* FIFO Status */
+#define UMSTAT 0x001C /* Modem Status */
+#define UTXH 0x0020 /* Transmit Buffer */
+#define URXH 0x0024 /* Receive Buffer */
+#define UBRDIV 0x0028 /* Baud Rate Divisor */
+#define UFRACVAL 0x002C /* Divisor Fractional Value */
+#define UINTP 0x0030 /* Interrupt Pending */
+#define UINTSP 0x0034 /* Interrupt Source Pending */
+#define UINTM 0x0038 /* Interrupt Mask */
+
+/*
+ * for indexing register in the uint32_t array
+ *
+ * 'reg' - register offset (see offsets definitions above)
+ *
+ */
+#define I_(reg) (reg / sizeof(uint32_t))
+
+typedef struct S5pc210UartReg {
+ const char *name; /* the only reason is the debug output */
+ target_phys_addr_t offset;
+ uint32_t reset_value;
+} S5pc210UartReg;
+
+static S5pc210UartReg s5pc210_uart_regs[] = {
+ {"ULCON" , ULCON , 0x00000000},
+ {"UCON" , UCON , 0x00003000},
+ {"UFCON" , UFCON , 0x00000000},
+ {"UMCON" , UMCON , 0x00000000},
+ {"UTRSTAT" , UTRSTAT , 0x00000006}, /* RO */
+ {"UERSTAT" , UERSTAT , 0x00000000}, /* RO */
+ {"UFSTAT" , UFSTAT , 0x00000000}, /* RO */
+ {"UMSTAT" , UMSTAT , 0x00000000}, /* RO */
+ {"UTXH" , UTXH , 0x5c5c5c5c}, /* WO, undefined reset value*/
+ {"URXH" , URXH , 0x00000000}, /* RO */
+ {"UBRDIV" , UBRDIV , 0x00000000},
+ {"UFRACVAL", UFRACVAL, 0x00000000},
+ {"UINTP" , UINTP , 0x00000000},
+ {"UINTSP" , UINTSP , 0x00000000},
+ {"UINTM" , UINTM , 0x00000000},
+};
+
+#define S5PC210_UART_REGS_MEM_SIZE 0x3c
+
+/* UART FIFO Control */
+#define UFCON_FIFO_ENABLE 0x1
+#define UFCON_Rx_FIFO_RESET 0x2
+#define UFCON_Tx_FIFO_RESET 0x4
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL \
+ (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT)
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL \
+ (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT)
+
+/* Uart FIFO Status */
+#define UFSTAT_Rx_FIFO_COUNT 0xff
+#define UFSTAT_Rx_FIFO_FULL 0x100
+#define UFSTAT_Rx_FIFO_ERROR 0x200
+#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16
+#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT)
+#define UFSTAT_Tx_FIFO_FULL_SHIFT 24
+#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT)
+
+/* UART Interrupt Source Pending */
+#define UINTSP_RXD 0x1 /* Receive interrupt */
+#define UINTSP_ERROR 0x2 /* Error interrupt */
+#define UINTSP_TXD 0x4 /* Transmit interrupt */
+#define UINTSP_MODEM 0x8 /* Modem interrupt */
+
+/* UART Line Control */
+#define ULCON_IR_MODE_SHIFT 6
+#define ULCON_PARITY_SHIFT 3
+#define ULCON_STOP_BIT_SHIFT 1
+
+
+
+/* Specifies Tx/Rx Status */
+#define UTRSTAT_TRANSMITTER_EMPTY 0x4
+#define UTRSTAT_Tx_BUFFER_EMPTY 0x2
+#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1
+
+typedef struct {
+ uint8_t *data;
+ uint32_t sp, rp; /* store and retrieve pointers */
+ uint32_t size;
+} S5pc210UartFIFO;
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg[S5PC210_UART_REGS_MEM_SIZE];
+ S5pc210UartFIFO rx;
+ S5pc210UartFIFO tx;
+
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint32_t channel;
+
+} S5pc210UartState;
+
+
+#ifdef DEBUG_UART
+/* The only meaning of life - debugging. This functions should be only used
+ * inside PRINT_DEBUG_... macroses
+ */
+static const char *s5pc210_uart_regname(target_phys_addr_t offset)
+{
+
+ int regs_number = sizeof(s5pc210_uart_regs)/sizeof(S5pc210UartReg);
+ int i;
+
+ for (i = 0; i < regs_number; i++) {
+ if (offset == s5pc210_uart_regs[i].offset) {
+ return s5pc210_uart_regs[i].name;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+
+static void fifo_store(S5pc210UartFIFO *q, uint8_t ch)
+{
+ q->data[q->sp] = ch;
+ q->sp = (q->sp + 1) % q->size;
+}
+
+static uint8_t fifo_retrieve(S5pc210UartFIFO *q)
+{
+ uint8_t ret = q->data[q->rp];
+ q->rp = (q->rp + 1) % q->size;
+ return ret;
+}
+
+static int fifo_elements_number(S5pc210UartFIFO *q)
+{
+ if (q->sp < q->rp) {
+ return q->size - q->rp + q->sp;
+ }
+
+ return q->sp - q->rp;
+}
+
+static int fifo_empty_elements_number(S5pc210UartFIFO *q)
+{
+ return q->size - fifo_elements_number(q);
+}
+
+static void fifo_reset(S5pc210UartFIFO *q)
+{
+ if (q->data != NULL) {
+ g_free(q->data);
+ q->data = NULL;
+ }
+
+ q->data = (uint8_t *)g_malloc0(q->size);
+
+ q->sp = 0;
+ q->rp = 0;
+}
+
+static uint32_t s5pc210_uart_Tx_FIFO_trigger_level(S5pc210UartState *s)
+{
+ uint32_t level = 0;
+ uint32_t reg;
+
+ reg = (s->reg[I_(UFCON)] && UFCON_Tx_FIFO_TRIGGER_LEVEL) >>
+ UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT;
+
+ switch (s->channel) {
+ case 0:
+ level = reg * 32;
+ break;
+ case 1:
+ case 4:
+ level = reg * 8;
+ break;
+ case 2:
+ case 3:
+ level = reg * 2;
+ break;
+ default:
+ level = 0;
+ PRINT_ERROR("Wrong UART chennel number: %d\n", s->channel);
+ }
+
+ return level;
+}
+
+static void s5pc210_uart_update_irq(S5pc210UartState *s)
+{
+ /*
+ * The Tx interrupt is always requested if the number of data in the
+ * transmit FIFO is smaller than the trigger level.
+ */
+ if (s->reg[I_(UFCON)] && UFCON_FIFO_ENABLE) {
+
+ uint32_t count = (s->reg[I_(UFSTAT)] && UFSTAT_Tx_FIFO_COUNT) >>
+ UFSTAT_Tx_FIFO_COUNT_SHIFT;
+
+ if (count <= s5pc210_uart_Tx_FIFO_trigger_level(s)) {
+ s->reg[I_(UINTSP)] |= UINTSP_TXD;
+ }
+ }
+
+ s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)];
+
+ if (s->reg[I_(UINTP)]) {
+ qemu_irq_raise(s->irq);
+
+#ifdef DEBUG_IRQ
+ fprintf(stderr, "UART%d: IRQ have been raised: %08x\n",
+ s->channel, s->reg[I_(UINTP)]);
+#endif
+
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void s5pc210_uart_update_parameters(S5pc210UartState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+ uint64_t uclk_rate;
+
+ if (s->reg[I_(UBRDIV)] == 0) {
+ return;
+ }
+
+ frame_size = 1; /* start bit */
+ if (s->reg[I_(ULCON)] & 0x20) {
+ frame_size++; /* parity bit */
+ if (s->reg[I_(ULCON)] & 0x28) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+
+ if (s->reg[I_(ULCON)] & 0x4) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
+
+ frame_size += data_bits + stop_bits;
+
+ switch (s->channel) {
+ case 0:
+ uclk_rate = s5pc210_cmu_get_rate(SCLK_UART0); break;
+ case 1:
+ uclk_rate = s5pc210_cmu_get_rate(SCLK_UART1); break;
+ case 2:
+ uclk_rate = s5pc210_cmu_get_rate(SCLK_UART2); break;
+ case 3:
+ uclk_rate = s5pc210_cmu_get_rate(SCLK_UART3); break;
+ case 4:
+ uclk_rate = s5pc210_cmu_get_rate(SCLK_UART4); break;
+ default:
+ hw_error("%s: Incorrect UART channel: %d\n",
+ __func__, s->channel);
+ }
+
+ speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
+ (s->reg[I_(UFRACVAL)] & 0x7) + 16);
+
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+ PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
+ s->channel, speed, parity, data_bits, stop_bits);
+}
+
+static void s5pc210_uart_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ S5pc210UartState *s = (S5pc210UartState *)opaque;
+ uint8_t ch;
+
+ if (offset > (S5PC210_UART_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ hw_error("s5pc210_cmu_write: Bad offset 0x%x\n", (int)offset);
+ }
+
+ PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n",
+ s->channel, offset, s5pc210_uart_regname(offset), val);
+
+ switch (offset) {
+ case ULCON:
+ case UBRDIV:
+ case UFRACVAL:
+ s->reg[I_(offset)] = val;
+ s5pc210_uart_update_parameters(s);
+ break;
+ case UFCON:
+ s->reg[I_(UFCON)] = val;
+ if (val & UFCON_Rx_FIFO_RESET) {
+ fifo_reset(&s->rx);
+ s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET;
+ PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel);
+ }
+ if (val & UFCON_Tx_FIFO_RESET) {
+ fifo_reset(&s->tx);
+ s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET;
+ PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel);
+ }
+ break;
+
+ case UTXH:
+ if (s->chr) {
+ s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
+ UTRSTAT_Tx_BUFFER_EMPTY);
+ ch = (uint8_t)val;
+ qemu_chr_fe_write(s->chr, &ch, 1);
+#ifdef DEBUG_Tx_DATA
+ fprintf(stderr, "%c", ch);
+#endif
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY |
+ UTRSTAT_Tx_BUFFER_EMPTY;
+ s->reg[I_(UINTSP)] |= UINTSP_TXD;
+ s5pc210_uart_update_irq(s);
+ }
+ break;
+
+ case UINTP:
+ s->reg[I_(UINTP)] &= ~val;
+ /* XXX: It's the assumption that it works in this way */
+ s->reg[I_(UINTSP)] &= ~val;
+ PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
+ s->channel, offset, s->reg[I_(UINTP)]);
+ s5pc210_uart_update_irq(s);
+ break;
+ case UTRSTAT:
+ case UERSTAT:
+ case UFSTAT:
+ case UMSTAT:
+ case URXH:
+ PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n",
+ s->channel, s5pc210_uart_regname(offset), offset);
+ break;
+ case UINTSP:
+ /* XXX: It's the assumption that it works in this way */
+ s->reg[I_(UINTSP)] &= ~val;
+ break;
+ case UINTM:
+ s->reg[I_(UINTM)] = val;
+ s5pc210_uart_update_irq(s);
+ break;
+ case UCON:
+ case UMCON:
+ default:
+ s->reg[I_(offset)] = val;
+ break;
+ }
+}
+static uint64_t s5pc210_uart_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ S5pc210UartState *s = (S5pc210UartState *)opaque;
+ uint32_t res;
+
+ if (offset > (S5PC210_UART_REGS_MEM_SIZE - sizeof(uint32_t))) {
+ hw_error("s5pc210_cmu_read: Bad offset 0x%x\n", (int)offset);
+ }
+
+ switch (offset) {
+ case UERSTAT: /* Read Only */
+ res = s->reg[I_(UERSTAT)];
+ s->reg[I_(UERSTAT)] = 0;
+ return res;
+ case UFSTAT: /* Read Only */
+ s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
+ if (fifo_empty_elements_number(&s->rx) == 0) {
+ s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL;
+ s->reg[I_(UFSTAT)] &= ~0xff;
+ }
+ return s->reg[I_(UFSTAT)];
+ case URXH:
+ if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+ if (fifo_elements_number(&s->rx)) {
+ res = fifo_retrieve(&s->rx);
+#ifdef DEBUG_Rx_DATA
+ fprintf(stderr, "%c", res);
+#endif
+ if (!fifo_elements_number(&s->rx)) {
+ s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+ } else {
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+ } else {
+ s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+ s5pc210_uart_update_irq(s);
+ res = 0;
+ }
+ } else {
+ s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+ res = s->reg[I_(URXH)];
+ }
+ return res;
+ case UTXH:
+ PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n",
+ s->channel, s5pc210_uart_regname(offset), offset);
+ break;
+ default:
+ return s->reg[I_(offset)];
+ break;
+ }
+
+ return 0;
+}
+
+
+static const MemoryRegionOps s5pc210_uart_ops = {
+ .read = s5pc210_uart_read,
+ .write = s5pc210_uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int s5pc210_uart_can_receive(void *opaque)
+{
+ S5pc210UartState *s = (S5pc210UartState *)opaque;
+
+ return fifo_empty_elements_number(&s->rx);
+}
+
+
+static void s5pc210_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ S5pc210UartState *s = (S5pc210UartState *)opaque;
+ int i;
+
+ if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+ if (fifo_empty_elements_number(&s->rx) < size) {
+ for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) {
+ fifo_store(&s->rx, buf[i]);
+ }
+ s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ } else {
+ for (i = 0; i < size; i++) {
+ fifo_store(&s->rx, buf[i]);
+ }
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+ /* XXX: after achieve trigger level*/
+ s->reg[I_(UINTSP)] |= UINTSP_RXD;
+ } else {
+ s->reg[I_(URXH)] = buf[0];
+ s->reg[I_(UINTSP)] |= UINTSP_RXD;
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+
+ s5pc210_uart_update_irq(s);
+}
+
+
+static void s5pc210_uart_event(void *opaque, int event)
+{
+ /*
+ * TODO: Implement this.
+ *
+ */
+}
+
+
+static void s5pc210_uart_reset(void *opaque)
+{
+ S5pc210UartState *s = (S5pc210UartState *)opaque;
+ int regs_number = sizeof(s5pc210_uart_regs)/sizeof(S5pc210UartReg);
+ int i;
+
+ for (i = 0; i < regs_number; i++) {
+ s->reg[I_(s5pc210_uart_regs[i].offset)] =
+ s5pc210_uart_regs[i].reset_value;
+ }
+
+ fifo_reset(&s->rx);
+ fifo_reset(&s->tx);
+
+ PRINT_DEBUG_EXTEND("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
+}
+
+
+static const VMStateDescription vmstate_s5pc210_uart = {
+ .name = "s5pc210.uart",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ /*
+ * TODO: We should save fifo too
+ */
+ VMSTATE_UINT32_ARRAY(reg, S5pc210UartState,
+ S5PC210_UART_REGS_MEM_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+DeviceState *s5pc210_uart_create(target_phys_addr_t addr,
+ int fifo_size,
+ int channel,
+ CharDriverState *chr,
+ qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *bus;
+ S5pc210UartState *state;
+
+ dev = qdev_create(NULL, "s5pc210.uart");
+
+ if (!chr) {
+ if (channel >= MAX_SERIAL_PORTS) {
+ hw_error("Only %d serial ports are supported by QEMU.\n",
+ MAX_SERIAL_PORTS);
+ }
+ chr = serial_hds[channel];
+ if (!chr) {
+ chr = qemu_chr_new("s5pc210.uart", "null", NULL);
+ if (!(chr)) {
+ hw_error("Can't assign serial port to UART%d.\n", channel);
+ }
+ }
+ }
+
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_prop_set_uint32(dev, "channel", channel);
+
+ bus = sysbus_from_qdev(dev);
+ qdev_init_nofail(dev);
+ if (addr != (target_phys_addr_t)-1) {
+ sysbus_mmio_map(bus, 0, addr);
+ }
+ sysbus_connect_irq(bus, 0, irq);
+
+ state = FROM_SYSBUS(S5pc210UartState, bus);
+
+ state->rx.size = fifo_size;
+ state->tx.size = fifo_size;
+
+ return dev;
+}
+
+static int s5pc210_uart_init(SysBusDevice *dev)
+{
+ S5pc210UartState *s = FROM_SYSBUS(S5pc210UartState, dev);
+
+ /* memory mapping */
+ memory_region_init_io(&s->iomem, &s5pc210_uart_ops, s, "s5pc210.uart",
+ S5PC210_UART_REGS_MEM_SIZE);
+ sysbus_init_mmio_region(dev, &s->iomem);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ qemu_chr_add_handlers(s->chr, s5pc210_uart_can_receive,
+ s5pc210_uart_receive, s5pc210_uart_event, s);
+
+ vmstate_register(&dev->qdev, -1, &vmstate_s5pc210_uart, s);
+
+ qemu_register_reset(s5pc210_uart_reset, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc210_uart_info = {
+ .init = s5pc210_uart_init,
+ .qdev.name = "s5pc210.uart",
+ .qdev.size = sizeof(S5pc210UartState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", S5pc210UartState, chr),
+ DEFINE_PROP_UINT32("channel", S5pc210UartState, channel, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc210_uart_register(void)
+{
+ sysbus_register_withprop(&s5pc210_uart_info);
+}
+
+device_init(s5pc210_uart_register)