mirror of https://gitee.com/openkylin/qemu.git
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:
commit
3d7f572140
14
MAINTAINERS
14
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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);
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "sysbus.h"
|
||||
#include "arm-misc.h"
|
||||
#include "primecell.h"
|
||||
#include "devices.h"
|
||||
#include "loader.h"
|
||||
#include "net.h"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
#include "sysbus.h"
|
||||
#include "primecell.h"
|
||||
#include "devices.h"
|
||||
#include "boards.h"
|
||||
#include "arm-misc.h"
|
||||
|
|
124
hw/lan9118.c
124
hw/lan9118.c
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "sysbus.h"
|
||||
#include "ssi.h"
|
||||
#include "primecell.h"
|
||||
|
||||
//#define DEBUG_PL022 1
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "sysbus.h"
|
||||
#include "arm-misc.h"
|
||||
#include "primecell.h"
|
||||
#include "devices.h"
|
||||
#include "net.h"
|
||||
#include "sysemu.h"
|
||||
|
|
422
hw/vexpress.c
422
hw/vexpress.c
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue