Merge remote-tracking branch 'pmaydell/arm-devs.for-upstream' into staging

* pmaydell/arm-devs.for-upstream: (22 commits)
  hw/pl031: Actually raise interrupt on timer expiry
  MAINTAINERS: Add hw/highbank.c maintainer
  Remove unnecessary includes of primecell.h
  hw/primecell.h: Remove obsolete pl080_init() declaration
  hw/arm_sysctl: Drop legacy init function
  hw/vexpress.c: Add vexpress-a15 machine
  arm_boot: Pass base address of GIC CPU interface, not whole GIC
  hw/vexpress.c: Instantiate the motherboard CLCD
  hw/vexpress.c: Factor out daughterboard-specific initialization
  hw/vexpress.c: Move secondary CPU boot code to SRAM
  hw/vexpress.c: Make motherboard peripheral memory map table-driven
  hw/a15mpcore.c: Add Cortex-A15 private peripheral model
  MAINTAINERS: Add maintainers for Exynos SOC.
  Exynos4210: added display controller implementation
  hw/exynos4210.c: Add LAN support for SMDKC210.
  hw/lan9118: Add basic 16-bit mode support.
  ARM: exynos4210: MCT support.
  ARM: exynos4210: basic Power Management Unit implementation
  ARM: exynos4210: PWM support.
  ARM: exynos4210: UART support
  ...
This commit is contained in:
Anthony Liguori 2012-02-17 06:50:07 -06:00
commit 3d7f572140
25 changed files with 7107 additions and 129 deletions

View file

@ -183,6 +183,20 @@ F: *win32*
ARM Machines
------------
Exynos
M: Evgeny Voevodin <e.voevodin@samsung.com>
M: Maksim Kozlov <m.kozlov@samsung.com>
M: Igor Mitsyanko <i.mitsyanko@samsung.com>
M: Dmitry Solodkiy <d.solodkiy@samsung.com>
S: Maintained
F: hw/exynos*
Calxeda Highbank
M: Mark Langsdorf <mark.langsdorf@calxeda.com>
S: Supported
F: hw/highbank.c
F: hw/xgmac.c
Gumstix
M: qemu-devel@nongnu.org
S: Orphan

View file

@ -344,8 +344,11 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y += versatile_pci.o
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
obj-arm-y += arm_l2x0.o
obj-arm-y += arm_mptimer.o
obj-arm-y += arm_mptimer.o a15mpcore.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += highbank.o
obj-arm-y += pl061.o

103
hw/a15mpcore.c Normal file
View file

@ -0,0 +1,103 @@
/*
* Cortex-A15MPCore internal peripheral emulation.
*
* Copyright (c) 2012 Linaro Limited.
* Written by Peter Maydell.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "sysbus.h"
/* Configuration for arm_gic.c:
* max number of CPUs, how to ID current CPU
*/
#define NCPU 4
static inline int gic_get_current_cpu(void)
{
return cpu_single_env->cpu_index;
}
#include "arm_gic.c"
/* A15MP private memory region. */
typedef struct A15MPPrivState {
gic_state gic;
uint32_t num_cpu;
uint32_t num_irq;
MemoryRegion container;
} A15MPPrivState;
static int a15mp_priv_init(SysBusDevice *dev)
{
A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev);
if (s->num_cpu > NCPU) {
hw_error("a15mp_priv_init: num-cpu may not be more than %d\n", NCPU);
}
gic_init(&s->gic, s->num_cpu, s->num_irq);
/* Memory map (addresses are offsets from PERIPHBASE):
* 0x0000-0x0fff -- reserved
* 0x1000-0x1fff -- GIC Distributor
* 0x2000-0x2fff -- GIC CPU interface
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
*/
memory_region_init(&s->container, "a15mp-priv-container", 0x8000);
memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
memory_region_add_subregion(&s->container, 0x2000, &s->gic.cpuiomem[0]);
sysbus_init_mmio(dev, &s->container);
return 0;
}
static Property a15mp_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1),
/* The Cortex-A15MP may have anything from 0 to 224 external interrupt
* IRQ lines (with another 32 internal). We default to 64+32, which
* is the number provided by the Cortex-A15MP test chip in the
* Versatile Express A15 development board.
* Other boards may differ and should set this property appropriately.
*/
DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 96),
DEFINE_PROP_END_OF_LIST(),
};
static void a15mp_priv_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = a15mp_priv_init;
dc->props = a15mp_priv_properties;
/* We currently have no savable state outside the common GIC state */
}
static TypeInfo a15mp_priv_info = {
.name = "a15mpcore_priv",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(A15MPPrivState),
.class_init = a15mp_priv_class_init,
};
static void a15mp_register_types(void)
{
type_register_static(&a15mp_priv_info);
}
type_init(a15mp_register_types)

View file

@ -37,7 +37,7 @@ struct arm_boot_info {
*/
target_phys_addr_t smp_loader_start;
target_phys_addr_t smp_bootreg_addr;
target_phys_addr_t smp_priv_base;
target_phys_addr_t gic_cpu_if_addr;
int nb_cpus;
int board_id;
int (*atag_board)(const struct arm_boot_info *info, void *p);

View file

@ -43,16 +43,16 @@ static uint32_t bootloader[] = {
* location for the kernel secondary CPU entry point.
*/
static uint32_t smpboot[] = {
0xe59f201c, /* ldr r2, privbase */
0xe59f201c, /* ldr r2, gic_cpu_if */
0xe59f001c, /* ldr r0, startaddr */
0xe3a01001, /* mov r1, #1 */
0xe5821100, /* str r1, [r2, #256] */
0xe5821000, /* str r1, [r2] */
0xe320f003, /* wfi */
0xe5901000, /* ldr r1, [r0] */
0xe1110001, /* tst r1, r1 */
0x0afffffb, /* beq <wfi> */
0xe12fff11, /* bx r1 */
0, /* privbase: Private memory region base address. */
0, /* gic_cpu_if: base address of GIC CPU interface */
0 /* bootreg: Boot register address is held here */
};
@ -61,7 +61,7 @@ static void default_write_secondary(CPUState *env,
{
int n;
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
}

View file

@ -378,7 +378,7 @@ static void arm_sysctl_gpio_set(void *opaque, int line, int level)
}
}
static int arm_sysctl_init1(SysBusDevice *dev)
static int arm_sysctl_init(SysBusDevice *dev)
{
arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev);
@ -389,18 +389,6 @@ static int arm_sysctl_init1(SysBusDevice *dev)
return 0;
}
/* Legacy helper function. */
void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id)
{
DeviceState *dev;
dev = qdev_create(NULL, "realview_sysctl");
qdev_prop_set_uint32(dev, "sys_id", sys_id);
qdev_init_nofail(dev);
qdev_prop_set_uint32(dev, "proc_id", proc_id);
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
}
static Property arm_sysctl_properties[] = {
DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0),
DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0),
@ -412,7 +400,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = arm_sysctl_init1;
k->init = arm_sysctl_init;
dc->reset = arm_sysctl_reset;
dc->vmsd = &vmstate_arm_sysctl;
dc->props = arm_sysctl_properties;

270
hw/exynos4210.c Normal file
View file

@ -0,0 +1,270 @@
/*
* Samsung exynos4210 SoC 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "boards.h"
#include "sysemu.h"
#include "sysbus.h"
#include "arm-misc.h"
#include "exynos4210.h"
#define EXYNOS4210_CHIPID_ADDR 0x10000000
/* PWM */
#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000
/* MCT */
#define EXYNOS4210_MCT_BASE_ADDR 0x10050000
/* UART's definitions */
#define EXYNOS4210_UART0_BASE_ADDR 0x13800000
#define EXYNOS4210_UART1_BASE_ADDR 0x13810000
#define EXYNOS4210_UART2_BASE_ADDR 0x13820000
#define EXYNOS4210_UART3_BASE_ADDR 0x13830000
#define EXYNOS4210_UART0_FIFO_SIZE 256
#define EXYNOS4210_UART1_FIFO_SIZE 64
#define EXYNOS4210_UART2_FIFO_SIZE 16
#define EXYNOS4210_UART3_FIFO_SIZE 16
/* Interrupt Group of External Interrupt Combiner for UART */
#define EXYNOS4210_UART_INT_GRP 26
/* External GIC */
#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR 0x10480000
#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR 0x10490000
/* Combiner */
#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000
#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000
/* PMU SFR base address */
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
/* Display controllers (FIMD) */
#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
0x09, 0x00, 0x00, 0x00 };
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
unsigned long ram_size)
{
qemu_irq cpu_irq[4];
int n;
Exynos4210State *s = g_new(Exynos4210State, 1);
qemu_irq *irqp;
qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
unsigned long mem_size;
DeviceState *dev;
SysBusDevice *busdev;
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
s->env[n] = cpu_init("cortex-a9");
if (!s->env[n]) {
fprintf(stderr, "Unable to find CPU %d definition\n", n);
exit(1);
}
/* Create PIC controller for each processor instance */
irqp = arm_pic_init_cpu(s->env[n]);
/*
* 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];
}
/*** IRQs ***/
s->irq_table = exynos4210_init_irq(&s->irqs);
/* IRQ Gate */
dev = qdev_create(NULL, "exynos4210.irq_gate");
qdev_init_nofail(dev);
/* Get IRQ Gate input in gate_irq */
for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) {
gate_irq[n] = qdev_get_gpio_in(dev, n);
}
busdev = sysbus_from_qdev(dev);
/* Connect IRQ Gate output to cpu_irq */
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
/* Private memory region and Internal GIC */
dev = qdev_create(NULL, "a9mpcore_priv");
qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
sysbus_connect_irq(busdev, n, gate_irq[n * 2]);
}
for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n);
}
/* Cache controller */
sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL);
/* External GIC */
dev = qdev_create(NULL, "exynos4210.gic");
qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
/* Map CPU interface */
sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR);
/* Map Distributer interface */
sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR);
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]);
}
for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n);
}
/* Internal Interrupt Combiner */
dev = qdev_create(NULL, "exynos4210.combiner");
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]);
}
exynos4210_combiner_get_gpioin(&s->irqs, dev, 0);
sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
/* External Interrupt Combiner */
dev = qdev_create(NULL, "exynos4210.combiner");
qdev_prop_set_uint32(dev, "external", 1);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]);
}
exynos4210_combiner_get_gpioin(&s->irqs, dev, 1);
sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
/* Initialize board IRQs. */
exynos4210_init_board_irqs(&s->irqs);
/*** Memory ***/
/* Chip-ID and OMR */
memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid",
sizeof(chipid_and_omr), chipid_and_omr);
memory_region_set_readonly(&s->chipid_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR,
&s->chipid_mem);
/* Internal ROM */
memory_region_init_ram(&s->irom_mem, "exynos4210.irom",
EXYNOS4210_IROM_SIZE);
memory_region_set_readonly(&s->irom_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
&s->irom_mem);
/* mirror of iROM */
memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
&s->irom_mem,
EXYNOS4210_IROM_BASE_ADDR,
EXYNOS4210_IROM_SIZE);
memory_region_set_readonly(&s->irom_alias_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
&s->irom_alias_mem);
/* Internal RAM */
memory_region_init_ram(&s->iram_mem, "exynos4210.iram",
EXYNOS4210_IRAM_SIZE);
vmstate_register_ram_global(&s->iram_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
&s->iram_mem);
/* DRAM */
mem_size = ram_size;
if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1",
mem_size - EXYNOS4210_DRAM_MAX_SIZE);
vmstate_register_ram_global(&s->dram1_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
&s->dram1_mem);
mem_size = EXYNOS4210_DRAM_MAX_SIZE;
}
memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size);
vmstate_register_ram_global(&s->dram0_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
&s->dram0_mem);
/* PMU.
* The only reason of existence at the moment is that secondary CPU boot
* loader uses PMU INFORM5 register as a holding pen.
*/
sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
/* PWM */
sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
s->irq_table[exynos4210_get_irq(22, 0)],
s->irq_table[exynos4210_get_irq(22, 1)],
s->irq_table[exynos4210_get_irq(22, 2)],
s->irq_table[exynos4210_get_irq(22, 3)],
s->irq_table[exynos4210_get_irq(22, 4)],
NULL);
/* Multi Core Timer */
dev = qdev_create(NULL, "exynos4210.mct");
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
for (n = 0; n < 4; n++) {
/* Connect global timer interrupts to Combiner gpio_in */
sysbus_connect_irq(busdev, n,
s->irq_table[exynos4210_get_irq(1, 4 + n)]);
}
/* Connect local timer interrupts to Combiner gpio_in */
sysbus_connect_irq(busdev, 4,
s->irq_table[exynos4210_get_irq(51, 0)]);
sysbus_connect_irq(busdev, 5,
s->irq_table[exynos4210_get_irq(35, 3)]);
sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR);
/*** UARTs ***/
exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]);
exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR,
EXYNOS4210_UART1_FIFO_SIZE, 1, NULL,
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]);
exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR,
EXYNOS4210_UART2_FIFO_SIZE, 2, NULL,
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]);
exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR,
EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
/*** Display controller (FIMD) ***/
sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
s->irq_table[exynos4210_get_irq(11, 0)],
s->irq_table[exynos4210_get_irq(11, 1)],
s->irq_table[exynos4210_get_irq(11, 2)],
NULL);
return s;
}

131
hw/exynos4210.h Normal file
View file

