[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 01/14] ARM: s5pc210: Basic support of s5pc210 board
From: |
Evgeny Voevodin |
Subject: |
[Qemu-devel] [PATCH 01/14] ARM: s5pc210: Basic support of s5pc210 boards |
Date: |
Wed, 07 Dec 2011 13:46:52 +0400 |
From: Maksim Kozlov <address@hidden>
Added support of S5PC210 based boards - SMDKC210 and NURI.
CMU and UART basic implementation.
Signed-off-by: Evgeny Voevodin <address@hidden>
---
Makefile.target | 1 +
hw/s5pc210.c | 284 +++++++++++++
hw/s5pc210.h | 66 +++
hw/s5pc210_cmu.c | 1144 +++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/s5pc210_uart.c | 677 +++++++++++++++++++++++++++++++
5 files changed, 2172 insertions(+), 0 deletions(-)
create mode 100644 hw/s5pc210.c
create mode 100644 hw/s5pc210.h
create mode 100644 hw/s5pc210_cmu.c
create mode 100644 hw/s5pc210_uart.c
diff --git a/Makefile.target b/Makefile.target
index a111521..38fc364 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -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
diff --git a/hw/s5pc210.c b/hw/s5pc210.c
new file mode 100644
index 0000000..d20ac95
--- /dev/null
+++ b/hw/s5pc210.c
@@ -0,0 +1,284 @@
+/*
+ * Samsung s5pc210 (aka Exynos4210) board emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <address@hidden>
+ * Evgeny Voevodin <address@hidden>
+ * Igor Mitsyanko <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 "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);
diff --git a/hw/s5pc210.h b/hw/s5pc210.h
new file mode 100644
index 0000000..bbf927c
--- /dev/null
+++ b/hw/s5pc210.h
@@ -0,0 +1,66 @@
+/*
+ * Samsung s5pc210 (aka Exynos4210) board emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <address@hidden>
+ * Evgeny Voevodin <address@hidden>
+ * Igor Mitsyanko <address@hidden>
+ *
+ *
+ * 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_ */
diff --git a/hw/s5pc210_cmu.c b/hw/s5pc210_cmu.c
new file mode 100644
index 0000000..12fba47
--- /dev/null
+++ b/hw/s5pc210_cmu.c
@@ -0,0 +1,1144 @@
+/*
+ * s5pc210 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 "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)
diff --git a/hw/s5pc210_uart.c b/hw/s5pc210_uart.c
new file mode 100644
index 0000000..d1fbddc
--- /dev/null
+++ b/hw/s5pc210_uart.c
@@ -0,0 +1,677 @@
+/*
+ * s5pc210 UART 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 "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)
--
1.7.4.1
- [Qemu-devel] [PATCH 00/14] ARM: Samsung S5PC210-based boards support., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 02/14] hw/sysbus.h: Increase maximum number of device IRQs., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 05/14] hw/arm_boot.c: Add new secondary CPU bootloader., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 04/14] ARM: s5pc210: PWM support., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 01/14] ARM: s5pc210: Basic support of s5pc210 boards,
Evgeny Voevodin <=
- [Qemu-devel] [PATCH 03/14] ARM: s5pc210: IRQ subsystem support., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 09/14] hw/lan9118.c: Basic byte/word/long access support., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 08/14] ARM: s5pc210: Boot secondary CPU., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 12/14] SD card: add query function to check wether SD card currently ready to recieve data Before executing data transfer to card, we must check that previously issued command wasn't a simple query command (for ex. CMD13), which doesn't require data transfer. Currently, we only can aquire information about whether SD card is in sending data state or not. This patch allows us to query wether previous command was data write command and it was successfully accepted by card (meaning that SD card in recieving data state)., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 06/14] hw/arm_gic.c: lower IRQ only on changing of enable bit., Evgeny Voevodin, 2011/12/07
- [Qemu-devel] [PATCH 14/14] s5pc210: Switch to sysbus_init_mmio., Evgeny Voevodin, 2011/12/07