mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'pmaydell/tags/pull-target-arm-20131217' into staging
target-arm queue: * AES instruction support for 32 bit ARM * pflash01: much better emulation of 2x16bit and similar configs where multiple flash devices are banked together * fixed CBAR handling on Zynq, Highbank * initial AArch64 KVM control support * first two chunks of patches for A64 instruction emulation * new board: canon-a1100 (Canon DIGIC SoC) * new board: cubieboard (Allwinner A10 SoC) # gpg: Signature made Tue 17 Dec 2013 12:18:39 PM PST using RSA key ID 14360CDE # gpg: Can't check signature: public key not found # By Alexander Graf (14) and others # Via Peter Maydell * pmaydell/tags/pull-target-arm-20131217: (62 commits) MAINTAINERS: add myself to maintain allwinner-a10 hw/arm: add cubieboard support hw/arm: add allwinner a10 SoC support hw/intc: add allwinner A10 interrupt controller hw/timer: add allwinner a10 timer vmstate: Add support for an array of ptimer_state * MAINTAINERS: Document 'Canon DIGIC' machine hw/arm/digic: add NOR ROM support hw/arm/digic: add UART support hw/arm/digic: add timer support hw/arm/digic: prepare DIGIC-based boards support hw/arm: add very initial support for Canon DIGIC SoC target-arm: A64: add support for logical (immediate) insns target-arm: A64: add support for 1-src CLS insn host-utils: add clrsb32/64 - count leading redundant sign bits target-arm: A64: add support for bitfield insns target-arm: A64: add support for 1-src REV insns target-arm: A64: add support for 1-src RBIT insn target-arm: A64: add support for 1-src data processing and CLZ target-arm: A64: add support for 2-src shift reg insns ... Message-id: 1387312160-12318-1-git-send-email-peter.maydell@linaro.org Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
+13
@@ -219,6 +219,13 @@ F: *win32*
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
M: Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
S: Maintained
|
||||
F: hw/*/allwinner-a10*
|
||||
F: include/hw/*/allwinner-a10*
|
||||
F: hw/arm/cubieboard.c
|
||||
|
||||
Exynos
|
||||
M: Evgeny Voevodin <e.voevodin@samsung.com>
|
||||
M: Maksim Kozlov <m.kozlov@samsung.com>
|
||||
@@ -233,6 +240,12 @@ S: Supported
|
||||
F: hw/arm/highbank.c
|
||||
F: hw/net/xgmac.c
|
||||
|
||||
Canon DIGIC
|
||||
M: Antony Pavlov <antonynpavlov@gmail.com>
|
||||
S: Maintained
|
||||
F: include/hw/arm/digic.h
|
||||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
|
||||
@@ -4438,7 +4438,7 @@ case "$target_name" in
|
||||
aarch64)
|
||||
TARGET_BASE_ARCH=arm
|
||||
bflt="yes"
|
||||
gdb_xml_files="aarch64-core.xml"
|
||||
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml"
|
||||
;;
|
||||
cris)
|
||||
;;
|
||||
@@ -4550,7 +4550,7 @@ case "$target_name" in
|
||||
*)
|
||||
esac
|
||||
case "$target_name" in
|
||||
arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
# Make sure the target and host cpus are compatible
|
||||
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
|
||||
\( "$target_name" = "$cpu" -o \
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default configuration for aarch64-softmmu
|
||||
|
||||
# We support all the 32 bit boards so need all their config
|
||||
include arm-softmmu.mak
|
||||
|
||||
# Currently no 64-bit specific config requirements
|
||||
@@ -64,6 +64,7 @@ CONFIG_XILINX_SPIPS=y
|
||||
|
||||
CONFIG_ARM11SCU=y
|
||||
CONFIG_A9SCU=y
|
||||
CONFIG_DIGIC=y
|
||||
CONFIG_MARVELL_88W8618=y
|
||||
CONFIG_OMAP=y
|
||||
CONFIG_TSC210X=y
|
||||
@@ -82,3 +83,7 @@ CONFIG_VERSATILE_I2C=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
|
||||
CONFIG_ALLWINNER_A10_PIT=y
|
||||
CONFIG_ALLWINNER_A10_PIC=y
|
||||
CONFIG_ALLWINNER_A10=y
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2009-2012 Free Software Foundation, Inc.
|
||||
Contributed by ARM Ltd.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.aarch64.fpu">
|
||||
<vector id="v2d" type="ieee_double" count="2"/>
|
||||
<vector id="v2u" type="uint64" count="2"/>
|
||||
<vector id="v2i" type="int64" count="2"/>
|
||||
<vector id="v4f" type="ieee_single" count="4"/>
|
||||
<vector id="v4u" type="uint32" count="4"/>
|
||||
<vector id="v4i" type="int32" count="4"/>
|
||||
<vector id="v8u" type="uint16" count="8"/>
|
||||
<vector id="v8i" type="int16" count="8"/>
|
||||
<vector id="v16u" type="uint8" count="16"/>
|
||||
<vector id="v16i" type="int8" count="16"/>
|
||||
<vector id="v1u" type="uint128" count="1"/>
|
||||
<vector id="v1i" type="int128" count="1"/>
|
||||
<union id="vnd">
|
||||
<field name="f" type="v2d"/>
|
||||
<field name="u" type="v2u"/>
|
||||
<field name="s" type="v2i"/>
|
||||
</union>
|
||||
<union id="vns">
|
||||
<field name="f" type="v4f"/>
|
||||
<field name="u" type="v4u"/>
|
||||
<field name="s" type="v4i"/>
|
||||
</union>
|
||||
<union id="vnh">
|
||||
<field name="u" type="v8u"/>
|
||||
<field name="s" type="v8i"/>
|
||||
</union>
|
||||
<union id="vnb">
|
||||
<field name="u" type="v16u"/>
|
||||
<field name="s" type="v16i"/>
|
||||
</union>
|
||||
<union id="vnq">
|
||||
<field name="u" type="v1u"/>
|
||||
<field name="s" type="v1i"/>
|
||||
</union>
|
||||
<union id="aarch64v">
|
||||
<field name="d" type="vnd"/>
|
||||
<field name="s" type="vns"/>
|
||||
<field name="h" type="vnh"/>
|
||||
<field name="b" type="vnb"/>
|
||||
<field name="q" type="vnq"/>
|
||||
</union>
|
||||
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
|
||||
<reg name="v1" bitsize="128" type="aarch64v" />
|
||||
<reg name="v2" bitsize="128" type="aarch64v" />
|
||||
<reg name="v3" bitsize="128" type="aarch64v" />
|
||||
<reg name="v4" bitsize="128" type="aarch64v" />
|
||||
<reg name="v5" bitsize="128" type="aarch64v" />
|
||||
<reg name="v6" bitsize="128" type="aarch64v" />
|
||||
<reg name="v7" bitsize="128" type="aarch64v" />
|
||||
<reg name="v8" bitsize="128" type="aarch64v" />
|
||||
<reg name="v9" bitsize="128" type="aarch64v" />
|
||||
<reg name="v10" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v11" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v12" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v13" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v14" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v15" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v16" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v17" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v18" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v19" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v20" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v21" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v22" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v23" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v24" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v25" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v26" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v27" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v28" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v29" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v30" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v31" bitsize="128" type="aarch64v"/>
|
||||
<reg name="fpsr" bitsize="32"/>
|
||||
<reg name="fpcr" bitsize="32"/>
|
||||
</feature>
|
||||
@@ -1,7 +1,10 @@
|
||||
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
|
||||
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
|
||||
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
|
||||
|
||||
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_DIGIC) += digic.o
|
||||
obj-y += omap1.o omap2.o strongarm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Allwinner A10 SoC emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.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.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "hw/arm/allwinner-a10.h"
|
||||
|
||||
static void aw_a10_init(Object *obj)
|
||||
{
|
||||
AwA10State *s = AW_A10(obj);
|
||||
|
||||
object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
|
||||
|
||||
object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC);
|
||||
qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
|
||||
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
|
||||
}
|
||||
|
||||
static void aw_a10_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
AwA10State *s = AW_A10(dev);
|
||||
SysBusDevice *sysbusdev;
|
||||
uint8_t i;
|
||||
qemu_irq fiq, irq;
|
||||
Error *err = NULL;
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ);
|
||||
fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->intc), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbusdev = SYS_BUS_DEVICE(&s->intc);
|
||||
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE);
|
||||
sysbus_connect_irq(sysbusdev, 0, irq);
|
||||
sysbus_connect_irq(sysbusdev, 1, fiq);
|
||||
for (i = 0; i < AW_A10_PIC_INT_NR; i++) {
|
||||
s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbusdev = SYS_BUS_DEVICE(&s->timer);
|
||||
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE);
|
||||
sysbus_connect_irq(sysbusdev, 0, s->irq[22]);
|
||||
sysbus_connect_irq(sysbusdev, 1, s->irq[23]);
|
||||
sysbus_connect_irq(sysbusdev, 2, s->irq[24]);
|
||||
sysbus_connect_irq(sysbusdev, 3, s->irq[25]);
|
||||
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
|
||||
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
|
||||
|
||||
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
|
||||
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||
}
|
||||
|
||||
static void aw_a10_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = aw_a10_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_a10_type_info = {
|
||||
.name = TYPE_AW_A10,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(AwA10State),
|
||||
.instance_init = aw_a10_init,
|
||||
.class_init = aw_a10_class_init,
|
||||
};
|
||||
|
||||
static void aw_a10_register_types(void)
|
||||
{
|
||||
type_register_static(&aw_a10_type_info);
|
||||
}
|
||||
|
||||
type_init(aw_a10_register_types)
|
||||
+144
-49
@@ -17,18 +17,55 @@
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
/* Kernel boot protocol is specified in the kernel docs
|
||||
* Documentation/arm/Booting and Documentation/arm64/booting.txt
|
||||
* They have different preferred image load offsets from system RAM base.
|
||||
*/
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
#define KERNEL64_LOAD_ADDR 0x00080000
|
||||
|
||||
typedef enum {
|
||||
FIXUP_NONE = 0, /* do nothing */
|
||||
FIXUP_TERMINATOR, /* end of insns */
|
||||
FIXUP_BOARDID, /* overwrite with board ID number */
|
||||
FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
|
||||
FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
|
||||
FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
|
||||
FIXUP_BOOTREG, /* overwrite with boot register address */
|
||||
FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
|
||||
FIXUP_MAX,
|
||||
} FixupType;
|
||||
|
||||
typedef struct ARMInsnFixup {
|
||||
uint32_t insn;
|
||||
FixupType fixup;
|
||||
} ARMInsnFixup;
|
||||
|
||||
static const ARMInsnFixup bootloader_aarch64[] = {
|
||||
{ 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
|
||||
{ 0xaa1f03e1 }, /* mov x1, xzr */
|
||||
{ 0xaa1f03e2 }, /* mov x2, xzr */
|
||||
{ 0xaa1f03e3 }, /* mov x3, xzr */
|
||||
{ 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
|
||||
{ 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
|
||||
{ 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
|
||||
{ 0 }, /* .word @DTB Higher 32-bits */
|
||||
{ 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
|
||||
{ 0 }, /* .word @Kernel Entry Higher 32-bits */
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
|
||||
static uint32_t bootloader[] = {
|
||||
0xe3a00000, /* mov r0, #0 */
|
||||
0xe59f1004, /* ldr r1, [pc, #4] */
|
||||
0xe59f2004, /* ldr r2, [pc, #4] */
|
||||
0xe59ff004, /* ldr pc, [pc, #4] */
|
||||
0, /* Board ID */
|
||||
0, /* Address of kernel args. Set by integratorcp_init. */
|
||||
0 /* Kernel entry point. Set by integratorcp_init. */
|
||||
static const ARMInsnFixup bootloader[] = {
|
||||
{ 0xe3a00000 }, /* mov r0, #0 */
|
||||
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
|
||||
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
|
||||
{ 0xe59ff004 }, /* ldr pc, [pc, #4] */
|
||||
{ 0, FIXUP_BOARDID },
|
||||
{ 0, FIXUP_ARGPTR },
|
||||
{ 0, FIXUP_ENTRYPOINT },
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
/* Handling for secondary CPU boot in a multicore system.
|
||||
@@ -48,39 +85,83 @@ static uint32_t bootloader[] = {
|
||||
#define DSB_INSN 0xf57ff04f
|
||||
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
|
||||
|
||||
static uint32_t smpboot[] = {
|
||||
0xe59f2028, /* ldr r2, gic_cpu_if */
|
||||
0xe59f0028, /* ldr r0, startaddr */
|
||||
0xe3a01001, /* mov r1, #1 */
|
||||
0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */
|
||||
0xe3a010ff, /* mov r1, #0xff */
|
||||
0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
|
||||
DSB_INSN, /* dsb */
|
||||
0xe320f003, /* wfi */
|
||||
0xe5901000, /* ldr r1, [r0] */
|
||||
0xe1110001, /* tst r1, r1 */
|
||||
0x0afffffb, /* beq <wfi> */
|
||||
0xe12fff11, /* bx r1 */
|
||||
0, /* gic_cpu_if: base address of GIC CPU interface */
|
||||
0 /* bootreg: Boot register address is held here */
|
||||
static const ARMInsnFixup smpboot[] = {
|
||||
{ 0xe59f2028 }, /* ldr r2, gic_cpu_if */
|
||||
{ 0xe59f0028 }, /* ldr r0, bootreg_addr */
|
||||
{ 0xe3a01001 }, /* mov r1, #1 */
|
||||
{ 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
|
||||
{ 0xe3a010ff }, /* mov r1, #0xff */
|
||||
{ 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
|
||||
{ 0, FIXUP_DSB }, /* dsb */
|
||||
{ 0xe320f003 }, /* wfi */
|
||||
{ 0xe5901000 }, /* ldr r1, [r0] */
|
||||
{ 0xe1110001 }, /* tst r1, r1 */
|
||||
{ 0x0afffffb }, /* beq <wfi> */
|
||||
{ 0xe12fff11 }, /* bx r1 */
|
||||
{ 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
|
||||
{ 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
static void write_bootloader(const char *name, hwaddr addr,
|
||||
const ARMInsnFixup *insns, uint32_t *fixupcontext)
|
||||
{
|
||||
/* Fix up the specified bootloader fragment and write it into
|
||||
* guest memory using rom_add_blob_fixed(). fixupcontext is
|
||||
* an array giving the values to write in for the fixup types
|
||||
* which write a value into the code array.
|
||||
*/
|
||||
int i, len;
|
||||
uint32_t *code;
|
||||
|
||||
len = 0;
|
||||
while (insns[len].fixup != FIXUP_TERMINATOR) {
|
||||
len++;
|
||||
}
|
||||
|
||||
code = g_new0(uint32_t, len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint32_t insn = insns[i].insn;
|
||||
FixupType fixup = insns[i].fixup;
|
||||
|
||||
switch (fixup) {
|
||||
case FIXUP_NONE:
|
||||
break;
|
||||
case FIXUP_BOARDID:
|
||||
case FIXUP_ARGPTR:
|
||||
case FIXUP_ENTRYPOINT:
|
||||
case FIXUP_GIC_CPU_IF:
|
||||
case FIXUP_BOOTREG:
|
||||
case FIXUP_DSB:
|
||||
insn = fixupcontext[fixup];
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
code[i] = tswap32(insn);
|
||||
}
|
||||
|
||||
rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
|
||||
|
||||
g_free(code);
|
||||
}
|
||||
|
||||
static void default_write_secondary(ARMCPU *cpu,
|
||||
const struct arm_boot_info *info)
|
||||
{
|
||||
int n;
|
||||
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
|
||||
smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
|
||||
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
|
||||
/* Replace DSB with the pre-v7 DSB if necessary. */
|
||||
if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
|
||||
smpboot[n] == DSB_INSN) {
|
||||
smpboot[n] = CP15_DSB_INSN;
|
||||
}
|
||||
smpboot[n] = tswap32(smpboot[n]);
|
||||
uint32_t fixupcontext[FIXUP_MAX];
|
||||
|
||||
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
|
||||
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
|
||||
fixupcontext[FIXUP_DSB] = DSB_INSN;
|
||||
} else {
|
||||
fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
|
||||
}
|
||||
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start);
|
||||
|
||||
write_bootloader("smpboot", info->smp_loader_start,
|
||||
smpboot, fixupcontext);
|
||||
}
|
||||
|
||||
static void default_reset_secondary(ARMCPU *cpu,
|
||||
@@ -334,7 +415,12 @@ static void do_cpu_reset(void *opaque)
|
||||
env->thumb = info->entry & 1;
|
||||
} else {
|
||||
if (CPU(cpu) == first_cpu) {
|
||||
env->regs[15] = info->loader_start;
|
||||
if (env->aarch64) {
|
||||
env->pc = info->loader_start;
|
||||
} else {
|
||||
env->regs[15] = info->loader_start;
|
||||
}
|
||||
|
||||
if (!info->dtb_filename) {
|
||||
if (old_param) {
|
||||
set_kernel_args_old(info);
|
||||
@@ -354,11 +440,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
CPUState *cs = CPU(cpu);
|
||||
int kernel_size;
|
||||
int initrd_size;
|
||||
int n;
|
||||
int is_linux = 0;
|
||||
uint64_t elf_entry;
|
||||
hwaddr entry;
|
||||
hwaddr entry, kernel_load_offset;
|
||||
int big_endian;
|
||||
static const ARMInsnFixup *primary_loader;
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!info->kernel_filename) {
|
||||
@@ -368,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
return;
|
||||
}
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
primary_loader = bootloader_aarch64;
|
||||
kernel_load_offset = KERNEL64_LOAD_ADDR;
|
||||
} else {
|
||||
primary_loader = bootloader;
|
||||
kernel_load_offset = KERNEL_LOAD_ADDR;
|
||||
}
|
||||
|
||||
info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
|
||||
|
||||
if (!info->secondary_cpu_reset_hook) {
|
||||
@@ -408,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
&is_linux);
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
entry = info->loader_start + KERNEL_LOAD_ADDR;
|
||||
entry = info->loader_start + kernel_load_offset;
|
||||
kernel_size = load_image_targphys(info->kernel_filename, entry,
|
||||
info->ram_size - KERNEL_LOAD_ADDR);
|
||||
info->ram_size - kernel_load_offset);
|
||||
is_linux = 1;
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
@@ -420,6 +514,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
}
|
||||
info->entry = entry;
|
||||
if (is_linux) {
|
||||
uint32_t fixupcontext[FIXUP_MAX];
|
||||
|
||||
if (info->initrd_filename) {
|
||||
initrd_size = load_ramdisk(info->initrd_filename,
|
||||
info->initrd_start,
|
||||
@@ -441,7 +537,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
}
|
||||
info->initrd_size = initrd_size;
|
||||
|
||||
bootloader[4] = info->board_id;
|
||||
fixupcontext[FIXUP_BOARDID] = info->board_id;
|
||||
|
||||
/* for device tree boot, we pass the DTB directly in r2. Otherwise
|
||||
* we point to the kernel args.
|
||||
@@ -456,9 +552,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
if (load_dtb(dtb_start, info)) {
|
||||
exit(1);
|
||||
}
|
||||
bootloader[5] = dtb_start;
|
||||
fixupcontext[FIXUP_ARGPTR] = dtb_start;
|
||||
} else {
|
||||
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
if (info->ram_size >= (1ULL << 32)) {
|
||||
fprintf(stderr, "qemu: RAM size must be less than 4GB to boot"
|
||||
" Linux kernel using ATAGS (try passing a device tree"
|
||||
@@ -466,12 +562,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
bootloader[6] = entry;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++) {
|
||||
bootloader[n] = tswap32(bootloader[n]);
|
||||
}
|
||||
rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
|
||||
info->loader_start);
|
||||
fixupcontext[FIXUP_ENTRYPOINT] = entry;
|
||||
|
||||
write_bootloader("bootloader", info->loader_start,
|
||||
primary_loader, fixupcontext);
|
||||
|
||||
if (info->nb_cpus > 1) {
|
||||
info->write_secondary_boot(cpu, info);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* cubieboard emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.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.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/arm/allwinner-a10.h"
|
||||
|
||||
static struct arm_boot_info cubieboard_binfo = {
|
||||
.loader_start = AW_A10_SDRAM_BASE,
|
||||
.board_id = 0x1008,
|
||||
};
|
||||
|
||||
typedef struct CubieBoardState {
|
||||
AwA10State *a10;
|
||||
MemoryRegion sdram;
|
||||
} CubieBoardState;
|
||||
|
||||
static void cubieboard_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
CubieBoardState *s = g_new(CubieBoardState, 1);
|
||||
Error *err = NULL;
|
||||
|
||||
s->a10 = AW_A10(object_new(TYPE_AW_A10));
|
||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize Allwinner A10: %s\n",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size);
|
||||
vmstate_register_ram_global(&s->sdram);
|
||||
memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
|
||||
&s->sdram);
|
||||
|
||||
cubieboard_binfo.ram_size = args->ram_size;
|
||||
cubieboard_binfo.kernel_filename = args->kernel_filename;
|
||||
cubieboard_binfo.kernel_cmdline = args->kernel_cmdline;
|
||||
arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
|
||||
}
|
||||
|
||||
static QEMUMachine cubieboard_machine = {
|
||||
.name = "cubieboard",
|
||||
.desc = "cubietech cubieboard",
|
||||
.init = cubieboard_init,
|
||||
};
|
||||
|
||||
|
||||
static void cubieboard_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&cubieboard_machine);
|
||||
}
|
||||
|
||||
machine_init(cubieboard_machine_init)
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* QEMU model of the Canon DIGIC SoC.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/arm/digic.h"
|
||||
|
||||
#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100)
|
||||
|
||||
#define DIGIC_UART_BASE 0xc0800000
|
||||
|
||||
static void digic_init(Object *obj)
|
||||
{
|
||||
DigicState *s = DIGIC(obj);
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
|
||||
object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
|
||||
|
||||
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
|
||||
#define DIGIC_TIMER_NAME_MLEN 11
|
||||
char name[DIGIC_TIMER_NAME_MLEN];
|
||||
|
||||
object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER);
|
||||
dev = DEVICE(&s->timer[i]);
|
||||
qdev_set_parent_bus(dev, sysbus_get_default());
|
||||
snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL);
|
||||
}
|
||||
|
||||
object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART);
|
||||
dev = DEVICE(&s->uart);
|
||||
qdev_set_parent_bus(dev, sysbus_get_default());
|
||||
object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL);
|
||||
}
|
||||
|
||||
static void digic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
DigicState *s = DIGIC(dev);
|
||||
Error *err = NULL;
|
||||
SysBusDevice *sbd;
|
||||
int i;
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
|
||||
object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
sbd = SYS_BUS_DEVICE(&s->timer[i]);
|
||||
sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i));
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
sbd = SYS_BUS_DEVICE(&s->uart);
|
||||
sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE);
|
||||
}
|
||||
|
||||
static void digic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = digic_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_type_info = {
|
||||
.name = TYPE_DIGIC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(DigicState),
|
||||
.instance_init = digic_init,
|
||||
.class_init = digic_class_init,
|
||||
};
|
||||
|
||||
static void digic_register_types(void)
|
||||
{
|
||||
type_register_static(&digic_type_info);
|
||||
}
|
||||
|
||||
type_init(digic_register_types)
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* QEMU model of the Canon DIGIC boards (cameras indeed :).
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* See docs here:
|
||||
* http://magiclantern.wikia.com/wiki/Register_Map
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/digic.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/loader.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
#define DIGIC4_ROM0_BASE 0xf0000000
|
||||
#define DIGIC4_ROM1_BASE 0xf8000000
|
||||
#define DIGIC4_ROM_MAX_SIZE 0x08000000
|
||||
|
||||
typedef struct DigicBoardState {
|
||||
DigicState *digic;
|
||||
MemoryRegion ram;
|
||||
} DigicBoardState;
|
||||
|
||||
typedef struct DigicBoard {
|
||||
hwaddr ram_size;
|
||||
void (*add_rom0)(DigicBoardState *, hwaddr, const char *);
|
||||
const char *rom0_def_filename;
|
||||
void (*add_rom1)(DigicBoardState *, hwaddr, const char *);
|
||||
const char *rom1_def_filename;
|
||||
} DigicBoard;
|
||||
|
||||
static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size)
|
||||
{
|
||||
memory_region_init_ram(&s->ram, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), 0, &s->ram);
|
||||
vmstate_register_ram_global(&s->ram);
|
||||
}
|
||||
|
||||
static void digic4_board_init(DigicBoard *board)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
DigicBoardState *s = g_new(DigicBoardState, 1);
|
||||
|
||||
s->digic = DIGIC(object_new(TYPE_DIGIC));
|
||||
object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize DIGIC SoC: %s\n",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
digic4_board_setup_ram(s, board->ram_size);
|
||||
|
||||
if (board->add_rom0) {
|
||||
board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename);
|
||||
}
|
||||
|
||||
if (board->add_rom1) {
|
||||
board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename);
|
||||
}
|
||||
}
|
||||
|
||||
static void digic_load_rom(DigicBoardState *s, hwaddr addr,
|
||||
hwaddr max_size, const char *def_filename)
|
||||
{
|
||||
target_long rom_size;
|
||||
const char *filename;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* qtest runs no code so don't attempt a ROM load which
|
||||
* could fail and result in a spurious test failure.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (bios_name) {
|
||||
filename = bios_name;
|
||||
} else {
|
||||
filename = def_filename;
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename);
|
||||
|
||||
if (!fn) {
|
||||
error_report("Couldn't find rom image '%s'.\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rom_size = load_image_targphys(fn, addr, max_size);
|
||||
if (rom_size < 0 || rom_size > max_size) {
|
||||
error_report("Couldn't load rom image '%s'.\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Samsung K8P3215UQB
|
||||
* 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory
|
||||
*/
|
||||
static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr,
|
||||
const char *def_filename)
|
||||
{
|
||||
#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024)
|
||||
#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024)
|
||||
|
||||
pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE,
|
||||
NULL, FLASH_K8P3215UQB_SECTOR_SIZE,
|
||||
FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE,
|
||||
DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE,
|
||||
4,
|
||||
0x00EC, 0x007E, 0x0003, 0x0001,
|
||||
0x0555, 0x2aa, 0);
|
||||
|
||||
digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename);
|
||||
}
|
||||
|
||||
static DigicBoard digic4_board_canon_a1100 = {
|
||||
.ram_size = 64 * 1024 * 1024,
|
||||
.add_rom1 = digic4_add_k8p3215uqb_rom,
|
||||
.rom1_def_filename = "canon-a1100-rom1.bin",
|
||||
};
|
||||
|
||||
static void canon_a1100_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
digic4_board_init(&digic4_board_canon_a1100);
|
||||
}
|
||||
|
||||
static QEMUMachine canon_a1100 = {
|
||||
.name = "canon-a1100",
|
||||
.desc = "Canon PowerShot A1100 IS",
|
||||
.init = &canon_a1100_init,
|
||||
};
|
||||
|
||||
static void digic_register_machines(void)
|
||||
{
|
||||
qemu_register_machine(&canon_a1100);
|
||||
}
|
||||
|
||||
machine_init(digic_register_machines)
|
||||
+21
-12
@@ -26,12 +26,13 @@
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define SMP_BOOT_ADDR 0x100
|
||||
#define SMP_BOOT_REG 0x40
|
||||
#define GIC_BASE_ADDR 0xfff10000
|
||||
#define SMP_BOOT_ADDR 0x100
|
||||
#define SMP_BOOT_REG 0x40
|
||||
#define MPCORE_PERIPHBASE 0xfff10000
|
||||
|
||||
#define NIRQ_GIC 160
|
||||
#define NIRQ_GIC 160
|
||||
|
||||
/* Board init. */
|
||||
|
||||
@@ -54,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
0xe1110001, /* tst r1, r1 */
|
||||
0x0afffffb, /* beq <wfi> */
|
||||
0xe12fff11, /* bx r1 */
|
||||
GIC_BASE_ADDR /* privbase: gic address. */
|
||||
MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */
|
||||
};
|
||||
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
|
||||
smpboot[n] = tswap32(smpboot[n]);
|
||||
@@ -229,15 +230,23 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
|
||||
}
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
|
||||
ARMCPU *cpu;
|
||||
cpu = cpu_arm_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
Error *err = NULL;
|
||||
|
||||
cpu = ARM_CPU(object_new(object_class_get_name(oc)));
|
||||
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
|
||||
&err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* This will become a QOM property eventually */
|
||||
cpu->reset_cbar = GIC_BASE_ADDR;
|
||||
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
|
||||
}
|
||||
|
||||
@@ -279,7 +288,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
|
||||
qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
|
||||
qdev_init_nofail(dev);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR);
|
||||
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
sysbus_connect_irq(busdev, n, cpu_irq[n]);
|
||||
}
|
||||
|
||||
+34
-10
@@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Open code a private version of pflash registration since we
|
||||
* need to set non-default device width for VExpress platform.
|
||||
*/
|
||||
static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
|
||||
DriveInfo *di)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
||||
|
||||
if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
qdev_prop_set_uint32(dev, "num-blocks",
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE);
|
||||
qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE);
|
||||
qdev_prop_set_uint8(dev, "width", 4);
|
||||
qdev_prop_set_uint8(dev, "device-width", 2);
|
||||
qdev_prop_set_uint8(dev, "big-endian", 0);
|
||||
qdev_prop_set_uint16(dev, "id0", 0x89);
|
||||
qdev_prop_set_uint16(dev, "id1", 0x18);
|
||||
qdev_prop_set_uint16(dev, "id2", 0x00);
|
||||
qdev_prop_set_uint16(dev, "id3", 0x00);
|
||||
qdev_prop_set_string(dev, "name", name);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
||||
return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
|
||||
}
|
||||
|
||||
static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
||||
QEMUMachineInitArgs *args)
|
||||
{
|
||||
@@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
||||
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
|
||||
|
||||
dinfo = drive_get_next(IF_PFLASH);
|
||||
pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
|
||||
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
|
||||
VEXPRESS_FLASH_SECT_SIZE,
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
|
||||
0x00, 0x89, 0x00, 0x18, 0);
|
||||
pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0",
|
||||
dinfo);
|
||||
if (!pflash0) {
|
||||
fprintf(stderr, "vexpress: error registering flash 0.\n");
|
||||
exit(1);
|
||||
@@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
||||
}
|
||||
|
||||
dinfo = drive_get_next(IF_PFLASH);
|
||||
if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
|
||||
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
|
||||
VEXPRESS_FLASH_SECT_SIZE,
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
|
||||
0x00, 0x89, 0x00, 0x18, 0)) {
|
||||
if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1",
|
||||
dinfo)) {
|
||||
fprintf(stderr, "vexpress: error registering flash 1.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
+17
-4
@@ -25,6 +25,7 @@
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/ssi.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define NUM_SPI_FLASHES 4
|
||||
#define NUM_QSPI_FLASHES 2
|
||||
@@ -35,6 +36,8 @@
|
||||
|
||||
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
|
||||
|
||||
#define MPCORE_PERIPHBASE 0xF8F00000
|
||||
|
||||
static const int dma_irqs[8] = {
|
||||
46, 47, 48, 49, 72, 73, 74, 75
|
||||
};
|
||||
@@ -102,6 +105,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
ObjectClass *cpu_oc;
|
||||
ARMCPU *cpu;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
|
||||
@@ -110,15 +114,24 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
||||
SysBusDevice *busdev;
|
||||
qemu_irq pic[64];
|
||||
NICInfo *nd;
|
||||
Error *err = NULL;
|
||||
int n;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = "cortex-a9";
|
||||
}
|
||||
cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
|
||||
|
||||
cpu = cpu_arm_init(cpu_model);
|
||||
if (!cpu) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc)));
|
||||
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -154,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
||||
qdev_prop_set_uint32(dev, "num-cpu", 1);
|
||||
qdev_init_nofail(dev);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(busdev, 0, 0xF8F00000);
|
||||
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
|
||||
sysbus_connect_irq(busdev, 0,
|
||||
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
|
||||
|
||||
|
||||
+228
-32
@@ -40,6 +40,7 @@
|
||||
#include "hw/block/flash.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "hw/sysbus.h"
|
||||
@@ -71,7 +72,9 @@ struct pflash_t {
|
||||
BlockDriverState *bs;
|
||||
uint32_t nb_blocs;
|
||||
uint64_t sector_len;
|
||||
uint8_t width;
|
||||
uint8_t bank_width;
|
||||
uint8_t device_width; /* If 0, device width not specified. */
|
||||
uint8_t max_device_width; /* max device width in bytes */
|
||||
uint8_t be;
|
||||
uint8_t wcycle; /* if 0, the flash is read normally */
|
||||
int ro;
|
||||
@@ -116,6 +119,119 @@ static void pflash_timer (void *opaque)
|
||||
pfl->cmd = 0;
|
||||
}
|
||||
|
||||
/* Perform a CFI query based on the bank width of the flash.
|
||||
* If this code is called we know we have a device_width set for
|
||||
* this flash.
|
||||
*/
|
||||
static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset)
|
||||
{
|
||||
int i;
|
||||
uint32_t resp = 0;
|
||||
hwaddr boff;
|
||||
|
||||
/* Adjust incoming offset to match expected device-width
|
||||
* addressing. CFI query addresses are always specified in terms of
|
||||
* the maximum supported width of the device. This means that x8
|
||||
* devices and x8/x16 devices in x8 mode behave differently. For
|
||||
* devices that are not used at their max width, we will be
|
||||
* provided with addresses that use higher address bits than
|
||||
* expected (based on the max width), so we will shift them lower
|
||||
* so that they will match the addresses used when
|
||||
* device_width==max_device_width.
|
||||
*/
|
||||
boff = offset >> (ctz32(pfl->bank_width) +
|
||||
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
|
||||
|
||||
if (boff > pfl->cfi_len) {
|
||||
return 0;
|
||||
}
|
||||
/* Now we will construct the CFI response generated by a single
|
||||
* device, then replicate that for all devices that make up the
|
||||
* bus. For wide parts used in x8 mode, CFI query responses
|
||||
* are different than native byte-wide parts.
|
||||
*/
|
||||
resp = pfl->cfi_table[boff];
|
||||
if (pfl->device_width != pfl->max_device_width) {
|
||||
/* The only case currently supported is x8 mode for a
|
||||
* wider part.
|
||||
*/
|
||||
if (pfl->device_width != 1 || pfl->bank_width > 4) {
|
||||
DPRINTF("%s: Unsupported device configuration: "
|
||||
"device_width=%d, max_device_width=%d\n",
|
||||
__func__, pfl->device_width,
|
||||
pfl->max_device_width);
|
||||
return 0;
|
||||
}
|
||||
/* CFI query data is repeated, rather than zero padded for
|
||||
* wide devices used in x8 mode.
|
||||
*/
|
||||
for (i = 1; i < pfl->max_device_width; i++) {
|
||||
resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]);
|
||||
}
|
||||
}
|
||||
/* Replicate responses for each device in bank. */
|
||||
if (pfl->device_width < pfl->bank_width) {
|
||||
for (i = pfl->device_width;
|
||||
i < pfl->bank_width; i += pfl->device_width) {
|
||||
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Perform a device id query based on the bank width of the flash. */
|
||||
static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
|
||||
{
|
||||
int i;
|
||||
uint32_t resp;
|
||||
hwaddr boff;
|
||||
|
||||
/* Adjust incoming offset to match expected device-width
|
||||
* addressing. Device ID read addresses are always specified in
|
||||
* terms of the maximum supported width of the device. This means
|
||||
* that x8 devices and x8/x16 devices in x8 mode behave
|
||||
* differently. For devices that are not used at their max width,
|
||||
* we will be provided with addresses that use higher address bits
|
||||
* than expected (based on the max width), so we will shift them
|
||||
* lower so that they will match the addresses used when
|
||||
* device_width==max_device_width.
|
||||
*/
|
||||
boff = offset >> (ctz32(pfl->bank_width) +
|
||||
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
|
||||
|
||||
/* Mask off upper bits which may be used in to query block
|
||||
* or sector lock status at other addresses.
|
||||
* Offsets 2/3 are block lock status, is not emulated.
|
||||
*/
|
||||
switch (boff & 0xFF) {
|
||||
case 0:
|
||||
resp = pfl->ident0;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
resp = pfl->ident1;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information offset=%x\n", __func__,
|
||||
(unsigned)offset);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
/* Replicate responses for each device in bank. */
|
||||
if (pfl->device_width < pfl->bank_width) {
|
||||
for (i = pfl->device_width;
|
||||
i < pfl->bank_width; i += pfl->device_width) {
|
||||
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
||||
int width, int be)
|
||||
{
|
||||
@@ -124,12 +240,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
||||
uint8_t *p;
|
||||
|
||||
ret = -1;
|
||||
boff = offset & 0xFF; /* why this here ?? */
|
||||
|
||||
if (pfl->width == 2)
|
||||
boff = boff >> 1;
|
||||
else if (pfl->width == 4)
|
||||
boff = boff >> 2;
|
||||
|
||||
#if 0
|
||||
DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
|
||||
@@ -190,35 +300,88 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
||||
case 0x60: /* Block /un)lock */
|
||||
case 0x70: /* Status Register */
|
||||
case 0xe8: /* Write block */
|
||||
/* Status register read */
|
||||
/* Status register read. Return status from each device in
|
||||
* bank.
|
||||
*/
|
||||
ret = pfl->status;
|
||||
if (width > 2) {
|
||||
if (pfl->device_width && width > pfl->device_width) {
|
||||
int shift = pfl->device_width * 8;
|
||||
while (shift + pfl->device_width * 8 <= width * 8) {
|
||||
ret |= pfl->status << shift;
|
||||
shift += pfl->device_width * 8;
|
||||
}
|
||||
} else if (!pfl->device_width && width > 2) {
|
||||
/* Handle 32 bit flash cases where device width is not
|
||||
* set. (Existing behavior before device width added.)
|
||||
*/
|
||||
ret |= pfl->status << 16;
|
||||
}
|
||||
DPRINTF("%s: status %x\n", __func__, ret);
|
||||
break;
|
||||
case 0x90:
|
||||
switch (boff) {
|
||||
case 0:
|
||||
ret = pfl->ident0 << 8 | pfl->ident1;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
ret = pfl->ident2 << 8 | pfl->ident3;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
|
||||
(unsigned)boff);
|
||||
ret = 0;
|
||||
break;
|
||||
if (!pfl->device_width) {
|
||||
/* Preserve old behavior if device width not specified */
|
||||
boff = offset & 0xFF;
|
||||
if (pfl->bank_width == 2) {
|
||||
boff = boff >> 1;
|
||||
} else if (pfl->bank_width == 4) {
|
||||
boff = boff >> 2;
|
||||
}
|
||||
|
||||
switch (boff) {
|
||||
case 0:
|
||||
ret = pfl->ident0 << 8 | pfl->ident1;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
ret = pfl->ident2 << 8 | pfl->ident3;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
|
||||
(unsigned)boff);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* If we have a read larger than the bank_width, combine multiple
|
||||
* manufacturer/device ID queries into a single response.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < width; i += pfl->bank_width) {
|
||||
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
|
||||
pflash_devid_query(pfl,
|
||||
offset + i * pfl->bank_width));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x98: /* Query mode */
|
||||
if (boff > pfl->cfi_len)
|
||||
ret = 0;
|
||||
else
|
||||
ret = pfl->cfi_table[boff];
|
||||
if (!pfl->device_width) {
|
||||
/* Preserve old behavior if device width not specified */
|
||||
boff = offset & 0xFF;
|
||||
if (pfl->bank_width == 2) {
|
||||
boff = boff >> 1;
|
||||
} else if (pfl->bank_width == 4) {
|
||||
boff = boff >> 2;
|
||||
}
|
||||
|
||||
if (boff > pfl->cfi_len) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = pfl->cfi_table[boff];
|
||||
}
|
||||
} else {
|
||||
/* If we have a read larger than the bank_width, combine multiple
|
||||
* CFI queries into a single response.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < width; i += pfl->bank_width) {
|
||||
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
|
||||
pflash_cfi_query(pfl,
|
||||
offset + i * pfl->bank_width));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
@@ -378,6 +541,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
|
||||
|
||||
break;
|
||||
case 0xe8:
|
||||
/* Mask writeblock size based on device width, or bank width if
|
||||
* device width not specified.
|
||||
*/
|
||||
if (pfl->device_width) {
|
||||
value = extract32(value, 0, pfl->device_width * 8);
|
||||
} else {
|
||||
value = extract32(value, 0, pfl->bank_width * 8);
|
||||
}
|
||||
DPRINTF("%s: block write of %x bytes\n", __func__, value);
|
||||
pfl->counter = value;
|
||||
pfl->wcycle++;
|
||||
@@ -613,6 +784,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
||||
pfl->ro = 0;
|
||||
}
|
||||
|
||||
/* Default to devices being used at their maximum device width. This was
|
||||
* assumed before the device_width support was added.
|
||||
*/
|
||||
if (!pfl->max_device_width) {
|
||||
pfl->max_device_width = pfl->device_width;
|
||||
}
|
||||
|
||||
pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
|
||||
pfl->wcycle = 0;
|
||||
pfl->cmd = 0;
|
||||
@@ -665,7 +843,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
||||
pfl->cfi_table[0x28] = 0x02;
|
||||
pfl->cfi_table[0x29] = 0x00;
|
||||
/* Max number of bytes in multi-bytes write */
|
||||
if (pfl->width == 1) {
|
||||
if (pfl->bank_width == 1) {
|
||||
pfl->cfi_table[0x2A] = 0x08;
|
||||
} else {
|
||||
pfl->cfi_table[0x2A] = 0x0B;
|
||||
@@ -706,7 +884,25 @@ static Property pflash_cfi01_properties[] = {
|
||||
DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
|
||||
DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
|
||||
DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
|
||||
DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
|
||||
/* width here is the overall width of this QEMU device in bytes.
|
||||
* The QEMU device may be emulating a number of flash devices
|
||||
* wired up in parallel; the width of each individual flash
|
||||
* device should be specified via device-width. If the individual
|
||||
* devices have a maximum width which is greater than the width
|
||||
* they are being used for, this maximum width should be set via
|
||||
* max-device-width (which otherwise defaults to device-width).
|
||||
* So for instance a 32-bit wide QEMU flash device made from four
|
||||
* 16-bit flash devices used in 8-bit wide mode would be configured
|
||||
* with width = 4, device-width = 1, max-device-width = 2.
|
||||
*
|
||||
* If device-width is not specified we default to backwards
|
||||
* compatible behaviour which is a bad emulation of two
|
||||
* 16 bit devices making up a 32 bit wide QEMU device. This
|
||||
* is deprecated for new uses of this device.
|
||||
*/
|
||||
DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0),
|
||||
DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0),
|
||||
DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0),
|
||||
DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
|
||||
DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
|
||||
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
|
||||
@@ -745,8 +941,8 @@ pflash_t *pflash_cfi01_register(hwaddr base,
|
||||
DeviceState *qdev, const char *name,
|
||||
hwaddr size,
|
||||
BlockDriverState *bs,
|
||||
uint32_t sector_len, int nb_blocs, int width,
|
||||
uint16_t id0, uint16_t id1,
|
||||
uint32_t sector_len, int nb_blocs,
|
||||
int bank_width, uint16_t id0, uint16_t id1,
|
||||
uint16_t id2, uint16_t id3, int be)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01);
|
||||
@@ -756,7 +952,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
|
||||
}
|
||||
qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
|
||||
qdev_prop_set_uint64(dev, "sector-length", sector_len);
|
||||
qdev_prop_set_uint8(dev, "width", width);
|
||||
qdev_prop_set_uint8(dev, "width", bank_width);
|
||||
qdev_prop_set_uint8(dev, "big-endian", !!be);
|
||||
qdev_prop_set_uint16(dev, "id0", id0);
|
||||
qdev_prop_set_uint16(dev, "id1", id1);
|
||||
|
||||
@@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o
|
||||
obj-$(CONFIG_OMAP) += omap_uart.o
|
||||
obj-$(CONFIG_SH4) += sh_serial.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
||||
obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* QEMU model of the Canon DIGIC UART block.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* See "Serial terminal" docs here:
|
||||
* http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers
|
||||
*
|
||||
* The QEMU model of the Milkymist UART block by Michael Walle
|
||||
* is used as a template.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#include "hw/char/digic-uart.h"
|
||||
|
||||
enum {
|
||||
ST_RX_RDY = (1 << 0),
|
||||
ST_TX_RDY = (1 << 1),
|
||||
};
|
||||
|
||||
static uint64_t digic_uart_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
switch (addr) {
|
||||
case R_RX:
|
||||
s->reg_st &= ~(ST_RX_RDY);
|
||||
ret = s->reg_rx;
|
||||
break;
|
||||
|
||||
case R_ST:
|
||||
ret = s->reg_st;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-uart: read access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
unsigned char ch = value;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
switch (addr) {
|
||||
case R_TX:
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_write_all(s->chr, &ch, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_ST:
|
||||
/*
|
||||
* Ignore write to R_ST.
|
||||
*
|
||||
* The point is that this register is actively used
|
||||
* during receiving and transmitting symbols,
|
||||
* but we don't know the function of most of bits.
|
||||
*
|
||||
* Ignoring writes to R_ST is only a simplification
|
||||
* of the model. It has no perceptible side effects
|
||||
* for existing guests.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-uart: write access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps uart_mmio_ops = {
|
||||
.read = digic_uart_read,
|
||||
.write = digic_uart_write,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int uart_can_rx(void *opaque)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
|
||||
return !(s->reg_st & ST_RX_RDY);
|
||||
}
|
||||
|
||||
static void uart_rx(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
|
||||
assert(uart_can_rx(opaque));
|
||||
|
||||
s->reg_st |= ST_RX_RDY;
|
||||
s->reg_rx = *buf;
|
||||
}
|
||||
|
||||
static void uart_event(void *opaque, int event)
|
||||
{
|
||||
}
|
||||
|
||||
static void digic_uart_reset(DeviceState *d)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(d);
|
||||
|
||||
s->reg_rx = 0;
|
||||
s->reg_st = ST_TX_RDY;
|
||||
}
|
||||
|
||||
static void digic_uart_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(dev);
|
||||
|
||||
s->chr = qemu_char_get_next_serial();
|
||||
if (s->chr) {
|
||||
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void digic_uart_init(Object *obj)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(obj);
|
||||
|
||||
memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s,
|
||||
TYPE_DIGIC_UART, 0x18);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_digic_uart = {
|
||||
.name = "digic-uart",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(reg_rx, DigicUartState),
|
||||
VMSTATE_UINT32(reg_st, DigicUartState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void digic_uart_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = digic_uart_realize;
|
||||
dc->reset = digic_uart_reset;
|
||||
dc->vmsd = &vmstate_digic_uart;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_uart_info = {
|
||||
.name = TYPE_DIGIC_UART,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(DigicUartState),
|
||||
.instance_init = digic_uart_init,
|
||||
.class_init = digic_uart_class_init,
|
||||
};
|
||||
|
||||
static void digic_uart_register_types(void)
|
||||
{
|
||||
type_register_static(&digic_uart_info);
|
||||
}
|
||||
|
||||
type_init(digic_uart_register_types)
|
||||
@@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
|
||||
obj-$(CONFIG_SH4) += sh_intc.o
|
||||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Allwinner A10 interrupt controller device emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.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.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/intc/allwinner-a10-pic.h"
|
||||
|
||||
static void aw_a10_pic_update(AwA10PICState *s)
|
||||
{
|
||||
uint8_t i;
|
||||
int irq = 0, fiq = 0;
|
||||
|
||||
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
|
||||
irq |= s->irq_pending[i] & ~s->mask[i];
|
||||
fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i];
|
||||
}
|
||||
|
||||
qemu_set_irq(s->parent_irq, !!irq);
|
||||
qemu_set_irq(s->parent_fiq, !!fiq);
|
||||
}
|
||||
|
||||
static void aw_a10_pic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
|
||||
if (level) {
|
||||
set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]);
|
||||
}
|
||||
aw_a10_pic_update(s);
|
||||
}
|
||||
|
||||
static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
uint8_t index = (offset & 0xc) / 4;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIC_VECTOR:
|
||||
return s->vector;
|
||||
case AW_A10_PIC_BASE_ADDR:
|
||||
return s->base_addr;
|
||||
case AW_A10_PIC_PROTECT:
|
||||
return s->protect;
|
||||
case AW_A10_PIC_NMI:
|
||||
return s->nmi;
|
||||
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
|
||||
return s->irq_pending[index];
|
||||
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
|
||||
return s->fiq_pending[index];
|
||||
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
|
||||
return s->select[index];
|
||||
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
|
||||
return s->enable[index];
|
||||
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
|
||||
return s->mask[index];
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
uint8_t index = (offset & 0xc) / 4;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIC_VECTOR:
|
||||
s->vector = value & ~0x3;
|
||||
break;
|
||||
case AW_A10_PIC_BASE_ADDR:
|
||||
s->base_addr = value & ~0x3;
|
||||
case AW_A10_PIC_PROTECT:
|
||||
s->protect = value;
|
||||
break;
|
||||
case AW_A10_PIC_NMI:
|
||||
s->nmi = value;
|
||||
break;
|
||||
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
|
||||
s->irq_pending[index] &= ~value;
|
||||
break;
|
||||
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
|
||||
s->fiq_pending[index] &= ~value;
|
||||
break;
|
||||
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
|
||||
s->select[index] = value;
|
||||
break;
|
||||
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
|
||||
s->enable[index] = value;
|
||||
break;
|
||||
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
|
||||
s->mask[index] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
aw_a10_pic_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aw_a10_pic_ops = {
|
||||
.read = aw_a10_pic_read,
|
||||
.write = aw_a10_pic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_aw_a10_pic = {
|
||||
.name = "a10.pic",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(vector, AwA10PICState),
|
||||
VMSTATE_UINT32(base_addr, AwA10PICState),
|
||||
VMSTATE_UINT32(protect, AwA10PICState),
|
||||
VMSTATE_UINT32(nmi, AwA10PICState),
|
||||
VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aw_a10_pic_init(Object *obj)
|
||||
{
|
||||
AwA10PICState *s = AW_A10_PIC(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
|
||||
qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR);
|
||||
sysbus_init_irq(dev, &s->parent_irq);
|
||||
sysbus_init_irq(dev, &s->parent_fiq);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s,
|
||||
TYPE_AW_A10_PIC, 0x400);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
}
|
||||
|
||||
static void aw_a10_pic_reset(DeviceState *d)
|
||||
{
|
||||
AwA10PICState *s = AW_A10_PIC(d);
|
||||
uint8_t i;
|
||||
|
||||
s->base_addr = 0;
|
||||
s->protect = 0;
|
||||
s->nmi = 0;
|
||||
s->vector = 0;
|
||||
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
|
||||
s->irq_pending[i] = 0;
|
||||
s->fiq_pending[i] = 0;
|
||||
s->select[i] = 0;
|
||||
s->enable[i] = 0;
|
||||
s->mask[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void aw_a10_pic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = aw_a10_pic_reset;
|
||||
dc->desc = "allwinner a10 pic";
|
||||
dc->vmsd = &vmstate_aw_a10_pic;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_a10_pic_info = {
|
||||
.name = TYPE_AW_A10_PIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AwA10PICState),
|
||||
.instance_init = aw_a10_pic_init,
|
||||
.class_init = aw_a10_pic_class_init,
|
||||
};
|
||||
|
||||
static void aw_a10_register_types(void)
|
||||
{
|
||||
type_register_static(&aw_a10_pic_info);
|
||||
}
|
||||
|
||||
type_init(aw_a10_register_types);
|
||||
@@ -26,5 +26,8 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
obj-$(CONFIG_SH4) += sh_timer.o
|
||||
obj-$(CONFIG_TUSB6010) += tusb6010.o
|
||||
obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
|
||||
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
|
||||
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user