@ -0,0 +1,131 @@
/*
* Samsung exynos4210 SoC 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef EXYNOS4210_H_
#define EXYNOS4210_H_
#include "qemu-common.h"
#include "memory.h"
#define EXYNOS4210_NCPUS 2
#define EXYNOS4210_DRAM0_BASE_ADDR 0x40000000
#define EXYNOS4210_DRAM1_BASE_ADDR 0xa0000000
#define EXYNOS4210_DRAM_MAX_SIZE 0x60000000 /* 1.5 GB */
#define EXYNOS4210_IROM_BASE_ADDR 0x00000000
#define EXYNOS4210_IROM_SIZE 0x00010000 /* 64 KB */
#define EXYNOS4210_IROM_MIRROR_BASE_ADDR 0x02000000
#define EXYNOS4210_IROM_MIRROR_SIZE 0x00010000 /* 64 KB */
#define EXYNOS4210_IRAM_BASE_ADDR 0x02020000
#define EXYNOS4210_IRAM_SIZE 0x00020000 /* 128 KB */
/* Secondary CPU startup code is in IROM memory */
#define EXYNOS4210_SMP_BOOT_ADDR EXYNOS4210_IROM_BASE_ADDR
#define EXYNOS4210_SMP_BOOT_SIZE 0x1000
#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR
/* Secondary CPU polling address to get loader start from */
#define EXYNOS4210_SECOND_CPU_BOOTREG 0x10020814
#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000
#define EXYNOS4210_L2X0_BASE_ADDR 0x10502000
/*
* exynos4210 IRQ subsystem stub definitions.
*/
#define EXYNOS4210_IRQ_GATE_NINPUTS 8
#define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64
#define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16
#define EXYNOS4210_MAX_INT_COMBINER_IN_IRQ \
(EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ * 8)
#define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ \
(EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8)
#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp)*8 + (bit))
#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8)
#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \
((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq))
/* IRQs number for external and internal GIC */
#define EXYNOS4210_EXT_GIC_NIRQ (160-32)
#define EXYNOS4210_INT_GIC_NIRQ 64
typedef struct Exynos4210Irq {
qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ];
qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ];
qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ];
qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
} Exynos4210Irq;
typedef struct Exynos4210State {
CPUState * env[EXYNOS4210_NCPUS];
Exynos4210Irq irqs;
qemu_irq *irq_table;
MemoryRegion chipid_mem;
MemoryRegion iram_mem;
MemoryRegion irom_mem;
MemoryRegion irom_alias_mem;
MemoryRegion dram0_mem;
MemoryRegion dram1_mem;
MemoryRegion boot_secondary;
MemoryRegion bootreg_mem;
} Exynos4210State;
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
unsigned long ram_size);
/* Initialize exynos4210 IRQ subsystem stub */
qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
/* Initialize board IRQs.
* These IRQs contain splitted Int/External Combiner and External Gic IRQs */
void exynos4210_init_board_irqs(Exynos4210Irq *s);
/* Get IRQ number from exynos4210 IRQ subsystem stub.
* To identify IRQ source use internal combiner group and bit number
* grp - group number
* bit - bit number inside group */
uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit);
/*
* Get Combiner input GPIO into irqs structure
*/
void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
int ext);
/*
* exynos4210 UART
*/
DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
int fifo_size,
int channel,
CharDriverState *chr,
qemu_irq irq);
#endif /* EXYNOS4210_H_ */

469
hw/exynos4210_combiner.c Normal file
View file

@ -0,0 +1,469 @@
/*
* Samsung exynos4210 Interrupt Combiner
*
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Evgeny Voevodin <e.voevodin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
* IRQ sources into groups and provides signal output to GIC from each group. It
* is driven by common mask and enable/disable logic. Take a note that not all
* IRQs are passed to GIC through Combiner.
*/
#include "sysbus.h"
#include "exynos4210.h"
//#define DEBUG_COMBINER
#ifdef DEBUG_COMBINER
#define DPRINTF(fmt, ...) \
do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while (0)
#endif
#define IIC_NGRP 64 /* Internal Interrupt Combiner
Groups number */
#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner
Interrupts number */
#define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */
#define IIC_REGSET_SIZE 0x41
/*
* State for each output signal of internal combiner
*/
typedef struct CombinerGroupState {
uint8_t src_mask; /* 1 - source enabled, 0 - disabled */
uint8_t src_pending; /* Pending source interrupts before masking */
} CombinerGroupState;
typedef struct Exynos4210CombinerState {
SysBusDevice busdev;
MemoryRegion iomem;
struct CombinerGroupState group[IIC_NGRP];
uint32_t reg_set[IIC_REGSET_SIZE];
uint32_t icipsr[2];
uint32_t external; /* 1 means that this combiner is external */
qemu_irq output_irq[IIC_NGRP];
} Exynos4210CombinerState;
static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
.name = "exynos4210.combiner.groupstate",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(src_mask, CombinerGroupState),
VMSTATE_UINT8(src_pending, CombinerGroupState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_exynos4210_combiner = {
.name = "exynos4210.combiner",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
vmstate_exynos4210_combiner_group_state, CombinerGroupState),
VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
IIC_REGSET_SIZE),
VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
VMSTATE_UINT32(external, Exynos4210CombinerState),
VMSTATE_END_OF_LIST()
}
};
/*
* Get Combiner input GPIO into irqs structure
*/
void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
int ext)
{
int n;
int bit;
int max;
qemu_irq *irq;
max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
/*
* Some IRQs of Int/External Combiner are going to two Combiners groups,
* so let split them.
*/
for (n = 0; n < max; n++) {
bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
switch (n) {
/* MDNIE_LCD1 INTG1 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
continue;
/* TMU INTG3 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
continue;
/* LCD1 INTG12 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
continue;
/* Multi-Core Timer INTG12 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG35 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG51 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG53 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
}
irq[n] = qdev_get_gpio_in(dev, n);
}
}
static uint64_t
exynos4210_combiner_read(void *opaque, target_phys_addr_t offset, unsigned size)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
get a start of corresponding group quad */
uint32_t grp_quad_base_n; /* Base of group quad */
uint32_t reg_n; /* Register number inside the quad */
uint32_t val;
if (s->external && (offset > 0x3c && offset != 0x100)) {
hw_error("exynos4210.combiner: unallowed read access at offset 0x"
TARGET_FMT_plx "\n", offset);
}
req_quad_base_n = offset >> 4;
grp_quad_base_n = req_quad_base_n << 2;
reg_n = (offset - (req_quad_base_n << 4)) >> 2;
if (req_quad_base_n >= IIC_NGRP) {
/* Read of ICIPSR register */
return s->icipsr[reg_n];
}
val = 0;
switch (reg_n) {
/* IISTR */
case 2:
val |= s->group[grp_quad_base_n].src_pending;
val |= s->group[grp_quad_base_n + 1].src_pending << 8;
val |= s->group[grp_quad_base_n + 2].src_pending << 16;
val |= s->group[grp_quad_base_n + 3].src_pending << 24;
break;
/* IIMSR */
case 3:
val |= s->group[grp_quad_base_n].src_mask &
s->group[grp_quad_base_n].src_pending;
val |= (s->group[grp_quad_base_n + 1].src_mask &
s->group[grp_quad_base_n + 1].src_pending) << 8;
val |= (s->group[grp_quad_base_n + 2].src_mask &
s->group[grp_quad_base_n + 2].src_pending) << 16;
val |= (s->group[grp_quad_base_n + 3].src_mask &
s->group[grp_quad_base_n + 3].src_pending) << 24;
break;
default:
if (offset >> 2 >= IIC_REGSET_SIZE) {
hw_error("exynos4210.combiner: overflow of reg_set by 0x"
TARGET_FMT_plx "offset\n", offset);
}
val = s->reg_set[offset >> 2];
return 0;
}
return val;
}
static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
/* Send interrupt if needed */
if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
#ifdef DEBUG_COMBINER
if (group_n != 26) {
/* skip uart */
DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
}
#endif
/* Set Combiner interrupt pending status after masking */
if (group_n >= 32) {
s->icipsr[1] |= 1 << (group_n - 32);
} else {
s->icipsr[0] |= 1 << group_n;
}
qemu_irq_raise(s->output_irq[group_n]);
} else {
#ifdef DEBUG_COMBINER
if (group_n != 26) {
/* skip uart */
DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
}
#endif
/* Set Combiner interrupt pending status after masking */
if (group_n >= 32) {
s->icipsr[1] &= ~(1 << (group_n - 32));
} else {
s->icipsr[0] &= ~(1 << group_n);
}
qemu_irq_lower(s->output_irq[group_n]);
}
}
static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset,
uint64_t val, unsigned size)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
get a start of corresponding group quad */
uint32_t grp_quad_base_n; /* Base of group quad */
uint32_t reg_n; /* Register number inside the quad */
if (s->external && (offset > 0x3c && offset != 0x100)) {
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
}
req_quad_base_n = offset >> 4;
grp_quad_base_n = req_quad_base_n << 2;
reg_n = (offset - (req_quad_base_n << 4)) >> 2;
if (req_quad_base_n >= IIC_NGRP) {
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
return;
}
if (reg_n > 1) {
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
return;
}
if (offset >> 2 >= IIC_REGSET_SIZE) {
hw_error("exynos4210.combiner: overflow of reg_set by 0x"
TARGET_FMT_plx "offset\n", offset);
}
s->reg_set[offset >> 2] = val;
switch (reg_n) {
/* IIESR */
case 0:
/* FIXME: what if irq is pending, allowed by mask, and we allow it
* again. Interrupt will rise again! */
DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
s->external ? "EXT" : "INT",
grp_quad_base_n,
grp_quad_base_n + 1,
grp_quad_base_n + 2,
grp_quad_base_n + 3);
/* Enable interrupt sources */
s->group[grp_quad_base_n].src_mask |= val & 0xFF;
s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
exynos4210_combiner_update(s, grp_quad_base_n);
exynos4210_combiner_update(s, grp_quad_base_n + 1);
exynos4210_combiner_update(s, grp_quad_base_n + 2);
exynos4210_combiner_update(s, grp_quad_base_n + 3);
break;
/* IIECR */
case 1:
DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
s->external ? "EXT" : "INT",
grp_quad_base_n,
grp_quad_base_n + 1,
grp_quad_base_n + 2,
grp_quad_base_n + 3);
/* Disable interrupt sources */
s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
exynos4210_combiner_update(s, grp_quad_base_n);
exynos4210_combiner_update(s, grp_quad_base_n + 1);
exynos4210_combiner_update(s, grp_quad_base_n + 2);
exynos4210_combiner_update(s, grp_quad_base_n + 3);
break;
default:
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
break;
}
return;
}
/* Get combiner group and bit from irq number */
static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
{
*bit = irq - ((irq >> 3) << 3);
return irq >> 3;
}
/* Process a change in an external IRQ input. */
static void exynos4210_combiner_handler(void *opaque, int irq, int level)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint8_t bit_n, group_n;
group_n = get_combiner_group_and_bit(irq, &bit_n);
if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
, group_n);
return;
}
if (level) {
s->group[group_n].src_pending |= 1 << bit_n;
} else {
s->group[group_n].src_pending &= ~(1 << bit_n);
}
exynos4210_combiner_update(s, group_n);
return;
}
static void exynos4210_combiner_reset(DeviceState *d)
{
struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
memset(&s->group, 0, sizeof(s->group));
memset(&s->reg_set, 0, sizeof(s->reg_set));
s->reg_set[0xC0 >> 2] = 0x01010101;
s->reg_set[0xC4 >> 2] = 0x01010101;
s->reg_set[0xD0 >> 2] = 0x01010101;
s->reg_set[0xD4 >> 2] = 0x01010101;
}
static const MemoryRegionOps exynos4210_combiner_ops = {
.read = exynos4210_combiner_read,
.write = exynos4210_combiner_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* Internal Combiner initialization.
*/
static int exynos4210_combiner_init(SysBusDevice *dev)
{
unsigned int i;
struct Exynos4210CombinerState *s =
FROM_SYSBUS(struct Exynos4210CombinerState, dev);
/* Allocate general purpose input signals and connect a handler to each of
* them */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
/* Connect SysBusDev irqs to device specific irqs */
for (i = 0; i < IIC_NIRQ; i++) {
sysbus_init_irq(dev, &s->output_irq[i]);
}
memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
"exynos4210-combiner", IIC_REGION_SIZE);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static Property exynos4210_combiner_properties[] = {
DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_combiner_init;
dc->reset = exynos4210_combiner_reset;
dc->props = exynos4210_combiner_properties;
dc->vmsd = &vmstate_exynos4210_combiner;
}
static TypeInfo exynos4210_combiner_info = {
.name = "exynos4210.combiner",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210CombinerState),
.class_init = exynos4210_combiner_class_init,
};
static void exynos4210_combiner_register_types(void)
{
type_register_static(&exynos4210_combiner_info);
}
type_init(exynos4210_combiner_register_types)

1928
hw/exynos4210_fimd.c Normal file

File diff suppressed because it is too large Load diff

458
hw/exynos4210_gic.c Normal file
View file

@ -0,0 +1,458 @@
/*
* Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
*
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Evgeny Voevodin <e.voevodin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "sysbus.h"
#include "qemu-common.h"
#include "irq.h"
#include "exynos4210.h"
enum ExtGicId {
EXT_GIC_ID_MDMA_LCD0 = 66,
EXT_GIC_ID_PDMA0,
EXT_GIC_ID_PDMA1,
EXT_GIC_ID_TIMER0,
EXT_GIC_ID_TIMER1,
EXT_GIC_ID_TIMER2,
EXT_GIC_ID_TIMER3,
EXT_GIC_ID_TIMER4,
EXT_GIC_ID_MCT_L0,
EXT_GIC_ID_WDT,
EXT_GIC_ID_RTC_ALARM,
EXT_GIC_ID_RTC_TIC,
EXT_GIC_ID_GPIO_XB,
EXT_GIC_ID_GPIO_XA,
EXT_GIC_ID_MCT_L1,
EXT_GIC_ID_IEM_APC,
EXT_GIC_ID_IEM_IEC,
EXT_GIC_ID_NFC,
EXT_GIC_ID_UART0,
EXT_GIC_ID_UART1,
EXT_GIC_ID_UART2,
EXT_GIC_ID_UART3,
EXT_GIC_ID_UART4,
EXT_GIC_ID_MCT_G0,
EXT_GIC_ID_I2C0,
EXT_GIC_ID_I2C1,
EXT_GIC_ID_I2C2,
EXT_GIC_ID_I2C3,
EXT_GIC_ID_I2C4,
EXT_GIC_ID_I2C5,
EXT_GIC_ID_I2C6,
EXT_GIC_ID_I2C7,
EXT_GIC_ID_SPI0,
EXT_GIC_ID_SPI1,
EXT_GIC_ID_SPI2,
EXT_GIC_ID_MCT_G1,
EXT_GIC_ID_USB_HOST,
EXT_GIC_ID_USB_DEVICE,
EXT_GIC_ID_MODEMIF,
EXT_GIC_ID_HSMMC0,
EXT_GIC_ID_HSMMC1,
EXT_GIC_ID_HSMMC2,
EXT_GIC_ID_HSMMC3,
EXT_GIC_ID_SDMMC,
EXT_GIC_ID_MIPI_CSI_4LANE,
EXT_GIC_ID_MIPI_DSI_4LANE,
EXT_GIC_ID_MIPI_CSI_2LANE,
EXT_GIC_ID_MIPI_DSI_2LANE,
EXT_GIC_ID_ONENAND_AUDI,
EXT_GIC_ID_ROTATOR,
EXT_GIC_ID_FIMC0,
EXT_GIC_ID_FIMC1,
EXT_GIC_ID_FIMC2,
EXT_GIC_ID_FIMC3,
EXT_GIC_ID_JPEG,
EXT_GIC_ID_2D,
EXT_GIC_ID_PCIe,
EXT_GIC_ID_MIXER,
EXT_GIC_ID_HDMI,
EXT_GIC_ID_HDMI_I2C,
EXT_GIC_ID_MFC,
EXT_GIC_ID_TVENC,
};
enum ExtInt {
EXT_GIC_ID_EXTINT0 = 48,
EXT_GIC_ID_EXTINT1,
EXT_GIC_ID_EXTINT2,
EXT_GIC_ID_EXTINT3,
EXT_GIC_ID_EXTINT4,
EXT_GIC_ID_EXTINT5,
EXT_GIC_ID_EXTINT6,
EXT_GIC_ID_EXTINT7,
EXT_GIC_ID_EXTINT8,
EXT_GIC_ID_EXTINT9,
EXT_GIC_ID_EXTINT10,
EXT_GIC_ID_EXTINT11,
EXT_GIC_ID_EXTINT12,
EXT_GIC_ID_EXTINT13,
EXT_GIC_ID_EXTINT14,
EXT_GIC_ID_EXTINT15
};
/*
* External GIC sources which are not from External Interrupt Combiner or
* External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
* which is INTG16 in Internal Interrupt Combiner.
*/
static uint32_t
combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
/* int combiner groups 16-19 */
{ }, { }, { }, { },
/* int combiner group 20 */
{ 0, EXT_GIC_ID_MDMA_LCD0 },
/* int combiner group 21 */
{ EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
/* int combiner group 22 */
{ EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
/* int combiner group 23 */
{ EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
/* int combiner group 24 */
{ EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
/* int combiner group 25 */
{ EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
/* int combiner group 26 */
{ EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
EXT_GIC_ID_UART4 },
/* int combiner group 27 */
{ EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
EXT_GIC_ID_I2C7 },
/* int combiner group 28 */
{ EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 },
/* int combiner group 29 */
{ EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
/* int combiner group 30 */
{ EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
/* int combiner group 31 */
{ EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
/* int combiner group 32 */
{ EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
/* int combiner group 33 */
{ EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
/* int combiner group 34 */
{ EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
/* int combiner group 35 */
{ 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* int combiner group 36 */
{ EXT_GIC_ID_MIXER },
/* int combiner group 37 */
{ EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
EXT_GIC_ID_EXTINT7 },
/* groups 38-50 */
{ }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
/* int combiner group 51 */
{ EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* group 52 */
{ },
/* int combiner group 53 */
{ EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* groups 54-63 */
{ }, { }, { }, { }, { }, { }, { }, { }, { }, { }
};
#define EXYNOS4210_GIC_NIRQ 160
#define NCPU EXYNOS4210_NCPUS
#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000
#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000
#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET 0x8000
#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100
#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
static void exynos4210_irq_handler(void *opaque, int irq, int level)
{
Exynos4210Irq *s = (Exynos4210Irq *)opaque;
/* Bypass */
qemu_set_irq(s->board_irqs[irq], level);
return;
}
/*
* Initialize exynos4210 IRQ subsystem stub.
*/
qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
{
return qemu_allocate_irqs(exynos4210_irq_handler, s,
EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
}
/*
* Initialize board IRQs.
* These IRQs contain splitted Int/External Combiner and External Gic IRQs.
*/
void exynos4210_init_board_irqs(Exynos4210Irq *s)
{
uint32_t grp, bit, irq_id, n;
for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_combiner_irq[n]);
irq_id = 0;
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
/* MCT_G0 is passed to External GIC */
irq_id = EXT_GIC_ID_MCT_G0;
}
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
/* MCT_G1 is passed to External and GIC */
irq_id = EXT_GIC_ID_MCT_G1;
}
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
}
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
/* these IDs are passed to Internal Combiner and External GIC */
grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
irq_id = combiner_grp_to_gic_id[grp -
EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
}
}
}
/*
* Get IRQ number from exynos4210 IRQ subsystem stub.
* To identify IRQ source use internal combiner group and bit number
* grp - group number
* bit - bit number inside group
*/
uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
{
return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
}
/********* GIC part *********/
static inline int
gic_get_current_cpu(void)
{
return cpu_single_env->cpu_index;
}
#include "arm_gic.c"
typedef struct {
gic_state gic;
MemoryRegion cpu_container;
MemoryRegion dist_container;
MemoryRegion cpu_alias[NCPU];
MemoryRegion dist_alias[NCPU];
uint32_t num_cpu;
} Exynos4210GicState;
static int exynos4210_gic_init(SysBusDevice *dev)
{
Exynos4210GicState *s = FROM_SYSBUSGIC(Exynos4210GicState, dev);
uint32_t i;
const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
const char dist_prefix[] = "exynos4210-gic-alias_dist";
char cpu_alias_name[sizeof(cpu_prefix) + 3];
char dist_alias_name[sizeof(cpu_prefix) + 3];
gic_init(&s->gic, s->num_cpu, EXYNOS4210_GIC_NIRQ);
memory_region_init(&s->cpu_container, "exynos4210-cpu-container",
EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
memory_region_init(&s->dist_container, "exynos4210-dist-container",
EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
for (i = 0; i < s->num_cpu; i++) {
/* Map CPU interface per SMP Core */
sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
memory_region_init_alias(&s->cpu_alias[i],
cpu_alias_name,
&s->gic.cpuiomem[0],
0,
EXYNOS4210_GIC_CPU_REGION_SIZE);
memory_region_add_subregion(&s->cpu_container,
EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
/* Map Distributor per SMP Core */
sprintf(dist_alias_name, "%s%x", dist_prefix, i);
memory_region_init_alias(&s->dist_alias[i],
dist_alias_name,
&s->gic.iomem,
0,
EXYNOS4210_GIC_DIST_REGION_SIZE);
memory_region_add_subregion(&s->dist_container,
EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
}
sysbus_init_mmio(dev, &s->cpu_container);
sysbus_init_mmio(dev, &s->dist_container);
gic_cpu_write(&s->gic, 1, 0, 1);
return 0;
}
static Property exynos4210_gic_properties[] = {
DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
DEFINE_PROP_END_OF_LIST(),
};
static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_gic_init;
dc->props = exynos4210_gic_properties;
}
static TypeInfo exynos4210_gic_info = {
.name = "exynos4210.gic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210GicState),
.class_init = exynos4210_gic_class_init,
};
static void exynos4210_gic_register_types(void)
{
type_register_static(&exynos4210_gic_info);
}
type_init(exynos4210_gic_register_types)
/*
* IRQGate struct.
* IRQ Gate represents OR gate between GICs to pass IRQ to PIC.
*/
typedef struct {
SysBusDevice busdev;
qemu_irq pic_irq[NCPU]; /* output IRQs to PICs */
uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */
} Exynos4210IRQGateState;
static const VMStateDescription vmstate_exynos4210_irq_gate = {
.name = "exynos4210.irq_gate",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState,
EXYNOS4210_IRQ_GATE_NINPUTS),
VMSTATE_END_OF_LIST()
}
};
/* Process a change in an external IRQ input. */
static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
{
Exynos4210IRQGateState *s =
(Exynos4210IRQGateState *)opaque;
uint32_t odd, even;
if (irq & 1) {
odd = irq;
even = irq & ~1;
} else {
even = irq;
odd = irq | 1;
}
assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS);
s->gpio_level[irq] = level;
if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) {
qemu_irq_raise(s->pic_irq[even >> 1]);
} else {
qemu_irq_lower(s->pic_irq[even >> 1]);
}
return;
}
static void exynos4210_irq_gate_reset(DeviceState *d)
{
Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d;
memset(&s->gpio_level, 0, sizeof(s->gpio_level));
}
/*
* IRQ Gate initialization.
*/
static int exynos4210_irq_gate_init(SysBusDevice *dev)
{
unsigned int i;
Exynos4210IRQGateState *s =
FROM_SYSBUS(Exynos4210IRQGateState, dev);
/* Allocate general purpose input signals and connect a handler to each of
* them */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler,
EXYNOS4210_IRQ_GATE_NINPUTS);
/* Connect SysBusDev irqs to device specific irqs */
for (i = 0; i < NCPU; i++) {
sysbus_init_irq(dev, &s->pic_irq[i]);
}
return 0;
}
static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_irq_gate_init;
dc->reset = exynos4210_irq_gate_reset;
dc->vmsd = &vmstate_exynos4210_irq_gate;
}
static TypeInfo exynos4210_irq_gate_info = {
.name = "exynos4210.irq_gate",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210IRQGateState),
.class_init = exynos4210_irq_gate_class_init,
};
static void exynos4210_irq_gate_register_types(void)
{
type_register_static(&exynos4210_irq_gate_info);
}
type_init(exynos4210_irq_gate_register_types)

1488
hw/exynos4210_mct.c Normal file

File diff suppressed because it is too large Load diff

499
hw/exynos4210_pmu.c Normal file
View file

@ -0,0 +1,499 @@
/*
* Exynos4210 Power Management Unit (PMU) Emulation
*
* Copyright (C) 2011 Samsung Electronics Co Ltd.
* Maksim Kozlov <m.kozlov@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This model implements PMU registers just as a bulk of memory. Currently,
* the only reason this device exists is that secondary CPU boot loader
* uses PMU INFORM5 register as a holding pen.
*/
#include "sysbus.h"
#ifndef DEBUG_PMU
#define DEBUG_PMU 0
#endif
#ifndef DEBUG_PMU_EXTEND
#define DEBUG_PMU_EXTEND 0
#endif
#if DEBUG_PMU
#define PRINT_DEBUG(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
} while (0)
#if DEBUG_PMU_EXTEND
#define PRINT_DEBUG_EXTEND(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
} while (0)
#else
#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0)
#endif /* EXTEND */
#else
#define PRINT_DEBUG(fmt, args...) do {} while (0)
#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0)
#endif
/*
* Offsets for PMU registers
*/
#define OM_STAT 0x0000 /* OM status register */
#define RTC_CLKO_SEL 0x000C /* Controls RTCCLKOUT */
#define GNSS_RTC_OUT_CTRL 0x0010 /* Controls GNSS_RTC_OUT */
/* Decides whether system-level low-power mode is used. */
#define SYSTEM_POWER_DOWN_CTRL 0x0200
/* Sets control options for CENTRAL_SEQ */
#define SYSTEM_POWER_DOWN_OPTION 0x0208
#define SWRESET 0x0400 /* Generate software reset */
#define RST_STAT 0x0404 /* Reset status register */
#define WAKEUP_STAT 0x0600 /* Wakeup status register */
#define EINT_WAKEUP_MASK 0x0604 /* Configure External INTerrupt mask */
#define WAKEUP_MASK 0x0608 /* Configure wakeup source mask */
#define HDMI_PHY_CONTROL 0x0700 /* HDMI PHY control register */
#define USBDEVICE_PHY_CONTROL 0x0704 /* USB Device PHY control register */
#define USBHOST_PHY_CONTROL 0x0708 /* USB HOST PHY control register */
#define DAC_PHY_CONTROL 0x070C /* DAC control register */
#define MIPI_PHY0_CONTROL 0x0710 /* MIPI PHY control register */
#define MIPI_PHY1_CONTROL 0x0714 /* MIPI PHY control register */
#define ADC_PHY_CONTROL 0x0718 /* TS-ADC control register */
#define PCIe_PHY_CONTROL 0x071C /* TS-PCIe control register */
#define SATA_PHY_CONTROL 0x0720 /* TS-SATA control register */
#define INFORM0 0x0800 /* Information register 0 */
#define INFORM1 0x0804 /* Information register 1 */
#define INFORM2 0x0808 /* Information register 2 */
#define INFORM3 0x080C /* Information register 3 */
#define INFORM4 0x0810 /* Information register 4 */
#define INFORM5 0x0814 /* Information register 5 */
#define INFORM6 0x0818 /* Information register 6 */
#define INFORM7 0x081C /* Information register 7 */
#define PMU_DEBUG 0x0A00 /* PMU debug register */
/* Registers to set system-level low-power option */
#define ARM_CORE0_SYS_PWR_REG 0x1000
#define ARM_CORE1_SYS_PWR_REG 0x1010
#define ARM_COMMON_SYS_PWR_REG 0x1080
#define ARM_CPU_L2_0_SYS_PWR_REG 0x10C0
#define ARM_CPU_L2_1_SYS_PWR_REG 0x10C4
#define CMU_ACLKSTOP_SYS_PWR_REG 0x1100
#define CMU_SCLKSTOP_SYS_PWR_REG 0x1104
#define CMU_RESET_SYS_PWR_REG 0x110C
#define APLL_SYSCLK_SYS_PWR_REG 0x1120
#define MPLL_SYSCLK_SYS_PWR_REG 0x1124
#define VPLL_SYSCLK_SYS_PWR_REG 0x1128
#define EPLL_SYSCLK_SYS_PWR_REG 0x112C
#define CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG 0x1138
#define CMU_RESET_GPS_ALIVE_SYS_PWR_REG 0x113C
#define CMU_CLKSTOP_CAM_SYS_PWR_REG 0x1140
#define CMU_CLKSTOP_TV_SYS_PWR_REG 0x1144
#define CMU_CLKSTOP_MFC_SYS_PWR_REG 0x1148
#define CMU_CLKSTOP_G3D_SYS_PWR_REG 0x114C
#define CMU_CLKSTOP_LCD0_SYS_PWR_REG 0x1150
#define CMU_CLKSTOP_LCD1_SYS_PWR_REG 0x1154
#define CMU_CLKSTOP_MAUDIO_SYS_PWR_REG 0x1158
#define CMU_CLKSTOP_GPS_SYS_PWR_REG 0x115C
#define CMU_RESET_CAM_SYS_PWR_REG 0x1160
#define CMU_RESET_TV_SYS_PWR_REG 0x1164
#define CMU_RESET_MFC_SYS_PWR_REG 0x1168
#define CMU_RESET_G3D_SYS_PWR_REG 0x116C
#define CMU_RESET_LCD0_SYS_PWR_REG 0x1170
#define CMU_RESET_LCD1_SYS_PWR_REG 0x1174
#define CMU_RESET_MAUDIO_SYS_PWR_REG 0x1178
#define CMU_RESET_GPS_SYS_PWR_REG 0x117C
#define TOP_BUS_SYS_PWR_REG 0x1180
#define TOP_RETENTION_SYS_PWR_REG 0x1184
#define TOP_PWR_SYS_PWR_REG 0x1188
#define LOGIC_RESET_SYS_PWR_REG 0x11A0
#define OneNANDXL_MEM_SYS_PWR_REG 0x11C0
#define MODEMIF_MEM_SYS_PWR_REG 0x11C4
#define USBDEVICE_MEM_SYS_PWR_REG 0x11CC
#define SDMMC_MEM_SYS_PWR_REG 0x11D0
#define CSSYS_MEM_SYS_PWR_REG 0x11D4
#define SECSS_MEM_SYS_PWR_REG 0x11D8
#define PCIe_MEM_SYS_PWR_REG 0x11E0
#define SATA_MEM_SYS_PWR_REG 0x11E4
#define PAD_RETENTION_DRAM_SYS_PWR_REG 0x1200
#define PAD_RETENTION_MAUDIO_SYS_PWR_REG 0x1204
#define PAD_RETENTION_GPIO_SYS_PWR_REG 0x1220
#define PAD_RETENTION_UART_SYS_PWR_REG 0x1224
#define PAD_RETENTION_MMCA_SYS_PWR_REG 0x1228
#define PAD_RETENTION_MMCB_SYS_PWR_REG 0x122C
#define PAD_RETENTION_EBIA_SYS_PWR_REG 0x1230
#define PAD_RETENTION_EBIB_SYS_PWR_REG 0x1234
#define PAD_ISOLATION_SYS_PWR_REG 0x1240
#define PAD_ALV_SEL_SYS_PWR_REG 0x1260
#define XUSBXTI_SYS_PWR_REG 0x1280
#define XXTI_SYS_PWR_REG 0x1284
#define EXT_REGULATOR_SYS_PWR_REG 0x12C0
#define GPIO_MODE_SYS_PWR_REG 0x1300
#define GPIO_MODE_MAUDIO_SYS_PWR_REG 0x1340
#define CAM_SYS_PWR_REG 0x1380
#define TV_SYS_PWR_REG 0x1384
#define MFC_SYS_PWR_REG 0x1388
#define G3D_SYS_PWR_REG 0x138C
#define LCD0_SYS_PWR_REG 0x1390
#define LCD1_SYS_PWR_REG 0x1394
#define MAUDIO_SYS_PWR_REG 0x1398
#define GPS_SYS_PWR_REG 0x139C
#define GPS_ALIVE_SYS_PWR_REG 0x13A0
#define ARM_CORE0_CONFIGURATION 0x2000 /* Configure power mode of ARM_CORE0 */
#define ARM_CORE0_STATUS 0x2004 /* Check power mode of ARM_CORE0 */
#define ARM_CORE0_OPTION 0x2008 /* Sets control options for ARM_CORE0 */
#define ARM_CORE1_CONFIGURATION 0x2080 /* Configure power mode of ARM_CORE1 */
#define ARM_CORE1_STATUS 0x2084 /* Check power mode of ARM_CORE1 */
#define ARM_CORE1_OPTION 0x2088 /* Sets control options for ARM_CORE0 */
#define ARM_COMMON_OPTION 0x2408 /* Sets control options for ARM_COMMON */
/* Configure power mode of ARM_CPU_L2_0 */
#define ARM_CPU_L2_0_CONFIGURATION 0x2600
#define ARM_CPU_L2_0_STATUS 0x2604 /* Check power mode of ARM_CPU_L2_0 */
/* Configure power mode of ARM_CPU_L2_1 */
#define ARM_CPU_L2_1_CONFIGURATION 0x2620
#define ARM_CPU_L2_1_STATUS 0x2624 /* Check power mode of ARM_CPU_L2_1 */
/* Sets control options for PAD_RETENTION_MAUDIO */
#define PAD_RETENTION_MAUDIO_OPTION 0x3028
/* Sets control options for PAD_RETENTION_GPIO */
#define PAD_RETENTION_GPIO_OPTION 0x3108
/* Sets control options for PAD_RETENTION_UART */
#define PAD_RETENTION_UART_OPTION 0x3128
/* Sets control options for PAD_RETENTION_MMCA */
#define PAD_RETENTION_MMCA_OPTION 0x3148
/* Sets control options for PAD_RETENTION_MMCB */
#define PAD_RETENTION_MMCB_OPTION 0x3168
/* Sets control options for PAD_RETENTION_EBIA */
#define PAD_RETENTION_EBIA_OPTION 0x3188
/* Sets control options for PAD_RETENTION_EBIB */
#define PAD_RETENTION_EBIB_OPTION 0x31A8
#define PS_HOLD_CONTROL 0x330C /* PS_HOLD control register */
#define XUSBXTI_CONFIGURATION 0x3400 /* Configure the pad of XUSBXTI */
#define XUSBXTI_STATUS 0x3404 /* Check the pad of XUSBXTI */
/* Sets time required for XUSBXTI to be stabilized */
#define XUSBXTI_DURATION 0x341C
#define XXTI_CONFIGURATION 0x3420 /* Configure the pad of XXTI */
#define XXTI_STATUS 0x3424 /* Check the pad of XXTI */
/* Sets time required for XXTI to be stabilized */
#define XXTI_DURATION 0x343C
/* Sets time required for EXT_REGULATOR to be stabilized */
#define EXT_REGULATOR_DURATION 0x361C
#define CAM_CONFIGURATION 0x3C00 /* Configure power mode of CAM */
#define CAM_STATUS 0x3C04 /* Check power mode of CAM */
#define CAM_OPTION 0x3C08 /* Sets control options for CAM */
#define TV_CONFIGURATION 0x3C20 /* Configure power mode of TV */
#define TV_STATUS 0x3C24 /* Check power mode of TV */
#define TV_OPTION 0x3C28 /* Sets control options for TV */
#define MFC_CONFIGURATION 0x3C40 /* Configure power mode of MFC */
#define MFC_STATUS 0x3C44 /* Check power mode of MFC */
#define MFC_OPTION 0x3C48 /* Sets control options for MFC */
#define G3D_CONFIGURATION 0x3C60 /* Configure power mode of G3D */
#define G3D_STATUS 0x3C64 /* Check power mode of G3D */
#define G3D_OPTION 0x3C68 /* Sets control options for G3D */
#define LCD0_CONFIGURATION 0x3C80 /* Configure power mode of LCD0 */
#define LCD0_STATUS 0x3C84 /* Check power mode of LCD0 */
#define LCD0_OPTION 0x3C88 /* Sets control options for LCD0 */
#define LCD1_CONFIGURATION 0x3CA0 /* Configure power mode of LCD1 */
#define LCD1_STATUS 0x3CA4 /* Check power mode of LCD1 */
#define LCD1_OPTION 0x3CA8 /* Sets control options for LCD1 */
#define GPS_CONFIGURATION 0x3CE0 /* Configure power mode of GPS */
#define GPS_STATUS 0x3CE4 /* Check power mode of GPS */
#define GPS_OPTION 0x3CE8 /* Sets control options for GPS */
#define GPS_ALIVE_CONFIGURATION 0x3D00 /* Configure power mode of GPS */
#define GPS_ALIVE_STATUS 0x3D04 /* Check power mode of GPS */
#define GPS_ALIVE_OPTION 0x3D08 /* Sets control options for GPS */
#define EXYNOS4210_PMU_REGS_MEM_SIZE 0x3d0c
typedef struct Exynos4210PmuReg {
const char *name; /* for debug only */
uint32_t offset;
uint32_t reset_value;
} Exynos4210PmuReg;
static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
{"OM_STAT", OM_STAT, 0x00000000},
{"RTC_CLKO_SEL", RTC_CLKO_SEL, 0x00000000},
{"GNSS_RTC_OUT_CTRL", GNSS_RTC_OUT_CTRL, 0x00000001},
{"SYSTEM_POWER_DOWN_CTRL", SYSTEM_POWER_DOWN_CTRL, 0x00010000},
{"SYSTEM_POWER_DOWN_OPTION", SYSTEM_POWER_DOWN_OPTION, 0x03030000},
{"SWRESET", SWRESET, 0x00000000},
{"RST_STAT", RST_STAT, 0x00000000},
{"WAKEUP_STAT", WAKEUP_STAT, 0x00000000},
{"EINT_WAKEUP_MASK", EINT_WAKEUP_MASK, 0x00000000},
{"WAKEUP_MASK", WAKEUP_MASK, 0x00000000},
{"HDMI_PHY_CONTROL", HDMI_PHY_CONTROL, 0x00960000},
{"USBDEVICE_PHY_CONTROL", USBDEVICE_PHY_CONTROL, 0x00000000},
{"USBHOST_PHY_CONTROL", USBHOST_PHY_CONTROL, 0x00000000},
{"DAC_PHY_CONTROL", DAC_PHY_CONTROL, 0x00000000},
{"MIPI_PHY0_CONTROL", MIPI_PHY0_CONTROL, 0x00000000},
{"MIPI_PHY1_CONTROL", MIPI_PHY1_CONTROL, 0x00000000},
{"ADC_PHY_CONTROL", ADC_PHY_CONTROL, 0x00000001},
{"PCIe_PHY_CONTROL", PCIe_PHY_CONTROL, 0x00000000},
{"SATA_PHY_CONTROL", SATA_PHY_CONTROL, 0x00000000},
{"INFORM0", INFORM0, 0x00000000},
{"INFORM1", INFORM1, 0x00000000},
{"INFORM2", INFORM2, 0x00000000},
{"INFORM3", INFORM3, 0x00000000},
{"INFORM4", INFORM4, 0x00000000},
{"INFORM5", INFORM5, 0x00000000},
{"INFORM6", INFORM6, 0x00000000},
{"INFORM7", INFORM7, 0x00000000},
{"PMU_DEBUG", PMU_DEBUG, 0x00000000},
{"ARM_CORE0_SYS_PWR_REG", ARM_CORE0_SYS_PWR_REG, 0xFFFFFFFF},
{"ARM_CORE1_SYS_PWR_REG", ARM_CORE1_SYS_PWR_REG, 0xFFFFFFFF},
{"ARM_COMMON_SYS_PWR_REG", ARM_COMMON_SYS_PWR_REG, 0xFFFFFFFF},
{"ARM_CPU_L2_0_SYS_PWR_REG", ARM_CPU_L2_0_SYS_PWR_REG, 0xFFFFFFFF},
{"ARM_CPU_L2_1_SYS_PWR_REG", ARM_CPU_L2_1_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_ACLKSTOP_SYS_PWR_REG", CMU_ACLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_SCLKSTOP_SYS_PWR_REG", CMU_SCLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_SYS_PWR_REG", CMU_RESET_SYS_PWR_REG, 0xFFFFFFFF},
{"APLL_SYSCLK_SYS_PWR_REG", APLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
{"MPLL_SYSCLK_SYS_PWR_REG", MPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
{"VPLL_SYSCLK_SYS_PWR_REG", VPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
{"EPLL_SYSCLK_SYS_PWR_REG", EPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG", CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG,
0xFFFFFFFF},
{"CMU_RESET_GPS_ALIVE_SYS_PWR_REG", CMU_RESET_GPS_ALIVE_SYS_PWR_REG,
0xFFFFFFFF},
{"CMU_CLKSTOP_CAM_SYS_PWR_REG", CMU_CLKSTOP_CAM_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_TV_SYS_PWR_REG", CMU_CLKSTOP_TV_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_MFC_SYS_PWR_REG", CMU_CLKSTOP_MFC_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_G3D_SYS_PWR_REG", CMU_CLKSTOP_G3D_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_LCD0_SYS_PWR_REG", CMU_CLKSTOP_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_LCD1_SYS_PWR_REG", CMU_CLKSTOP_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_CLKSTOP_MAUDIO_SYS_PWR_REG", CMU_CLKSTOP_MAUDIO_SYS_PWR_REG,
0xFFFFFFFF},
{"CMU_CLKSTOP_GPS_SYS_PWR_REG", CMU_CLKSTOP_GPS_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_CAM_SYS_PWR_REG", CMU_RESET_CAM_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_TV_SYS_PWR_REG", CMU_RESET_TV_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_MFC_SYS_PWR_REG", CMU_RESET_MFC_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_G3D_SYS_PWR_REG", CMU_RESET_G3D_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_LCD0_SYS_PWR_REG", CMU_RESET_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_LCD1_SYS_PWR_REG", CMU_RESET_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_MAUDIO_SYS_PWR_REG", CMU_RESET_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
{"CMU_RESET_GPS_SYS_PWR_REG", CMU_RESET_GPS_SYS_PWR_REG, 0xFFFFFFFF},
{"TOP_BUS_SYS_PWR_REG", TOP_BUS_SYS_PWR_REG, 0xFFFFFFFF},
{"TOP_RETENTION_SYS_PWR_REG", TOP_RETENTION_SYS_PWR_REG, 0xFFFFFFFF},
{"TOP_PWR_SYS_PWR_REG", TOP_PWR_SYS_PWR_REG, 0xFFFFFFFF},
{"LOGIC_RESET_SYS_PWR_REG", LOGIC_RESET_SYS_PWR_REG, 0xFFFFFFFF},
{"OneNANDXL_MEM_SYS_PWR_REG", OneNANDXL_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"MODEMIF_MEM_SYS_PWR_REG", MODEMIF_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"USBDEVICE_MEM_SYS_PWR_REG", USBDEVICE_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"SDMMC_MEM_SYS_PWR_REG", SDMMC_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"CSSYS_MEM_SYS_PWR_REG", CSSYS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"SECSS_MEM_SYS_PWR_REG", SECSS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"PCIe_MEM_SYS_PWR_REG", PCIe_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"SATA_MEM_SYS_PWR_REG", SATA_MEM_SYS_PWR_REG, 0xFFFFFFFF},
{"PAD_RETENTION_DRAM_SYS_PWR_REG", PAD_RETENTION_DRAM_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_MAUDIO_SYS_PWR_REG", PAD_RETENTION_MAUDIO_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_GPIO_SYS_PWR_REG", PAD_RETENTION_GPIO_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_UART_SYS_PWR_REG", PAD_RETENTION_UART_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_MMCA_SYS_PWR_REG", PAD_RETENTION_MMCA_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_MMCB_SYS_PWR_REG", PAD_RETENTION_MMCB_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_EBIA_SYS_PWR_REG", PAD_RETENTION_EBIA_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_RETENTION_EBIB_SYS_PWR_REG", PAD_RETENTION_EBIB_SYS_PWR_REG,
0xFFFFFFFF},
{"PAD_ISOLATION_SYS_PWR_REG", PAD_ISOLATION_SYS_PWR_REG, 0xFFFFFFFF},
{"PAD_ALV_SEL_SYS_PWR_REG", PAD_ALV_SEL_SYS_PWR_REG, 0xFFFFFFFF},
{"XUSBXTI_SYS_PWR_REG", XUSBXTI_SYS_PWR_REG, 0xFFFFFFFF},
{"XXTI_SYS_PWR_REG", XXTI_SYS_PWR_REG, 0xFFFFFFFF},
{"EXT_REGULATOR_SYS_PWR_REG", EXT_REGULATOR_SYS_PWR_REG, 0xFFFFFFFF},
{"GPIO_MODE_SYS_PWR_REG", GPIO_MODE_SYS_PWR_REG, 0xFFFFFFFF},
{"GPIO_MODE_MAUDIO_SYS_PWR_REG", GPIO_MODE_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
{"CAM_SYS_PWR_REG", CAM_SYS_PWR_REG, 0xFFFFFFFF},
{"TV_SYS_PWR_REG", TV_SYS_PWR_REG, 0xFFFFFFFF},
{"MFC_SYS_PWR_REG", MFC_SYS_PWR_REG, 0xFFFFFFFF},
{"G3D_SYS_PWR_REG", G3D_SYS_PWR_REG, 0xFFFFFFFF},
{"LCD0_SYS_PWR_REG", LCD0_SYS_PWR_REG, 0xFFFFFFFF},
{"LCD1_SYS_PWR_REG", LCD1_SYS_PWR_REG, 0xFFFFFFFF},
{"MAUDIO_SYS_PWR_REG", MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
{"GPS_SYS_PWR_REG", GPS_SYS_PWR_REG, 0xFFFFFFFF},
{"GPS_ALIVE_SYS_PWR_REG", GPS_ALIVE_SYS_PWR_REG, 0xFFFFFFFF},
{"ARM_CORE0_CONFIGURATION", ARM_CORE0_CONFIGURATION, 0x00000003},
{"ARM_CORE0_STATUS", ARM_CORE0_STATUS, 0x00030003},
{"ARM_CORE0_OPTION", ARM_CORE0_OPTION, 0x01010001},
{"ARM_CORE1_CONFIGURATION", ARM_CORE1_CONFIGURATION, 0x00000003},
{"ARM_CORE1_STATUS", ARM_CORE1_STATUS, 0x00030003},
{"ARM_CORE1_OPTION", ARM_CORE1_OPTION, 0x01010001},
{"ARM_COMMON_OPTION", ARM_COMMON_OPTION, 0x00000001},
{"ARM_CPU_L2_0_CONFIGURATION", ARM_CPU_L2_0_CONFIGURATION, 0x00000003},
{"ARM_CPU_L2_0_STATUS", ARM_CPU_L2_0_STATUS, 0x00000003},
{"ARM_CPU_L2_1_CONFIGURATION", ARM_CPU_L2_1_CONFIGURATION, 0x00000003},
{"ARM_CPU_L2_1_STATUS", ARM_CPU_L2_1_STATUS, 0x00000003},
{"PAD_RETENTION_MAUDIO_OPTION", PAD_RETENTION_MAUDIO_OPTION, 0x00000000},
{"PAD_RETENTION_GPIO_OPTION", PAD_RETENTION_GPIO_OPTION, 0x00000000},
{"PAD_RETENTION_UART_OPTION", PAD_RETENTION_UART_OPTION, 0x00000000},
{"PAD_RETENTION_MMCA_OPTION", PAD_RETENTION_MMCA_OPTION, 0x00000000},
{"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000},
{"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000},
{"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000},
{"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200},
{"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001},
{"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001},
{"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000},
{"XXTI_CONFIGURATION", XXTI_CONFIGURATION, 0x00000001},
{"XXTI_STATUS", XXTI_STATUS, 0x00000001},
{"XXTI_DURATION", XXTI_DURATION, 0xFFF00000},
{"EXT_REGULATOR_DURATION", EXT_REGULATOR_DURATION, 0xFFF03FFF},
{"CAM_CONFIGURATION", CAM_CONFIGURATION, 0x00000007},
{"CAM_STATUS", CAM_STATUS, 0x00060007},
{"CAM_OPTION", CAM_OPTION, 0x00000001},
{"TV_CONFIGURATION", TV_CONFIGURATION, 0x00000007},
{"TV_STATUS", TV_STATUS, 0x00060007},
{"TV_OPTION", TV_OPTION, 0x00000001},
{"MFC_CONFIGURATION", MFC_CONFIGURATION, 0x00000007},
{"MFC_STATUS", MFC_STATUS, 0x00060007},
{"MFC_OPTION", MFC_OPTION, 0x00000001},
{"G3D_CONFIGURATION", G3D_CONFIGURATION, 0x00000007},
{"G3D_STATUS", G3D_STATUS, 0x00060007},
{"G3D_OPTION", G3D_OPTION, 0x00000001},
{"LCD0_CONFIGURATION", LCD0_CONFIGURATION, 0x00000007},
{"LCD0_STATUS", LCD0_STATUS, 0x00060007},
{"LCD0_OPTION", LCD0_OPTION, 0x00000001},
{"LCD1_CONFIGURATION", LCD1_CONFIGURATION, 0x00000007},
{"LCD1_STATUS", LCD1_STATUS, 0x00060007},
{"LCD1_OPTION", LCD1_OPTION, 0x00000001},
{"GPS_CONFIGURATION", GPS_CONFIGURATION, 0x00000007},
{"GPS_STATUS", GPS_STATUS, 0x00060007},
{"GPS_OPTION", GPS_OPTION, 0x00000001},
{"GPS_ALIVE_CONFIGURATION", GPS_ALIVE_CONFIGURATION, 0x00000007},
{"GPS_ALIVE_STATUS", GPS_ALIVE_STATUS, 0x00060007},
{"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001},
};
#define PMU_NUM_OF_REGISTERS \
(sizeof(exynos4210_pmu_regs) / sizeof(Exynos4210PmuReg))
typedef struct Exynos4210PmuState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t reg[PMU_NUM_OF_REGISTERS];
} Exynos4210PmuState;
static uint64_t exynos4210_pmu_read(void *opaque, target_phys_addr_t offset,
unsigned size)
{
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
unsigned i;
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
if (reg_p->offset == offset) {
PRINT_DEBUG_EXTEND("%s [0x%04x] -> 0x%04x\n", reg_p->name,
(uint32_t)offset, s->reg[i]);
return s->reg[i];
}
reg_p++;
}
PRINT_DEBUG("QEMU PMU ERROR: bad read offset 0x%04x\n", (uint32_t)offset);
return 0;
}
static void exynos4210_pmu_write(void *opaque, target_phys_addr_t offset,
uint64_t val, unsigned size)
{
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
unsigned i;
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
if (reg_p->offset == offset) {
PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name,
(uint32_t)offset, (uint32_t)val);
s->reg[i] = val;
return;
}
reg_p++;
}
PRINT_DEBUG("QEMU PMU ERROR: bad write offset 0x%04x\n", (uint32_t)offset);
}
static const MemoryRegionOps exynos4210_pmu_ops = {
.read = exynos4210_pmu_read,
.write = exynos4210_pmu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false
}
};
static void exynos4210_pmu_reset(DeviceState *dev)
{
Exynos4210PmuState *s =
container_of(dev, Exynos4210PmuState, busdev.qdev);
unsigned i;
/* Set default values for registers */
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
s->reg[i] = exynos4210_pmu_regs[i].reset_value;
}
}
static int exynos4210_pmu_init(SysBusDevice *dev)
{
Exynos4210PmuState *s = FROM_SYSBUS(Exynos4210PmuState, dev);
/* memory mapping */
memory_region_init_io(&s->iomem, &exynos4210_pmu_ops, s, "exynos4210.pmu",
EXYNOS4210_PMU_REGS_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static const VMStateDescription exynos4210_pmu_vmstate = {
.name = "exynos4210.pmu",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS),
VMSTATE_END_OF_LIST()
}
};
static void exynos4210_pmu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_pmu_init;
dc->reset = exynos4210_pmu_reset;
dc->vmsd = &exynos4210_pmu_vmstate;
}
static TypeInfo exynos4210_pmu_info = {
.name = "exynos4210.pmu",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210PmuState),
.class_init = exynos4210_pmu_class_init,
};
static void exynos4210_pmu_register(void)
{
type_register_static(&exynos4210_pmu_info);
}
type_init(exynos4210_pmu_register)

422
hw/exynos4210_pwm.c Normal file
View file

@ -0,0 +1,422 @@
/*
* Samsung exynos4210 Pulse Width Modulation Timer
*
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Evgeny Voevodin <e.voevodin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "sysbus.h"
#include "qemu-timer.h"
#include "qemu-common.h"
#include "ptimer.h"
#include "exynos4210.h"
//#define DEBUG_PWM
#ifdef DEBUG_PWM
#define DPRINTF(fmt, ...) \
do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while (0)
#endif
#define EXYNOS4210_PWM_TIMERS_NUM 5
#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
#define TCFG0 0x0000
#define TCFG1 0x0004
#define TCON 0x0008
#define TCNTB0 0x000C
#define TCMPB0 0x0010
#define TCNTO0 0x0014
#define TCNTB1 0x0018
#define TCMPB1 0x001C
#define TCNTO1 0x0020
#define TCNTB2 0x0024
#define TCMPB2 0x0028
#define TCNTO2 0x002C
#define TCNTB3 0x0030
#define TCMPB3 0x0034
#define TCNTO3 0x0038
#define TCNTB4 0x003C
#define TCNTO4 0x0040
#define TINT_CSTAT 0x0044
#define TCNTB(x) (0xC * (x))
#define TCMPB(x) (0xC * (x) + 1)
#define TCNTO(x) (0xC * (x) + 2)
#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
/*
* Attention! Timer4 doesn't have OUTPUT_INVERTER,
* so Auto Reload bit is not accessible by macros!
*/
#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
#define TCON_TIMER4_AUTO_RELOAD (1 << 22)
#define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
#define TINT_CSTAT_ENABLE(x) (1 << (x))
/* timer struct */
typedef struct {
uint32_t id; /* timer id */
qemu_irq irq; /* local timer irq */
uint32_t freq; /* timer frequency */
/* use ptimer.c to represent count down timer */
ptimer_state *ptimer; /* timer */
/* registers */
uint32_t reg_tcntb; /* counter register buffer */
uint32_t reg_tcmpb; /* compare register buffer */
struct Exynos4210PWMState *parent;
} Exynos4210PWM;
typedef struct Exynos4210PWMState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t reg_tcfg[2];
uint32_t reg_tcon;
uint32_t reg_tint_cstat;
Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
} Exynos4210PWMState;
/*** VMState ***/
static const VMStateDescription vmstate_exynos4210_pwm = {
.name = "exynos4210.pwm.pwm",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(id, Exynos4210PWM),
VMSTATE_UINT32(freq, Exynos4210PWM),
VMSTATE_PTIMER(ptimer, Exynos4210PWM),
VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_exynos4210_pwm_state = {
.name = "exynos4210.pwm",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
EXYNOS4210_PWM_TIMERS_NUM, 0,
vmstate_exynos4210_pwm, Exynos4210PWM),
VMSTATE_END_OF_LIST()
}
};
/*
* PWM update frequency
*/
static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
{
uint32_t freq;
freq = s->timer[id].freq;
if (id > 1) {
s->timer[id].freq = 24000000 /
((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
(GET_DIVIDER(s->reg_tcfg[1], id)));
} else {
s->timer[id].freq = 24000000 /
((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
(GET_DIVIDER(s->reg_tcfg[1], id)));
}
if (freq != s->timer[id].freq) {
ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
DPRINTF("freq=%dHz\n", s->timer[id].freq);
}
}
/*
* Counter tick handler
*/
static void exynos4210_pwm_tick(void *opaque)
{
Exynos4210PWM *s = (Exynos4210PWM *)opaque;
Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
uint32_t id = s->id;
bool cmp;
DPRINTF("timer %d tick\n", id);
/* set irq status */
p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
/* raise IRQ */
if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
DPRINTF("timer %d IRQ\n", id);
qemu_irq_raise(p->timer[id].irq);
}
/* reload timer */
if (id != 4) {
cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
} else {
cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
}
if (cmp) {
DPRINTF("auto reload timer %d count to %x\n", id,
p->timer[id].reg_tcntb);
ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
ptimer_run(p->timer[id].ptimer, 1);
} else {
/* stop timer, set status to STOP, see Basic Timer Operation */
p->reg_tcon = ~TCON_TIMER_START(id);
ptimer_stop(p->timer[id].ptimer);
}
}
/*
* PWM Read
*/
static uint64_t exynos4210_pwm_read(void *opaque, target_phys_addr_t offset,
unsigned size)
{
Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
uint32_t value = 0;
int index;
switch (offset) {
case TCFG0: case TCFG1:
index = (offset - TCFG0) >> 2;
value = s->reg_tcfg[index];
break;
case TCON:
value = s->reg_tcon;
break;
case TCNTB0: case TCNTB1:
case TCNTB2: case TCNTB3: case TCNTB4:
index = (offset - TCNTB0) / 0xC;
value = s->timer[index].reg_tcntb;
break;
case TCMPB0: case TCMPB1:
case TCMPB2: case TCMPB3:
index = (offset - TCMPB0) / 0xC;
value = s->timer[index].reg_tcmpb;
break;
case TCNTO0: case TCNTO1:
case TCNTO2: case TCNTO3: case TCNTO4:
index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
value = ptimer_get_count(s->timer[index].ptimer);
break;
case TINT_CSTAT:
value = s->reg_tint_cstat;
break;
default:
fprintf(stderr,
"[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
offset);
break;
}
return value;
}
/*
* PWM Write
*/
static void exynos4210_pwm_write(void *opaque, target_phys_addr_t offset,
uint64_t value, unsigned size)
{
Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
int index;
uint32_t new_val;
int i;
switch (offset) {
case TCFG0: case TCFG1:
index = (offset - TCFG0) >> 2;
s->reg_tcfg[index] = value;
/* update timers frequencies */
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
exynos4210_pwm_update_freq(s, s->timer[i].id);
}
break;
case TCON:
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
if ((value & TCON_TIMER_MANUAL_UPD(i)) >
(s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
/*
* TCNTB and TCMPB are loaded into TCNT and TCMP.
* Update timers.
*/
/* this will start timer to run, this ok, because
* during processing start bit timer will be stopped
* if needed */
ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
DPRINTF("set timer %d count to %x\n", i,
s->timer[i].reg_tcntb);
}
if ((value & TCON_TIMER_START(i)) >
(s->reg_tcon & TCON_TIMER_START(i))) {
/* changed to start */
ptimer_run(s->timer[i].ptimer, 1);
DPRINTF("run timer %d\n", i);
}
if ((value & TCON_TIMER_START(i)) <
(s->reg_tcon & TCON_TIMER_START(i))) {
/* changed to stop */
ptimer_stop(s->timer[i].ptimer);
DPRINTF("stop timer %d\n", i);
}
}
s->reg_tcon = value;
break;
case TCNTB0: case TCNTB1:
case TCNTB2: case TCNTB3: case TCNTB4:
index = (offset - TCNTB0) / 0xC;
s->timer[index].reg_tcntb = value;
break;
case TCMPB0: case TCMPB1:
case TCMPB2: case TCMPB3:
index = (offset - TCMPB0) / 0xC;
s->timer[index].reg_tcmpb = value;
break;
case TINT_CSTAT:
new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
new_val &= ~(0x3E0 & value);
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
if ((new_val & TINT_CSTAT_STATUS(i)) <
(s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
qemu_irq_lower(s->timer[i].irq);
}
}
s->reg_tint_cstat = new_val;
break;
default:
fprintf(stderr,
"[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
offset);
break;
}
}
/*
* Set default values to timer fields and registers
*/
static void exynos4210_pwm_reset(DeviceState *d)
{
Exynos4210PWMState *s = (Exynos4210PWMState *)d;
int i;
s->reg_tcfg[0] = 0x0101;
s->reg_tcfg[1] = 0x0;
s->reg_tcon = 0;
s->reg_tint_cstat = 0;
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
s->timer[i].reg_tcmpb = 0;
s->timer[i].reg_tcntb = 0;
exynos4210_pwm_update_freq(s, s->timer[i].id);
ptimer_stop(s->timer[i].ptimer);
}
}
static const MemoryRegionOps exynos4210_pwm_ops = {
.read = exynos4210_pwm_read,
.write = exynos4210_pwm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* PWM timer initialization
*/
static int exynos4210_pwm_init(SysBusDevice *dev)
{
Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
int i;
QEMUBH *bh;
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
sysbus_init_irq(dev, &s->timer[i].irq);
s->timer[i].ptimer = ptimer_init(bh);
s->timer[i].id = i;
s->timer[i].parent = s;
}
memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
EXYNOS4210_PWM_REG_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_pwm_init;
dc->reset = exynos4210_pwm_reset;
dc->vmsd = &vmstate_exynos4210_pwm_state;
}
static TypeInfo exynos4210_pwm_info = {
.name = "exynos4210.pwm",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210PWMState),
.class_init = exynos4210_pwm_class_init,
};
static void exynos4210_pwm_register_types(void)
{
type_register_static(&exynos4210_pwm_info);
}
type_init(exynos4210_pwm_register_types)

676
hw/exynos4210_uart.c Normal file
View file

@ -0,0 +1,676 @@
/*
* Exynos4210 UART Emulation
*
* Copyright (C) 2011 Samsung Electronics Co Ltd.
* Maksim Kozlov, <m.kozlov@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "sysbus.h"
#include "sysemu.h"
#include "qemu-char.h"
#include "exynos4210.h"
#undef DEBUG_UART
#undef DEBUG_UART_EXTEND
#undef DEBUG_IRQ
#undef DEBUG_Rx_DATA
#undef DEBUG_Tx_DATA
#define DEBUG_UART 0
#define DEBUG_UART_EXTEND 0
#define DEBUG_IRQ 0
#define DEBUG_Rx_DATA 0
#define DEBUG_Tx_DATA 0
#if DEBUG_UART
#define PRINT_DEBUG(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
} while (0)
#if DEBUG_UART_EXTEND
#define PRINT_DEBUG_EXTEND(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
} while (0)
#else
#define PRINT_DEBUG_EXTEND(fmt, args...) \
do {} while (0)
#endif /* EXTEND */
#else
#define PRINT_DEBUG(fmt, args...) \
do {} while (0)
#define PRINT_DEBUG_EXTEND(fmt, args...) \
do {} while (0)
#endif
#define PRINT_ERROR(fmt, args...) \
do { \
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
} while (0)
/*
* 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 /* UART 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 Exynos4210UartReg {
const char *name; /* the only reason is the debug output */
target_phys_addr_t offset;
uint32_t reset_value;
} Exynos4210UartReg;
static Exynos4210UartReg exynos4210_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 EXYNOS4210_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
/* UART Tx/Rx Status */
#define UTRSTAT_TRANSMITTER_EMPTY 0x4
#define UTRSTAT_Tx_BUFFER_EMPTY 0x2
#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1
/* UART Error Status */
#define UERSTAT_OVERRUN 0x1
#define UERSTAT_PARITY 0x2
#define UERSTAT_FRAME 0x4
#define UERSTAT_BREAK 0x8
typedef struct {
uint8_t *data;
uint32_t sp, rp; /* store and retrieve pointers */
uint32_t size;
} Exynos4210UartFIFO;
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
Exynos4210UartFIFO rx;
Exynos4210UartFIFO tx;
CharDriverState *chr;
qemu_irq irq;
uint32_t channel;
} Exynos4210UartState;
#if DEBUG_UART
/* Used only for debugging inside PRINT_DEBUG_... macros */
static const char *exynos4210_uart_regname(target_phys_addr_t offset)
{
int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg);
int i;
for (i = 0; i < regs_number; i++) {
if (offset == exynos4210_uart_regs[i].offset) {
return exynos4210_uart_regs[i].name;
}
}
return NULL;
}
#endif
static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch)
{
q->data[q->sp] = ch;
q->sp = (q->sp + 1) % q->size;
}
static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
{
uint8_t ret = q->data[q->rp];
q->rp = (q->rp + 1) % q->size;
return ret;
}
static int fifo_elements_number(Exynos4210UartFIFO *q)
{
if (q->sp < q->rp) {
return q->size - q->rp + q->sp;
}
return q->sp - q->rp;
}
static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
{
return q->size - fifo_elements_number(q);
}
static void fifo_reset(Exynos4210UartFIFO *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 exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *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 channel number: %d\n", s->channel);
}
return level;
}
static void exynos4210_uart_update_irq(Exynos4210UartState *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 <= exynos4210_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);
#if DEBUG_IRQ
fprintf(stderr, "UART%d: IRQ has been raised: %08x\n",
s->channel, s->reg[I_(UINTP)]);
#endif
} else {
qemu_irq_lower(s->irq);
}
}
static void exynos4210_uart_update_parameters(Exynos4210UartState *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;
uclk_rate = 24000000;
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 exynos4210_uart_write(void *opaque, target_phys_addr_t offset,
uint64_t val, unsigned size)
{
Exynos4210UartState *s = (Exynos4210UartState *)opaque;
uint8_t ch;
PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel,
offset, exynos4210_uart_regname(offset), (long long unsigned int)val);
switch (offset) {
case ULCON:
case UBRDIV:
case UFRACVAL:
s->reg[I_(offset)] = val;
exynos4210_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);
#if 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;
exynos4210_uart_update_irq(s);
}
break;
case UINTP:
s->reg[I_(UINTP)] &= ~val;
s->reg[I_(UINTSP)] &= ~val;
PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
s->channel, offset, s->reg[I_(UINTP)]);
exynos4210_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, exynos4210_uart_regname(offset), offset);
break;
case UINTSP:
s->reg[I_(UINTSP)] &= ~val;
break;
case UINTM:
s->reg[I_(UINTM)] = val;
exynos4210_uart_update_irq(s);
break;
case UCON:
case UMCON:
default:
s->reg[I_(offset)] = val;
break;
}
}
static uint64_t exynos4210_uart_read(void *opaque, target_phys_addr_t offset,
unsigned size)
{
Exynos4210UartState *s = (Exynos4210UartState *)opaque;
uint32_t res;
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);
#if 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;
exynos4210_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, exynos4210_uart_regname(offset), offset);
break;
default:
return s->reg[I_(offset)];
}
return 0;
}
static const MemoryRegionOps exynos4210_uart_ops = {
.read = exynos4210_uart_read,
.write = exynos4210_uart_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.max_access_size = 4,
.unaligned = false
},
};
static int exynos4210_uart_can_receive(void *opaque)
{
Exynos4210UartState *s = (Exynos4210UartState *)opaque;
return fifo_empty_elements_number(&s->rx);
}
static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size)
{
Exynos4210UartState *s = (Exynos4210UartState *)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: Around here we maybe should check Rx 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;
}
exynos4210_uart_update_irq(s);
}
static void exynos4210_uart_event(void *opaque, int event)
{
Exynos4210UartState *s = (Exynos4210UartState *)opaque;
if (event == CHR_EVENT_BREAK) {
/* When the RxDn is held in logic 0, then a null byte is pushed into the
* fifo */
fifo_store(&s->rx, '\0');
s->reg[I_(UERSTAT)] |= UERSTAT_BREAK;
exynos4210_uart_update_irq(s);
}
}
static void exynos4210_uart_reset(DeviceState *dev)
{
Exynos4210UartState *s =
container_of(dev, Exynos4210UartState, busdev.qdev);
int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
int i;
for (i = 0; i < regs_number; i++) {
s->reg[I_(exynos4210_uart_regs[i].offset)] =
exynos4210_uart_regs[i].reset_value;
}
fifo_reset(&s->rx);
fifo_reset(&s->tx);
PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
}
static const VMStateDescription vmstate_exynos4210_uart_fifo = {
.name = "exynos4210.uart.fifo",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(sp, Exynos4210UartFIFO),
VMSTATE_UINT32(rp, Exynos4210UartFIFO),
VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_exynos4210_uart = {
.name = "exynos4210.uart",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState,
EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
VMSTATE_END_OF_LIST()
}
};
DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
int fifo_size,
int channel,
CharDriverState *chr,
qemu_irq irq)
{
DeviceState *dev;
SysBusDevice *bus;
const char chr_name[] = "serial";
char label[ARRAY_SIZE(chr_name) + 1];
dev = qdev_create(NULL, "exynos4210.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) {
snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
chr = qemu_chr_new(label, "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);
qdev_prop_set_uint32(dev, "rx-size", fifo_size);
qdev_prop_set_uint32(dev, "tx-size", fifo_size);
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);
return dev;
}
static int exynos4210_uart_init(SysBusDevice *dev)
{
Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev);
/* memory mapping */
memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart",
EXYNOS4210_UART_REGS_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
sysbus_init_irq(dev, &s->irq);
qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive,
exynos4210_uart_receive, exynos4210_uart_event, s);
return 0;
}
static Property exynos4210_uart_properties[] = {
DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16),
DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16),
DEFINE_PROP_END_OF_LIST(),
};
static void exynos4210_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_uart_init;
dc->reset = exynos4210_uart_reset;
dc->props = exynos4210_uart_properties;
dc->vmsd = &vmstate_exynos4210_uart;
}
static TypeInfo exynos4210_uart_info = {
.name = "exynos4210.uart",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210UartState),
.class_init = exynos4210_uart_class_init,
};
static void exynos4210_uart_register(void)
{
type_register_static(&exynos4210_uart_info);
}
type_init(exynos4210_uart_register)

177
hw/exynos4_boards.c Normal file
View file

@ -0,0 +1,177 @@
/*
* Samsung exynos4 SoC based boards 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "sysemu.h"
#include "sysbus.h"
#include "net.h"
#include "arm-misc.h"
#include "exec-memory.h"
#include "exynos4210.h"
#include "boards.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 SMDK_LAN9118_BASE_ADDR 0x05000000
typedef enum Exynos4BoardType {
EXYNOS4_BOARD_NURI,
EXYNOS4_BOARD_SMDKC210,
EXYNOS4_NUM_OF_BOARDS
} Exynos4BoardType;
static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
[EXYNOS4_BOARD_NURI] = 0xD33,
[EXYNOS4_BOARD_SMDKC210] = 0xB16,
};
static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = {
[EXYNOS4_BOARD_NURI] = EXYNOS4210_SECOND_CPU_BOOTREG,
[EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG,
};
static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = {
[EXYNOS4_BOARD_NURI] = 0x40000000,
[EXYNOS4_BOARD_SMDKC210] = 0x40000000,
};
static struct arm_boot_info exynos4_board_binfo = {
.loader_start = EXYNOS4210_BASE_BOOT_ADDR,
.smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR,
.nb_cpus = EXYNOS4210_NCPUS,
};
static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
static void lan9215_init(uint32_t base, qemu_irq irq)
{
DeviceState *dev;
SysBusDevice *s;
/* This should be a 9215 but the 9118 is close enough */
if (nd_table[0].vlan) {
qemu_check_nic_model(&nd_table[0], "lan9118");
dev = qdev_create(NULL, "lan9118");
qdev_set_nic_properties(dev, &nd_table[0]);
qdev_prop_set_uint32(dev, "mode_16bit", 1);
qdev_init_nofail(dev);
s = sysbus_from_qdev(dev);
sysbus_mmio_map(s, 0, base);
sysbus_connect_irq(s, 0, irq);
}
}
static Exynos4210State *exynos4_boards_init_common(
const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
Exynos4BoardType board_type)
{
if (smp_cpus != EXYNOS4210_NCPUS) {
fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
" value.\n",
exynos4_machines[board_type].name,
exynos4_machines[board_type].max_cpus);
}
exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
exynos4_board_binfo.board_id = exynos4_board_id[board_type];
exynos4_board_binfo.smp_bootreg_addr =
exynos4_board_smp_bootreg_addr[board_type];
exynos4_board_binfo.kernel_filename = kernel_filename;
exynos4_board_binfo.initrd_filename = initrd_filename;
exynos4_board_binfo.kernel_cmdline = kernel_cmdline;
exynos4_board_binfo.gic_cpu_if_addr =
EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100;
PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n"
" kernel_filename: %s\n"
" kernel_cmdline: %s\n"
" initrd_filename: %s\n",
exynos4_board_ram_size[board_type] / 1048576,
exynos4_board_ram_size[board_type],
kernel_filename,
kernel_cmdline,
initrd_filename);
return exynos4210_init(get_system_memory(),
exynos4_board_ram_size[board_type]);
}
static void 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)
{
exynos4_boards_init_common(kernel_filename, kernel_cmdline,
initrd_filename, EXYNOS4_BOARD_NURI);
arm_load_kernel(first_cpu, &exynos4_board_binfo);
}
static void 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)
{
Exynos4210State *s = exynos4_boards_init_common(kernel_filename,
kernel_cmdline, initrd_filename, EXYNOS4_BOARD_SMDKC210);
lan9215_init(SMDK_LAN9118_BASE_ADDR,
qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
arm_load_kernel(first_cpu, &exynos4_board_binfo);
}
static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = {
[EXYNOS4_BOARD_NURI] = {
.name = "nuri",
.desc = "Samsung NURI board (Exynos4210)",
.init = nuri_init,
.max_cpus = EXYNOS4210_NCPUS,
},
[EXYNOS4_BOARD_SMDKC210] = {
.name = "smdkc210",
.desc = "Samsung SMDKC210 board (Exynos4210)",
.init = smdkc210_init,
.max_cpus = EXYNOS4210_NCPUS,
},
};
static void exynos4_machine_init(void)
{
qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]);
qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]);
}
machine_init(exynos4_machine_init);

View file

@ -19,7 +19,6 @@
#include "sysbus.h"
#include "arm-misc.h"
#include "primecell.h"
#include "devices.h"
#include "loader.h"
#include "net.h"

View file

@ -8,7 +8,6 @@
*/
#include "sysbus.h"
#include "primecell.h"
#include "devices.h"
#include "boards.h"
#include "arm-misc.h"

View file

@ -235,11 +235,21 @@ typedef struct {
int32_t rxp_offset;
int32_t rxp_size;
int32_t rxp_pad;
uint32_t write_word_prev_offset;
uint32_t write_word_n;
uint16_t write_word_l;
uint16_t write_word_h;
uint32_t read_word_prev_offset;
uint32_t read_word_n;
uint32_t read_long;
uint32_t mode_16bit;
} lan9118_state;
static const VMStateDescription vmstate_lan9118 = {
.name = "lan9118",
.version_id = 1,
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(timer, lan9118_state),
@ -294,6 +304,14 @@ static const VMStateDescription vmstate_lan9118 = {
VMSTATE_INT32(rxp_offset, lan9118_state),
VMSTATE_INT32(rxp_size, lan9118_state),
VMSTATE_INT32(rxp_pad, lan9118_state),
VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
VMSTATE_UINT32_V(read_long, lan9118_state, 2),
VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
VMSTATE_END_OF_LIST()
}
};
@ -390,7 +408,7 @@ static void lan9118_reset(DeviceState *d)
s->fifo_int = 0x48000000;
s->rx_cfg = 0;
s->tx_cfg = 0;
s->hw_cfg = 0x00050000;
s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
s->pmt_ctrl &= 0x45;
s->gpio_cfg = 0;
s->txp->fifo_used = 0;
@ -429,6 +447,9 @@ static void lan9118_reset(DeviceState *d)
s->mac_mii_data = 0;
s->mac_flow = 0;
s->read_word_n = 0;
s->write_word_n = 0;
phy_reset(s);
s->eeprom_writable = 0;
@ -984,7 +1005,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
{
lan9118_state *s = (lan9118_state *)opaque;
offset &= 0xff;
//DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
if (offset >= 0x20 && offset < 0x40) {
/* TX FIFO */
@ -1034,7 +1055,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
/* SRST */
lan9118_reset(&s->busdev.qdev);
} else {
s->hw_cfg = val & 0x003f300;
s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
}
break;
case CSR_RX_DP_CTRL:
@ -1113,6 +1134,46 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
lan9118_update(s);
}
static void lan9118_writew(void *opaque, target_phys_addr_t offset,
uint32_t val)
{
lan9118_state *s = (lan9118_state *)opaque;
offset &= 0xff;
if (s->write_word_prev_offset != (offset & ~0x3)) {
/* New offset, reset word counter */
s->write_word_n = 0;
s->write_word_prev_offset = offset & ~0x3;
}
if (offset & 0x2) {
s->write_word_h = val;
} else {
s->write_word_l = val;
}
//DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
s->write_word_n++;
if (s->write_word_n == 2) {
s->write_word_n = 0;
lan9118_writel(s, offset & ~3, s->write_word_l +
(s->write_word_h << 16), 4);
}
}
static void lan9118_16bit_mode_write(void *opaque, target_phys_addr_t offset,
uint64_t val, unsigned size)
{
switch (size) {
case 2:
return lan9118_writew(opaque, offset, (uint32_t)val);
case 4:
return lan9118_writel(opaque, offset, val, size);
}
hw_error("lan9118_write: Bad size 0x%x\n", size);
}
static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
unsigned size)
{
@ -1149,7 +1210,7 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
case CSR_TX_CFG:
return s->tx_cfg;
case CSR_HW_CFG:
return s->hw_cfg | 0x4;
return s->hw_cfg;
case CSR_RX_DP_CTRL:
return 0;
case CSR_RX_FIFO_INF:
@ -1187,12 +1248,60 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
return 0;
}
static uint32_t lan9118_readw(void *opaque, target_phys_addr_t offset)
{
lan9118_state *s = (lan9118_state *)opaque;
uint32_t val;
if (s->read_word_prev_offset != (offset & ~0x3)) {
/* New offset, reset word counter */
s->read_word_n = 0;
s->read_word_prev_offset = offset & ~0x3;
}
s->read_word_n++;
if (s->read_word_n == 1) {
s->read_long = lan9118_readl(s, offset & ~3, 4);
} else {
s->read_word_n = 0;
}
if (offset & 2) {
val = s->read_long >> 16;
} else {
val = s->read_long & 0xFFFF;
}
//DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
return val;
}
static uint64_t lan9118_16bit_mode_read(void *opaque, target_phys_addr_t offset,
unsigned size)
{
switch (size) {
case 2:
return lan9118_readw(opaque, offset);
case 4:
return lan9118_readl(opaque, offset, size);
}
hw_error("lan9118_read: Bad size 0x%x\n", size);
return 0;
}
static const MemoryRegionOps lan9118_mem_ops = {
.read = lan9118_readl,
.write = lan9118_writel,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps lan9118_16bit_mem_ops = {
.read = lan9118_16bit_mode_read,
.write = lan9118_16bit_mode_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void lan9118_cleanup(VLANClientState *nc)
{
lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
@ -1214,8 +1323,10 @@ static int lan9118_init1(SysBusDevice *dev)
lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
QEMUBH *bh;
int i;
const MemoryRegionOps *mem_ops =
s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
memory_region_init_io(&s->mmio, &lan9118_mem_ops, s, "lan9118-mmio", 0x100);
memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
sysbus_init_mmio(dev, &s->mmio);
sysbus_init_irq(dev, &s->irq);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
@ -1240,6 +1351,7 @@ static int lan9118_init1(SysBusDevice *dev)
static Property lan9118_properties[] = {
DEFINE_NIC_PROPERTIES(lan9118_state, conf),
DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
DEFINE_PROP_END_OF_LIST(),
};

View file

@ -9,7 +9,6 @@
#include "sysbus.h"
#include "ssi.h"
#include "primecell.h"
//#define DEBUG_PL022 1

View file

@ -76,7 +76,7 @@ static void pl031_interrupt(void * opaque)
{
pl031_state *s = (pl031_state *)opaque;
s->im = 1;
s->is = 1;
DPRINTF("Alarm raised\n");
pl031_update(s);
}

View file

@ -5,12 +5,6 @@
/* Also includes some devices that are currently only used by the
ARM boards. */
/* pl080.c */
void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
/* arm_sysctl.c */
void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id);
/* arm_sysctl GPIO lines */
#define ARM_SYSCTL_GPIO_MMC_WPROT 0
#define ARM_SYSCTL_GPIO_MMC_CARDIN 1

View file

@ -222,21 +222,23 @@ static void realview_init(ram_addr_t ram_size,
sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
if (is_mpcore) {
target_phys_addr_t periphbase;
dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore");
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
if (is_pb) {
realview_binfo.smp_priv_base = 0x1f000000;
periphbase = 0x1f000000;
} else {
realview_binfo.smp_priv_base = 0x10100000;
periphbase = 0x10100000;
}
sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base);
sysbus_mmio_map(busdev, 0, periphbase);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000,
NULL);
sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL);
/* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */
realview_binfo.gic_cpu_if_addr = periphbase + 0x100;
} else {
uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
/* For now just create the nIRQ GIC, and ignore the others. */

View file

@ -9,7 +9,6 @@
#include "sysbus.h"
#include "arm-misc.h"
#include "primecell.h"
#include "devices.h"
#include "net.h"
#include "sysemu.h"

View file

@ -30,42 +30,152 @@
#include "boards.h"
#include "exec-memory.h"
#define SMP_BOOT_ADDR 0xe0000000
#define SMP_BOOTREG_ADDR 0x10000030
#define VEXPRESS_BOARD_ID 0x8e0
static struct arm_boot_info vexpress_binfo = {
.smp_loader_start = SMP_BOOT_ADDR,
.smp_bootreg_addr = SMP_BOOTREG_ADDR,
static struct arm_boot_info vexpress_binfo;
/* Address maps for peripherals:
* the Versatile Express motherboard has two possible maps,
* the "legacy" one (used for A9) and the "Cortex-A Series"
* map (used for newer cores).
* Individual daughterboards can also have different maps for
* their peripherals.
*/
enum {
VE_SYSREGS,
VE_SP810,
VE_SERIALPCI,
VE_PL041,
VE_MMCI,
VE_KMI0,
VE_KMI1,
VE_UART0,
VE_UART1,
VE_UART2,
VE_UART3,
VE_WDT,
VE_TIMER01,
VE_TIMER23,
VE_SERIALDVI,
VE_RTC,
VE_COMPACTFLASH,
VE_CLCD,
VE_NORFLASH0,
VE_NORFLASH0ALIAS,
VE_NORFLASH1,
VE_SRAM,
VE_VIDEORAM,
VE_ETHERNET,
VE_USB,
VE_DAPROM,
};
static void vexpress_a9_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)
static target_phys_addr_t motherboard_legacy_map[] = {
/* CS7: 0x10000000 .. 0x10020000 */
[VE_SYSREGS] = 0x10000000,
[VE_SP810] = 0x10001000,
[VE_SERIALPCI] = 0x10002000,
[VE_PL041] = 0x10004000,
[VE_MMCI] = 0x10005000,
[VE_KMI0] = 0x10006000,
[VE_KMI1] = 0x10007000,
[VE_UART0] = 0x10009000,
[VE_UART1] = 0x1000a000,
[VE_UART2] = 0x1000b000,
[VE_UART3] = 0x1000c000,
[VE_WDT] = 0x1000f000,
[VE_TIMER01] = 0x10011000,
[VE_TIMER23] = 0x10012000,
[VE_SERIALDVI] = 0x10016000,
[VE_RTC] = 0x10017000,
[VE_COMPACTFLASH] = 0x1001a000,
[VE_CLCD] = 0x1001f000,
/* CS0: 0x40000000 .. 0x44000000 */
[VE_NORFLASH0] = 0x40000000,
/* CS1: 0x44000000 .. 0x48000000 */
[VE_NORFLASH1] = 0x44000000,
/* CS2: 0x48000000 .. 0x4a000000 */
[VE_SRAM] = 0x48000000,
/* CS3: 0x4c000000 .. 0x50000000 */
[VE_VIDEORAM] = 0x4c000000,
[VE_ETHERNET] = 0x4e000000,
[VE_USB] = 0x4f000000,
};
static target_phys_addr_t motherboard_aseries_map[] = {
/* CS0: 0x00000000 .. 0x0c000000 */
[VE_NORFLASH0] = 0x00000000,
[VE_NORFLASH0ALIAS] = 0x08000000,
/* CS4: 0x0c000000 .. 0x10000000 */
[VE_NORFLASH1] = 0x0c000000,
/* CS5: 0x10000000 .. 0x14000000 */
/* CS1: 0x14000000 .. 0x18000000 */
[VE_SRAM] = 0x14000000,
/* CS2: 0x18000000 .. 0x1c000000 */
[VE_VIDEORAM] = 0x18000000,
[VE_ETHERNET] = 0x1a000000,
[VE_USB] = 0x1b000000,
/* CS3: 0x1c000000 .. 0x20000000 */
[VE_DAPROM] = 0x1c000000,
[VE_SYSREGS] = 0x1c010000,
[VE_SP810] = 0x1c020000,
[VE_SERIALPCI] = 0x1c030000,
[VE_PL041] = 0x1c040000,
[VE_MMCI] = 0x1c050000,
[VE_KMI0] = 0x1c060000,
[VE_KMI1] = 0x1c070000,
[VE_UART0] = 0x1c090000,
[VE_UART1] = 0x1c0a0000,
[VE_UART2] = 0x1c0b0000,
[VE_UART3] = 0x1c0c0000,
[VE_WDT] = 0x1c0f0000,
[VE_TIMER01] = 0x1c110000,
[VE_TIMER23] = 0x1c120000,
[VE_SERIALDVI] = 0x1c160000,
[VE_RTC] = 0x1c170000,
[VE_COMPACTFLASH] = 0x1c1a0000,
[VE_CLCD] = 0x1c1f0000,
};
/* Structure defining the peculiarities of a specific daughterboard */
typedef struct VEDBoardInfo VEDBoardInfo;
typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
ram_addr_t ram_size,
const char *cpu_model,
qemu_irq *pic, uint32_t *proc_id);
struct VEDBoardInfo {
const target_phys_addr_t *motherboard_map;
target_phys_addr_t loader_start;
const target_phys_addr_t gic_cpu_if_addr;
DBoardInitFn *init;
};
static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
ram_addr_t ram_size,
const char *cpu_model,
qemu_irq *pic, uint32_t *proc_id)
{
CPUState *env = NULL;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *lowram = g_new(MemoryRegion, 1);
MemoryRegion *vram = g_new(MemoryRegion, 1);
MemoryRegion *sram = g_new(MemoryRegion, 1);
MemoryRegion *hackram = g_new(MemoryRegion, 1);
DeviceState *dev, *sysctl, *pl041;
DeviceState *dev;
SysBusDevice *busdev;
qemu_irq *irqp;
qemu_irq pic[64];
int n;
qemu_irq cpu_irq[4];
uint32_t proc_id;
uint32_t sys_id;
ram_addr_t low_ram_size, vram_size, sram_size;
ram_addr_t low_ram_size;
if (!cpu_model) {
cpu_model = "cortex-a9";
}
*proc_id = 0x0c000191;
for (n = 0; n < smp_cpus; n++) {
env = cpu_init(cpu_model);
if (!env) {
@ -78,7 +188,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
if (ram_size > 0x40000000) {
/* 1GB is the maximum the address space permits */
fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n");
fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n");
exit(1);
}
@ -101,8 +211,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
vexpress_binfo.smp_priv_base = 0x1e000000;
sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base);
sysbus_mmio_map(busdev, 0, 0x1e000000);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
@ -116,54 +225,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
pic[n] = qdev_get_gpio_in(dev, n);
}
/* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */
sys_id = 0x1190f500;
proc_id = 0x0c000191;
/* 0x10000000 System registers */
sysctl = qdev_create(NULL, "realview_sysctl");
qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
qdev_prop_set_uint32(sysctl, "proc_id", proc_id);
qdev_init_nofail(sysctl);
sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
/* 0x10001000 SP810 system control */
/* 0x10002000 serial bus PCI */
/* 0x10004000 PL041 audio */
pl041 = qdev_create(NULL, "pl041");
qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
qdev_init_nofail(pl041);
sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL);
/* Wire up MMC card detect and read-only signals */
qdev_connect_gpio_out(dev, 0,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
qdev_connect_gpio_out(dev, 1,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]);
sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]);
sysbus_create_simple("pl011", 0x10009000, pic[5]);
sysbus_create_simple("pl011", 0x1000a000, pic[6]);
sysbus_create_simple("pl011", 0x1000b000, pic[7]);
sysbus_create_simple("pl011", 0x1000c000, pic[8]);
/* 0x1000f000 SP805 WDT */
sysbus_create_simple("sp804", 0x10011000, pic[2]);
sysbus_create_simple("sp804", 0x10012000, pic[3]);
/* 0x10016000 Serial Bus DVI */
sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */
/* 0x1001a000 Compact Flash */
/* 0x1001f000 PL111 CLCD (motherboard) */
/* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
/* 0x10020000 PL111 CLCD (daughterboard) */
@ -183,37 +244,189 @@ static void vexpress_a9_init(ram_addr_t ram_size,
/* 0x10200000 CoreSight debug APB */
/* 0x1e00a000 PL310 L2 Cache Controller */
sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
}
static const VEDBoardInfo a9_daughterboard = {
.motherboard_map = motherboard_legacy_map,
.loader_start = 0x60000000,
.gic_cpu_if_addr = 0x1e000100,
.init = a9_daughterboard_init,
};
static void a15_daughterboard_init(const VEDBoardInfo *daughterboard,
ram_addr_t ram_size,
const char *cpu_model,
qemu_irq *pic, uint32_t *proc_id)
{
int n;
CPUState *env = NULL;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *sram = g_new(MemoryRegion, 1);
qemu_irq cpu_irq[4];
DeviceState *dev;
SysBusDevice *busdev;
if (!cpu_model) {
cpu_model = "cortex-a15";
}
*proc_id = 0x14000217;
for (n = 0; n < smp_cpus; n++) {
qemu_irq *irqp;
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
irqp = arm_pic_init_cpu(env);
cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
}
if (ram_size > 0x80000000) {
fprintf(stderr, "vexpress-a15: cannot model more than 2GB RAM\n");
exit(1);
}
memory_region_init_ram(ram, "vexpress.highmem", ram_size);
vmstate_register_ram_global(ram);
/* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */
memory_region_add_subregion(sysmem, 0x80000000, ram);
/* 0x2c000000 A15MPCore private memory region (GIC) */
dev = qdev_create(NULL, "a15mpcore_priv");
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
sysbus_mmio_map(busdev, 0, 0x2c000000);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
/* Interrupts [42:0] are from the motherboard;
* [47:43] are reserved; [63:48] are daughterboard
* peripherals. Note that some documentation numbers
* external interrupts starting from 32 (because there
* are internal interrupts 0..31).
*/
for (n = 0; n < 64; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
/* A15 daughterboard peripherals: */
/* 0x20000000: CoreSight interfaces: not modelled */
/* 0x2a000000: PL301 AXI interconnect: not modelled */
/* 0x2a420000: SCC: not modelled */
/* 0x2a430000: system counter: not modelled */
/* 0x2b000000: HDLCD controller: not modelled */
/* 0x2b060000: SP805 watchdog: not modelled */
/* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
/* 0x2e000000: system SRAM */
memory_region_init_ram(sram, "vexpress.a15sram", 0x10000);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0x2e000000, sram);
/* 0x7ffb0000: DMA330 DMA controller: not modelled */
/* 0x7ffd0000: PL354 static memory controller: not modelled */
}
static const VEDBoardInfo a15_daughterboard = {
.motherboard_map = motherboard_aseries_map,
.loader_start = 0x80000000,
.gic_cpu_if_addr = 0x2c002000,
.init = a15_daughterboard_init,
};
static void vexpress_common_init(const VEDBoardInfo *daughterboard,
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)
{
DeviceState *dev, *sysctl, *pl041;
qemu_irq pic[64];
uint32_t proc_id;
uint32_t sys_id;
ram_addr_t vram_size, sram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *vram = g_new(MemoryRegion, 1);
MemoryRegion *sram = g_new(MemoryRegion, 1);
const target_phys_addr_t *map = daughterboard->motherboard_map;
daughterboard->init(daughterboard, ram_size, cpu_model, pic, &proc_id);
/* Motherboard peripherals: the wiring is the same but the
* addresses vary between the legacy and A-Series memory maps.
*/
sys_id = 0x1190f500;
sysctl = qdev_create(NULL, "realview_sysctl");
qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
qdev_prop_set_uint32(sysctl, "proc_id", proc_id);
qdev_init_nofail(sysctl);
sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]);
/* VE_SP810: not modelled */
/* VE_SERIALPCI: not modelled */
pl041 = qdev_create(NULL, "pl041");
qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
qdev_init_nofail(pl041);
sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]);
sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL);
/* Wire up MMC card detect and read-only signals */
qdev_connect_gpio_out(dev, 0,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
qdev_connect_gpio_out(dev, 1,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]);
sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]);
sysbus_create_simple("pl011", map[VE_UART0], pic[5]);
sysbus_create_simple("pl011", map[VE_UART1], pic[6]);
sysbus_create_simple("pl011", map[VE_UART2], pic[7]);
sysbus_create_simple("pl011", map[VE_UART3], pic[8]);
sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
/* VE_SERIALDVI: not modelled */
sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */
/* VE_COMPACTFLASH: not modelled */
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
/* VE_NORFLASH0: not modelled */
/* VE_NORFLASH0ALIAS: not modelled */
/* VE_NORFLASH1: not modelled */
/* CS0: NOR0 flash : 0x40000000 .. 0x44000000 */
/* CS4: NOR1 flash : 0x44000000 .. 0x48000000 */
/* CS2: SRAM : 0x48000000 .. 0x4a000000 */
sram_size = 0x2000000;
memory_region_init_ram(sram, "vexpress.sram", sram_size);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0x48000000, sram);
memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
/* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */
/* 0x4c000000 Video RAM */
vram_size = 0x800000;
memory_region_init_ram(vram, "vexpress.vram", vram_size);
vmstate_register_ram_global(vram);
memory_region_add_subregion(sysmem, 0x4c000000, vram);
memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
/* 0x4e000000 LAN9118 Ethernet */
if (nd_table[0].vlan) {
lan9118_init(&nd_table[0], 0x4e000000, pic[15]);
lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]);
}
/* 0x4f000000 ISP1761 USB */
/* VE_USB: not modelled */
/* ??? Hack to map an additional page of ram for the secondary CPU
startup code. I guess this works on real hardware because the
BootROM happens to be in ROM/flash or in memory that isn't clobbered
until after Linux boots the secondary CPUs. */
memory_region_init_ram(hackram, "vexpress.hack", 0x1000);
vmstate_register_ram_global(hackram);
memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, hackram);
/* VE_DAPROM: not modelled */
vexpress_binfo.ram_size = ram_size;
vexpress_binfo.kernel_filename = kernel_filename;
@ -221,10 +434,36 @@ static void vexpress_a9_init(ram_addr_t ram_size,
vexpress_binfo.initrd_filename = initrd_filename;
vexpress_binfo.nb_cpus = smp_cpus;
vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
vexpress_binfo.loader_start = 0x60000000;
vexpress_binfo.loader_start = daughterboard->loader_start;
vexpress_binfo.smp_loader_start = map[VE_SRAM];
vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
arm_load_kernel(first_cpu, &vexpress_binfo);
}
static void vexpress_a9_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)
{
vexpress_common_init(&a9_daughterboard,
ram_size, boot_device, kernel_filename,
kernel_cmdline, initrd_filename, cpu_model);
}
static void vexpress_a15_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)
{
vexpress_common_init(&a15_daughterboard,
ram_size, boot_device, kernel_filename,
kernel_cmdline, initrd_filename, cpu_model);
}
static QEMUMachine vexpress_a9_machine = {
.name = "vexpress-a9",
@ -234,9 +473,18 @@ static QEMUMachine vexpress_a9_machine = {
.max_cpus = 4,
};
static QEMUMachine vexpress_a15_machine = {
.name = "vexpress-a15",
.desc = "ARM Versatile Express for Cortex-A15",
.init = vexpress_a15_init,
.use_scsi = 1,
.max_cpus = 4,
};
static void vexpress_machine_init(void)
{
qemu_register_machine(&vexpress_a9_machine);
qemu_register_machine(&vexpress_a15_machine);
}
machine_init(vexpress_machine_init);