aboutsummaryrefslogtreecommitdiff
path: root/hw/arm
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2013-02-05 12:03:15 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2013-03-01 15:01:19 +0100
commit530182169e897c0e401b245552a4c58dc6846912 (patch)
tree8c39251d7e89855a89d925359f71639400782cd6 /hw/arm
parente4c8b28cde12d01ada8fe869567dc5717a2dfcb7 (diff)
downloadqemu-530182169e897c0e401b245552a4c58dc6846912.zip
qemu-530182169e897c0e401b245552a4c58dc6846912.tar.gz
qemu-530182169e897c0e401b245552a4c58dc6846912.tar.bz2
hw: move boards and other isolated files to hw/ARCH
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/arm')
-rw-r--r--hw/arm/Makefile.objs31
-rw-r--r--hw/arm/boot.c480
-rw-r--r--hw/arm/collie.c73
-rw-r--r--hw/arm/exynos4_boards.c170
-rw-r--r--hw/arm/gumstix.c141
-rw-r--r--hw/arm/highbank.c342
-rw-r--r--hw/arm/integratorcp.c566
-rw-r--r--hw/arm/kzm.c157
-rw-r--r--hw/arm/mainstone.c190
-rw-r--r--hw/arm/musicpal.c1697
-rw-r--r--hw/arm/nseries.c1430
-rw-r--r--hw/arm/omap_sx1.c238
-rw-r--r--hw/arm/palm.c291
-rw-r--r--hw/arm/pic_cpu.c40
-rw-r--r--hw/arm/realview.c404
-rw-r--r--hw/arm/spitz.c1138
-rw-r--r--hw/arm/stellaris.c1401
-rw-r--r--hw/arm/tosa.c302
-rw-r--r--hw/arm/versatilepb.c403
-rw-r--r--hw/arm/vexpress.c500
-rw-r--r--hw/arm/xilinx_zynq.c224
-rw-r--r--hw/arm/z2.c384
22 files changed, 10585 insertions, 17 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 3eb1366..c09cc3a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,35 +1,32 @@
-obj-y = integratorcp.o versatilepb.o arm_pic.o
-obj-y += arm_boot.o
-obj-y += xilinx_zynq.o zynq_slcr.o
+obj-y += zynq_slcr.o
obj-y += xilinx_spips.o
obj-y += arm_gic.o arm_gic_common.o
obj-y += a9scu.o
-obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
+obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
-obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
+obj-y += exynos4210_uart.o exynos4210_pwm.o
obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
obj-y += exynos4210_rtc.o exynos4210_i2c.o
obj-y += arm_mptimer.o a15mpcore.o
-obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o
-obj-y += highbank.o
+obj-y += armv7m.o armv7m_nvic.o stellaris_enet.o
obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
-obj-y += gumstix.o
-obj-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o
+obj-y += zaurus.o ide/microdrive.o tc6393xb.o
obj-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
omap_gpio.o omap_intc.o omap_uart.o
obj-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
-obj-y += omap_sx1.o palm.o tsc210x.o
-obj-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
-obj-y += mst_fpga.o mainstone.o
-obj-y += z2.o
-obj-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
+obj-y += tsc210x.o
+obj-y += blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
+obj-y += mst_fpga.o
+obj-y += bitbang_i2c.o marvell_88w8618_audio.o
obj-y += framebuffer.o
-obj-y += vexpress.o
obj-y += strongarm.o
-obj-y += collie.o
obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
-obj-y += kzm.o
obj-y := $(addprefix ../,$(obj-y))
+
+obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
+obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
+obj-y += omap_sx1.o palm.o pic_cpu.o realview.o spitz.o stellaris.o
+obj-y += tosa.o versatilepb.o vexpress.o xilinx_zynq.o z2.o
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
new file mode 100644
index 0000000..43253fd
--- /dev/null
+++ b/hw/arm/boot.c
@@ -0,0 +1,480 @@
+/*
+ * ARM kernel loader.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "config.h"
+#include "hw/hw.h"
+#include "hw/arm-misc.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "sysemu/device_tree.h"
+#include "qemu/config-file.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+
+/* 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. */
+};
+
+/* Handling for secondary CPU boot in a multicore system.
+ * Unlike the uniprocessor/primary CPU boot, this is platform
+ * dependent. The default code here is based on the secondary
+ * CPU boot protocol used on realview/vexpress boards, with
+ * some parameterisation to increase its flexibility.
+ * QEMU platform models for which this code is not appropriate
+ * should override write_secondary_boot and secondary_cpu_reset_hook
+ * instead.
+ *
+ * This code enables the interrupt controllers for the secondary
+ * CPUs and then puts all the secondary CPUs into a loop waiting
+ * for an interprocessor interrupt and polling a configurable
+ * location for the kernel secondary CPU entry point.
+ */
+#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 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]);
+ }
+ rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+}
+
+static void default_reset_secondary(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ CPUARMState *env = &cpu->env;
+
+ stl_phys_notdirty(info->smp_bootreg_addr, 0);
+ env->regs[15] = info->smp_loader_start;
+}
+
+#define WRITE_WORD(p, value) do { \
+ stl_phys_notdirty(p, value); \
+ p += 4; \
+} while (0)
+
+static void set_kernel_args(const struct arm_boot_info *info)
+{
+ int initrd_size = info->initrd_size;
+ hwaddr base = info->loader_start;
+ hwaddr p;
+
+ p = base + KERNEL_ARGS_ADDR;
+ /* ATAG_CORE */
+ WRITE_WORD(p, 5);
+ WRITE_WORD(p, 0x54410001);
+ WRITE_WORD(p, 1);
+ WRITE_WORD(p, 0x1000);
+ WRITE_WORD(p, 0);
+ /* ATAG_MEM */
+ /* TODO: handle multiple chips on one ATAG list */
+ WRITE_WORD(p, 4);
+ WRITE_WORD(p, 0x54410002);
+ WRITE_WORD(p, info->ram_size);
+ WRITE_WORD(p, info->loader_start);
+ if (initrd_size) {
+ /* ATAG_INITRD2 */
+ WRITE_WORD(p, 4);
+ WRITE_WORD(p, 0x54420005);
+ WRITE_WORD(p, info->initrd_start);
+ WRITE_WORD(p, initrd_size);
+ }
+ if (info->kernel_cmdline && *info->kernel_cmdline) {
+ /* ATAG_CMDLINE */
+ int cmdline_size;
+
+ cmdline_size = strlen(info->kernel_cmdline);
+ cpu_physical_memory_write(p + 8, (void *)info->kernel_cmdline,
+ cmdline_size + 1);
+ cmdline_size = (cmdline_size >> 2) + 1;
+ WRITE_WORD(p, cmdline_size + 2);
+ WRITE_WORD(p, 0x54410009);
+ p += cmdline_size * 4;
+ }
+ if (info->atag_board) {
+ /* ATAG_BOARD */
+ int atag_board_len;
+ uint8_t atag_board_buf[0x1000];
+
+ atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
+ WRITE_WORD(p, (atag_board_len + 8) >> 2);
+ WRITE_WORD(p, 0x414f4d50);
+ cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
+ p += atag_board_len;
+ }
+ /* ATAG_END */
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+}
+
+static void set_kernel_args_old(const struct arm_boot_info *info)
+{
+ hwaddr p;
+ const char *s;
+ int initrd_size = info->initrd_size;
+ hwaddr base = info->loader_start;
+
+ /* see linux/include/asm-arm/setup.h */
+ p = base + KERNEL_ARGS_ADDR;
+ /* page_size */
+ WRITE_WORD(p, 4096);
+ /* nr_pages */
+ WRITE_WORD(p, info->ram_size / 4096);
+ /* ramdisk_size */
+ WRITE_WORD(p, 0);
+#define FLAG_READONLY 1
+#define FLAG_RDLOAD 4
+#define FLAG_RDPROMPT 8
+ /* flags */
+ WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
+ /* rootdev */
+ WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
+ /* video_num_cols */
+ WRITE_WORD(p, 0);
+ /* video_num_rows */
+ WRITE_WORD(p, 0);
+ /* video_x */
+ WRITE_WORD(p, 0);
+ /* video_y */
+ WRITE_WORD(p, 0);
+ /* memc_control_reg */
+ WRITE_WORD(p, 0);
+ /* unsigned char sounddefault */
+ /* unsigned char adfsdrives */
+ /* unsigned char bytes_per_char_h */
+ /* unsigned char bytes_per_char_v */
+ WRITE_WORD(p, 0);
+ /* pages_in_bank[4] */
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ /* pages_in_vram */
+ WRITE_WORD(p, 0);
+ /* initrd_start */
+ if (initrd_size) {
+ WRITE_WORD(p, info->initrd_start);
+ } else {
+ WRITE_WORD(p, 0);
+ }
+ /* initrd_size */
+ WRITE_WORD(p, initrd_size);
+ /* rd_start */
+ WRITE_WORD(p, 0);
+ /* system_rev */
+ WRITE_WORD(p, 0);
+ /* system_serial_low */
+ WRITE_WORD(p, 0);
+ /* system_serial_high */
+ WRITE_WORD(p, 0);
+ /* mem_fclk_21285 */
+ WRITE_WORD(p, 0);
+ /* zero unused fields */
+ while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
+ WRITE_WORD(p, 0);
+ }
+ s = info->kernel_cmdline;
+ if (s) {
+ cpu_physical_memory_write(p, (void *)s, strlen(s) + 1);
+ } else {
+ WRITE_WORD(p, 0);
+ }
+}
+
+static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
+{
+#ifdef CONFIG_FDT
+ uint32_t *mem_reg_property;
+ uint32_t mem_reg_propsize;
+ void *fdt = NULL;
+ char *filename;
+ int size, rc;
+ uint32_t acells, scells, hival;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
+ if (!filename) {
+ fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
+ return -1;
+ }
+
+ fdt = load_device_tree(filename, &size);
+ if (!fdt) {
+ fprintf(stderr, "Couldn't open dtb file %s\n", filename);
+ g_free(filename);
+ return -1;
+ }
+ g_free(filename);
+
+ acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells");
+ scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells");
+ if (acells == 0 || scells == 0) {
+ fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
+ return -1;
+ }
+
+ mem_reg_propsize = acells + scells;
+ mem_reg_property = g_new0(uint32_t, mem_reg_propsize);
+ mem_reg_property[acells - 1] = cpu_to_be32(binfo->loader_start);
+ hival = cpu_to_be32(binfo->loader_start >> 32);
+ if (acells > 1) {
+ mem_reg_property[acells - 2] = hival;
+ } else if (hival != 0) {
+ fprintf(stderr, "qemu: dtb file not compatible with "
+ "RAM start address > 4GB\n");
+ exit(1);
+ }
+ mem_reg_property[acells + scells - 1] = cpu_to_be32(binfo->ram_size);
+ hival = cpu_to_be32(binfo->ram_size >> 32);
+ if (scells > 1) {
+ mem_reg_property[acells + scells - 2] = hival;
+ } else if (hival != 0) {
+ fprintf(stderr, "qemu: dtb file not compatible with "
+ "RAM size > 4GB\n");
+ exit(1);
+ }
+
+ rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
+ mem_reg_propsize * sizeof(uint32_t));
+ if (rc < 0) {
+ fprintf(stderr, "couldn't set /memory/reg\n");
+ }
+
+ if (binfo->kernel_cmdline && *binfo->kernel_cmdline) {
+ rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ binfo->kernel_cmdline);
+ if (rc < 0) {
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ }
+ }
+
+ if (binfo->initrd_size) {
+ rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ binfo->initrd_start);
+ if (rc < 0) {
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
+ }
+
+ rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ binfo->initrd_start + binfo->initrd_size);
+ if (rc < 0) {
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
+ }
+ }
+
+ cpu_physical_memory_write(addr, fdt, size);
+
+ return 0;
+
+#else
+ fprintf(stderr, "Device tree requested, "
+ "but qemu was compiled without fdt support\n");
+ return -1;
+#endif
+}
+
+static void do_cpu_reset(void *opaque)
+{
+ ARMCPU *cpu = opaque;
+ CPUARMState *env = &cpu->env;
+ const struct arm_boot_info *info = env->boot_info;
+
+ cpu_reset(CPU(cpu));
+ if (info) {
+ if (!info->is_linux) {
+ /* Jump to the entry point. */
+ env->regs[15] = info->entry & 0xfffffffe;
+ env->thumb = info->entry & 1;
+ } else {
+ if (env == first_cpu) {
+ env->regs[15] = info->loader_start;
+ if (!info->dtb_filename) {
+ if (old_param) {
+ set_kernel_args_old(info);
+ } else {
+ set_kernel_args(info);
+ }
+ }
+ } else {
+ info->secondary_cpu_reset_hook(cpu, info);
+ }
+ }
+ }
+}
+
+void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+{
+ CPUARMState *env = &cpu->env;
+ int kernel_size;
+ int initrd_size;
+ int n;
+ int is_linux = 0;
+ uint64_t elf_entry;
+ hwaddr entry;
+ int big_endian;
+ QemuOpts *machine_opts;
+
+ /* Load the kernel. */
+ if (!info->kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
+ if (machine_opts) {
+ info->dtb_filename = qemu_opt_get(machine_opts, "dtb");
+ } else {
+ info->dtb_filename = NULL;
+ }
+
+ if (!info->secondary_cpu_reset_hook) {
+ info->secondary_cpu_reset_hook = default_reset_secondary;
+ }
+ if (!info->write_secondary_boot) {
+ info->write_secondary_boot = default_write_secondary;
+ }
+
+ if (info->nb_cpus == 0)
+ info->nb_cpus = 1;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ /* We want to put the initrd far enough into RAM that when the
+ * kernel is uncompressed it will not clobber the initrd. However
+ * on boards without much RAM we must ensure that we still leave
+ * enough room for a decent sized initrd, and on boards with large
+ * amounts of RAM we must avoid the initrd being so far up in RAM
+ * that it is outside lowmem and inaccessible to the kernel.
+ * So for boards with less than 256MB of RAM we put the initrd
+ * halfway into RAM, and for boards with 256MB of RAM or more we put
+ * the initrd at 128MB.
+ */
+ info->initrd_start = info->loader_start +
+ MIN(info->ram_size / 2, 128 * 1024 * 1024);
+
+ /* Assume that raw images are linux kernels, and ELF images are not. */
+ kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
+ NULL, NULL, big_endian, ELF_MACHINE, 1);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
+ &is_linux);
+ }
+ if (kernel_size < 0) {
+ entry = info->loader_start + KERNEL_LOAD_ADDR;
+ kernel_size = load_image_targphys(info->kernel_filename, entry,
+ info->ram_size - KERNEL_LOAD_ADDR);
+ is_linux = 1;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ info->kernel_filename);
+ exit(1);
+ }
+ info->entry = entry;
+ if (is_linux) {
+ if (info->initrd_filename) {
+ initrd_size = load_image_targphys(info->initrd_filename,
+ info->initrd_start,
+ info->ram_size -
+ info->initrd_start);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n",
+ info->initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+ info->initrd_size = initrd_size;
+
+ bootloader[4] = info->board_id;
+
+ /* for device tree boot, we pass the DTB directly in r2. Otherwise
+ * we point to the kernel args.
+ */
+ if (info->dtb_filename) {
+ /* Place the DTB after the initrd in memory. Note that some
+ * kernels will trash anything in the 4K page the initrd
+ * ends in, so make sure the DTB isn't caught up in that.
+ */
+ hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
+ 4096);
+ if (load_dtb(dtb_start, info)) {
+ exit(1);
+ }
+ bootloader[5] = dtb_start;
+ } else {
+ bootloader[5] = 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"
+ " using -dtb)\n");
+ 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);
+ if (info->nb_cpus > 1) {
+ info->write_secondary_boot(cpu, info);
+ }
+ }
+ info->is_linux = is_linux;
+
+ for (; env; env = env->next_cpu) {
+ cpu = arm_env_get_cpu(env);
+ env->boot_info = info;
+ qemu_register_reset(do_cpu_reset, cpu);
+ }
+}
diff --git a/hw/arm/collie.c b/hw/arm/collie.c
new file mode 100644
index 0000000..17fddc8
--- /dev/null
+++ b/hw/arm/collie.c
@@ -0,0 +1,73 @@
+/*
+ * SA-1110-based Sharp Zaurus SL-5500 platform.
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * This code is licensed under GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/boards.h"
+#include "hw/devices.h"
+#include "hw/strongarm.h"
+#include "hw/arm-misc.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+
+static struct arm_boot_info collie_binfo = {
+ .loader_start = SA_SDCS0,
+ .ram_size = 0x20000000,
+};
+
+static void collie_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ StrongARMState *s;
+ DriveInfo *dinfo;
+ MemoryRegion *sysmem = get_system_memory();
+
+ if (!cpu_model) {
+ cpu_model = "sa1110";
+ }
+
+ s = sa1110_init(sysmem, collie_binfo.ram_size, cpu_model);
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+
+ dinfo = drive_get(IF_PFLASH, 0, 1);
+ pflash_cfi01_register(SA_CS1, NULL, "collie.fl2", 0x02000000,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+
+ sysbus_create_simple("scoop", 0x40800000, NULL);
+
+ collie_binfo.kernel_filename = kernel_filename;
+ collie_binfo.kernel_cmdline = kernel_cmdline;
+ collie_binfo.initrd_filename = initrd_filename;
+ collie_binfo.board_id = 0x208;
+ arm_load_kernel(s->cpu, &collie_binfo);
+}
+
+static QEMUMachine collie_machine = {
+ .name = "collie",
+ .desc = "Collie PDA (SA-1110)",
+ .init = collie_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void collie_machine_init(void)
+{
+ qemu_register_machine(&collie_machine);
+}
+
+machine_init(collie_machine_init)
diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c
new file mode 100644
index 0000000..473da34
--- /dev/null
+++ b/hw/arm/exynos4_boards.c
@@ -0,0 +1,170 @@
+/*
+ * 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/sysemu.h"
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/arm-misc.h"
+#include "exec/address-spaces.h"
+#include "hw/exynos4210.h"
+#include "hw/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,
+ .write_secondary_boot = exynos4210_write_secondary,
+};
+
+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].used) {
+ 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 = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+ }
+}
+
+static Exynos4210State *exynos4_boards_init_common(QEMUMachineInitArgs *args,
+ 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 = args->kernel_filename;
+ exynos4_board_binfo.initrd_filename = args->initrd_filename;
+ exynos4_board_binfo.kernel_cmdline = args->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],
+ args->kernel_filename,
+ args->kernel_cmdline,
+ args->initrd_filename);
+
+ return exynos4210_init(get_system_memory(),
+ exynos4_board_ram_size[board_type]);
+}
+
+static void nuri_init(QEMUMachineInitArgs *args)
+{
+ exynos4_boards_init_common(args, EXYNOS4_BOARD_NURI);
+
+ arm_load_kernel(arm_env_get_cpu(first_cpu), &exynos4_board_binfo);
+}
+
+static void smdkc210_init(QEMUMachineInitArgs *args)
+{
+ Exynos4210State *s = exynos4_boards_init_common(args,
+ EXYNOS4_BOARD_SMDKC210);
+
+ lan9215_init(SMDK_LAN9118_BASE_ADDR,
+ qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
+ arm_load_kernel(arm_env_get_cpu(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,
+ DEFAULT_MACHINE_OPTIONS,
+ },
+ [EXYNOS4_BOARD_SMDKC210] = {
+ .name = "smdkc210",
+ .desc = "Samsung SMDKC210 board (Exynos4210)",
+ .init = smdkc210_init,
+ .max_cpus = EXYNOS4210_NCPUS,
+ DEFAULT_MACHINE_OPTIONS,
+ },
+};
+
+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);
diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c
new file mode 100644
index 0000000..8859b73
--- /dev/null
+++ b/hw/arm/gumstix.c
@@ -0,0 +1,141 @@
+/*
+ * Gumstix Platforms
+ *
+ * Copyright (c) 2007 by Thorsten Zitterell <info@bitmux.org>
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+/*
+ * Example usage:
+ *
+ * connex:
+ * =======
+ * create image:
+ * # dd of=flash bs=1k count=16k if=/dev/zero
+ * # dd of=flash bs=1k conv=notrunc if=u-boot.bin
+ * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2
+ * start it:
+ * # qemu-system-arm -M connex -pflash flash -monitor null -nographic
+ *
+ * verdex:
+ * =======
+ * create image:
+ * # dd of=flash bs=1k count=32k if=/dev/zero
+ * # dd of=flash bs=1k conv=notrunc if=u-boot.bin
+ * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2
+ * # dd of=flash bs=1k conv=notrunc seek=31744 if=uImage
+ * start it:
+ * # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289
+ */
+
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "net/net.h"
+#include "hw/flash.h"
+#include "hw/devices.h"
+#include "hw/boards.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+
+static const int sector_len = 128 * 1024;
+
+static void connex_init(QEMUMachineInitArgs *args)
+{
+ PXA2xxState *cpu;
+ DriveInfo *dinfo;
+ int be;
+ MemoryRegion *address_space_mem = get_system_memory();
+
+ uint32_t connex_rom = 0x01000000;
+ uint32_t connex_ram = 0x04000000;
+
+ cpu = pxa255_init(address_space_mem, connex_ram);
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "A flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom,
+ dinfo->bdrv, sector_len, connex_rom / sector_len,
+ 2, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* Interrupt line of NIC is connected to GPIO line 36 */
+ smc91c111_init(&nd_table[0], 0x04000300,
+ qdev_get_gpio_in(cpu->gpio, 36));
+}
+
+static void verdex_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ PXA2xxState *cpu;
+ DriveInfo *dinfo;
+ int be;
+ MemoryRegion *address_space_mem = get_system_memory();
+
+ uint32_t verdex_rom = 0x02000000;
+ uint32_t verdex_ram = 0x10000000;
+
+ cpu = pxa270_init(address_space_mem, verdex_ram, cpu_model ?: "pxa270-c0");
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "A flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom,
+ dinfo->bdrv, sector_len, verdex_rom / sector_len,
+ 2, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* Interrupt line of NIC is connected to GPIO line 99 */
+ smc91c111_init(&nd_table[0], 0x04000300,
+ qdev_get_gpio_in(cpu->gpio, 99));
+}
+
+static QEMUMachine connex_machine = {
+ .name = "connex",
+ .desc = "Gumstix Connex (PXA255)",
+ .init = connex_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine verdex_machine = {
+ .name = "verdex",
+ .desc = "Gumstix Verdex (PXA270)",
+ .init = verdex_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void gumstix_machine_init(void)
+{
+ qemu_register_machine(&connex_machine);
+ qemu_register_machine(&verdex_machine);
+}
+
+machine_init(gumstix_machine_init);
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
new file mode 100644
index 0000000..a622224
--- /dev/null
+++ b/hw/arm/highbank.c
@@ -0,0 +1,342 @@
+/*
+ * Calxeda Highbank SoC emulation
+ *
+ * Copyright (c) 2010-2012 Calxeda
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/loader.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+
+#define SMP_BOOT_ADDR 0x100
+#define SMP_BOOT_REG 0x40
+#define GIC_BASE_ADDR 0xfff10000
+
+#define NIRQ_GIC 160
+
+/* Board init. */
+
+static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ int n;
+ uint32_t smpboot[] = {
+ 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5 - read current core id */
+ 0xe210000f, /* ands r0, r0, #0x0f */
+ 0xe3a03040, /* mov r3, #0x40 - jump address is 0x40 + 0x10 * core id */
+ 0xe0830200, /* add r0, r3, r0, lsl #4 */
+ 0xe59f2024, /* ldr r2, privbase */
+ 0xe3a01001, /* mov r1, #1 */
+ 0xe5821100, /* str r1, [r2, #256] - set GICC_CTLR.Enable */
+ 0xe3a010ff, /* mov r1, #0xff */
+ 0xe5821104, /* str r1, [r2, #260] - set GICC_PMR.Priority to 0xff */
+ 0xf57ff04f, /* dsb */
+ 0xe320f003, /* wfi */
+ 0xe5901000, /* ldr r1, [r0] */
+ 0xe1110001, /* tst r1, r1 */
+ 0x0afffffb, /* beq <wfi> */
+ 0xe12fff11, /* bx r1 */
+ GIC_BASE_ADDR /* privbase: gic address. */
+ };
+ for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
+ smpboot[n] = tswap32(smpboot[n]);
+ }
+ rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR);
+}
+
+static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ CPUARMState *env = &cpu->env;
+
+ switch (info->nb_cpus) {
+ case 4:
+ stl_phys_notdirty(SMP_BOOT_REG + 0x30, 0);
+ case 3:
+ stl_phys_notdirty(SMP_BOOT_REG + 0x20, 0);
+ case 2:
+ stl_phys_notdirty(SMP_BOOT_REG + 0x10, 0);
+ env->regs[15] = SMP_BOOT_ADDR;
+ break;
+ default:
+ break;
+ }
+}
+
+#define NUM_REGS 0x200
+static void hb_regs_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ uint32_t *regs = opaque;
+
+ if (offset == 0xf00) {
+ if (value == 1 || value == 2) {
+ qemu_system_reset_request();
+ } else if (value == 3) {
+ qemu_system_shutdown_request();
+ }
+ }
+
+ regs[offset/4] = value;
+}
+
+static uint64_t hb_regs_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint32_t *regs = opaque;
+ uint32_t value = regs[offset/4];
+
+ if ((offset == 0x100) || (offset == 0x108) || (offset == 0x10C)) {
+ value |= 0x30000000;
+ }
+
+ return value;
+}
+
+static const MemoryRegionOps hb_mem_ops = {
+ .read = hb_regs_read,
+ .write = hb_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion *iomem;
+ uint32_t regs[NUM_REGS];
+} HighbankRegsState;
+
+static VMStateDescription vmstate_highbank_regs = {
+ .name = "highbank-regs",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, HighbankRegsState, NUM_REGS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void highbank_regs_reset(DeviceState *dev)
+{
+ SysBusDevice *sys_dev = SYS_BUS_DEVICE(dev);
+ HighbankRegsState *s = FROM_SYSBUS(HighbankRegsState, sys_dev);
+
+ s->regs[0x40] = 0x05F20121;
+ s->regs[0x41] = 0x2;
+ s->regs[0x42] = 0x05F30121;
+ s->regs[0x43] = 0x05F40121;
+}
+
+static int highbank_regs_init(SysBusDevice *dev)
+{
+ HighbankRegsState *s = FROM_SYSBUS(HighbankRegsState, dev);
+
+ s->iomem = g_new(MemoryRegion, 1);
+ memory_region_init_io(s->iomem, &hb_mem_ops, s->regs, "highbank_regs",
+ 0x1000);
+ sysbus_init_mmio(dev, s->iomem);
+
+ return 0;
+}
+
+static void highbank_regs_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ sbc->init = highbank_regs_init;
+ dc->desc = "Calxeda Highbank registers";
+ dc->vmsd = &vmstate_highbank_regs;
+ dc->reset = highbank_regs_reset;
+}
+
+static const TypeInfo highbank_regs_info = {
+ .name = "highbank-regs",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(HighbankRegsState),
+ .class_init = highbank_regs_class_init,
+};
+
+static void highbank_regs_register_types(void)
+{
+ type_register_static(&highbank_regs_info);
+}
+
+type_init(highbank_regs_register_types)
+
+static struct arm_boot_info highbank_binfo;
+
+/* ram_size must be set to match the upper bound of memory in the
+ * device tree (linux/arch/arm/boot/dts/highbank.dts), which is
+ * normally 0xff900000 or -m 4089. When running this board on a
+ * 32-bit host, set the reg value of memory to 0xf7ff00000 in the
+ * device tree and pass -m 2047 to QEMU.
+ */
+static void highbank_init(QEMUMachineInitArgs *args)
+{
+ ram_addr_t ram_size = args->ram_size;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ qemu_irq *irqp;
+ qemu_irq pic[128];
+ int n;
+ qemu_irq cpu_irq[4];
+ MemoryRegion *sysram;
+ MemoryRegion *dram;
+ MemoryRegion *sysmem;
+ char *sysboot_filename;
+
+ if (!cpu_model) {
+ cpu_model = "cortex-a9";
+ }
+
+ for (n = 0; n < smp_cpus; n++) {
+ ARMCPU *cpu;
+ cpu = cpu_arm_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ /* This will become a QOM property eventually */
+ cpu->reset_cbar = GIC_BASE_ADDR;
+ irqp = arm_pic_init_cpu(cpu);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ sysmem = get_system_memory();
+ dram = g_new(MemoryRegion, 1);
+ memory_region_init_ram(dram, "highbank.dram", ram_size);
+ /* SDRAM at address zero. */
+ memory_region_add_subregion(sysmem, 0, dram);
+
+ sysram = g_new(MemoryRegion, 1);
+ memory_region_init_ram(sysram, "highbank.sysram", 0x8000);
+ memory_region_add_subregion(sysmem, 0xfff88000, sysram);
+ if (bios_name != NULL) {
+ sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (sysboot_filename != NULL) {
+ uint32_t filesize = get_image_size(sysboot_filename);
+ if (load_image_targphys("sysram.bin", 0xfff88000, filesize) < 0) {
+ hw_error("Unable to load %s\n", bios_name);
+ }
+ } else {
+ hw_error("Unable to find %s\n", bios_name);
+ }
+ }
+
+ dev = qdev_create(NULL, "a9mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ 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);
+ for (n = 0; n < smp_cpus; n++) {
+ sysbus_connect_irq(busdev, n, cpu_irq[n]);
+ }
+
+ for (n = 0; n < 128; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ dev = qdev_create(NULL, "l2x0");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0xfff12000);
+
+ dev = qdev_create(NULL, "sp804");
+ qdev_prop_set_uint32(dev, "freq0", 150000000);
+ qdev_prop_set_uint32(dev, "freq1", 150000000);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0xfff34000);
+ sysbus_connect_irq(busdev, 0, pic[18]);
+ sysbus_create_simple("pl011", 0xfff36000, pic[20]);
+
+ dev = qdev_create(NULL, "highbank-regs");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0xfff3c000);
+
+ sysbus_create_simple("pl061", 0xfff30000, pic[14]);
+ sysbus_create_simple("pl061", 0xfff31000, pic[15]);
+ sysbus_create_simple("pl061", 0xfff32000, pic[16]);
+ sysbus_create_simple("pl061", 0xfff33000, pic[17]);
+ sysbus_create_simple("pl031", 0xfff35000, pic[19]);
+ sysbus_create_simple("pl022", 0xfff39000, pic[23]);
+
+ sysbus_create_simple("sysbus-ahci", 0xffe08000, pic[83]);
+
+ if (nd_table[0].used) {
+ qemu_check_nic_model(&nd_table[0], "xgmac");
+ dev = qdev_create(NULL, "xgmac");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]);
+
+ qemu_check_nic_model(&nd_table[1], "xgmac");
+ dev = qdev_create(NULL, "xgmac");
+ qdev_set_nic_properties(dev, &nd_table[1]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[81]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]);
+ }
+
+ highbank_binfo.ram_size = ram_size;
+ highbank_binfo.kernel_filename = kernel_filename;
+ highbank_binfo.kernel_cmdline = kernel_cmdline;
+ highbank_binfo.initrd_filename = initrd_filename;
+ /* highbank requires a dtb in order to boot, and the dtb will override
+ * the board ID. The following value is ignored, so set it to -1 to be
+ * clear that the value is meaningless.
+ */
+ highbank_binfo.board_id = -1;
+ highbank_binfo.nb_cpus = smp_cpus;
+ highbank_binfo.loader_start = 0;
+ highbank_binfo.write_secondary_boot = hb_write_secondary;
+ highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary;
+ arm_load_kernel(arm_env_get_cpu(first_cpu), &highbank_binfo);
+}
+
+static QEMUMachine highbank_machine = {
+ .name = "highbank",
+ .desc = "Calxeda Highbank (ECX-1000)",
+ .init = highbank_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 4,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void highbank_machine_init(void)
+{
+ qemu_register_machine(&highbank_machine);
+}
+
+machine_init(highbank_machine_init);
diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c
new file mode 100644
index 0000000..e0ba327
--- /dev/null
+++ b/hw/arm/integratorcp.c
@@ -0,0 +1,566 @@
+/*
+ * ARM Integrator CP System emulation.
+ *
+ * Copyright (c) 2005-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "hw/boards.h"
+#include "hw/arm-misc.h"
+#include "net/net.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t memsz;
+ MemoryRegion flash;
+ uint32_t cm_osc;
+ uint32_t cm_ctrl;
+ uint32_t cm_lock;
+ uint32_t cm_auxosc;
+ uint32_t cm_sdram;
+ uint32_t cm_init;
+ uint32_t cm_flags;
+ uint32_t cm_nvflags;
+ uint32_t int_level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+} integratorcm_state;
+
+static uint8_t integrator_spd[128] = {
+ 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1,
+ 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40
+};
+
+static uint64_t integratorcm_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ if (offset >= 0x100 && offset < 0x200) {
+ /* CM_SPD */
+ if (offset >= 0x180)
+ return 0;
+ return integrator_spd[offset >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* CM_ID */
+ return 0x411a3001;
+ case 1: /* CM_PROC */
+ return 0;
+ case 2: /* CM_OSC */
+ return s->cm_osc;
+ case 3: /* CM_CTRL */
+ return s->cm_ctrl;
+ case 4: /* CM_STAT */
+ return 0x00100000;
+ case 5: /* CM_LOCK */
+ if (s->cm_lock == 0xa05f) {
+ return 0x1a05f;
+ } else {
+ return s->cm_lock;
+ }
+ case 6: /* CM_LMBUSCNT */
+ /* ??? High frequency timer. */
+ hw_error("integratorcm_read: CM_LMBUSCNT");
+ case 7: /* CM_AUXOSC */
+ return s->cm_auxosc;
+ case 8: /* CM_SDRAM */
+ return s->cm_sdram;
+ case 9: /* CM_INIT */
+ return s->cm_init;
+ case 10: /* CM_REFCT */
+ /* ??? High frequency timer. */
+ hw_error("integratorcm_read: CM_REFCT");
+ case 12: /* CM_FLAGS */
+ return s->cm_flags;
+ case 14: /* CM_NVFLAGS */
+ return s->cm_nvflags;
+ case 16: /* CM_IRQ_STAT */
+ return s->int_level & s->irq_enabled;
+ case 17: /* CM_IRQ_RSTAT */
+ return s->int_level;
+ case 18: /* CM_IRQ_ENSET */
+ return s->irq_enabled;
+ case 20: /* CM_SOFT_INTSET */
+ return s->int_level & 1;
+ case 24: /* CM_FIQ_STAT */
+ return s->int_level & s->fiq_enabled;
+ case 25: /* CM_FIQ_RSTAT */
+ return s->int_level;
+ case 26: /* CM_FIQ_ENSET */
+ return s->fiq_enabled;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ return 0;
+ default:
+ hw_error("integratorcm_read: Unimplemented offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void integratorcm_do_remap(integratorcm_state *s)
+{
+ /* Sync memory region state with CM_CTRL REMAP bit:
+ * bit 0 => flash at address 0; bit 1 => RAM
+ */
+ memory_region_set_enabled(&s->flash, !(s->cm_ctrl & 4));
+}
+
+static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value)
+{
+ if (value & 8) {
+ qemu_system_reset_request();
+ }
+ if ((s->cm_ctrl ^ value) & 1) {
+ /* (value & 1) != 0 means the green "MISC LED" is lit.
+ * We don't have any nice place to display LEDs. printf is a bad
+ * idea because Linux uses the LED as a heartbeat and the output
+ * will swamp anything else on the terminal.
+ */
+ }
+ /* Note that the RESET bit [3] always reads as zero */
+ s->cm_ctrl = (s->cm_ctrl & ~5) | (value & 5);
+ integratorcm_do_remap(s);
+}
+
+static void integratorcm_update(integratorcm_state *s)
+{
+ /* ??? The CPU irq/fiq is raised when either the core module or base PIC
+ are active. */
+ if (s->int_level & (s->irq_enabled | s->fiq_enabled))
+ hw_error("Core module interrupt\n");
+}
+
+static void integratorcm_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ switch (offset >> 2) {
+ case 2: /* CM_OSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_osc = value;
+ break;
+ case 3: /* CM_CTRL */
+ integratorcm_set_ctrl(s, value);
+ break;
+ case 5: /* CM_LOCK */
+ s->cm_lock = value & 0xffff;
+ break;
+ case 7: /* CM_AUXOSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_auxosc = value;
+ break;
+ case 8: /* CM_SDRAM */
+ s->cm_sdram = value;
+ break;
+ case 9: /* CM_INIT */
+ /* ??? This can change the memory bus frequency. */
+ s->cm_init = value;
+ break;
+ case 12: /* CM_FLAGSS */
+ s->cm_flags |= value;
+ break;
+ case 13: /* CM_FLAGSC */
+ s->cm_flags &= ~value;
+ break;
+ case 14: /* CM_NVFLAGSS */
+ s->cm_nvflags |= value;
+ break;
+ case 15: /* CM_NVFLAGSS */
+ s->cm_nvflags &= ~value;
+ break;
+ case 18: /* CM_IRQ_ENSET */
+ s->irq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 19: /* CM_IRQ_ENCLR */
+ s->irq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 20: /* CM_SOFT_INTSET */
+ s->int_level |= (value & 1);
+ integratorcm_update(s);
+ break;
+ case 21: /* CM_SOFT_INTCLR */
+ s->int_level &= ~(value & 1);
+ integratorcm_update(s);
+ break;
+ case 26: /* CM_FIQ_ENSET */
+ s->fiq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 27: /* CM_FIQ_ENCLR */
+ s->fiq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ break;
+ default:
+ hw_error("integratorcm_write: Unimplemented offset 0x%x\n",
+ (int)offset);
+ break;
+ }
+}
+
+/* Integrator/CM control registers. */
+
+static const MemoryRegionOps integratorcm_ops = {
+ .read = integratorcm_read,
+ .write = integratorcm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int integratorcm_init(SysBusDevice *dev)
+{
+ integratorcm_state *s = FROM_SYSBUS(integratorcm_state, dev);
+
+ s->cm_osc = 0x01000048;
+ /* ??? What should the high bits of this value be? */
+ s->cm_auxosc = 0x0007feff;
+ s->cm_sdram = 0x00011122;
+ if (s->memsz >= 256) {
+ integrator_spd[31] = 64;
+ s->cm_sdram |= 0x10;
+ } else if (s->memsz >= 128) {
+ integrator_spd[31] = 32;
+ s->cm_sdram |= 0x0c;
+ } else if (s->memsz >= 64) {
+ integrator_spd[31] = 16;
+ s->cm_sdram |= 0x08;
+ } else if (s->memsz >= 32) {
+ integrator_spd[31] = 4;
+ s->cm_sdram |= 0x04;
+ } else {
+ integrator_spd[31] = 2;
+ }
+ memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
+ s->cm_init = 0x00000112;
+ memory_region_init_ram(&s->flash, "integrator.flash", 0x100000);
+ vmstate_register_ram_global(&s->flash);
+
+ memory_region_init_io(&s->iomem, &integratorcm_ops, s,
+ "integratorcm", 0x00800000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ integratorcm_do_remap(s);
+ /* ??? Save/restore. */
+ return 0;
+}
+
+/* Integrator/CP hardware emulation. */
+/* Primary interrupt controller. */
+
+typedef struct icp_pic_state
+{
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+} icp_pic_state;
+
+static void icp_pic_update(icp_pic_state *s)
+{
+ uint32_t flags;
+
+ flags = (s->level & s->irq_enabled);
+ qemu_set_irq(s->parent_irq, flags != 0);
+ flags = (s->level & s->fiq_enabled);
+ qemu_set_irq(s->parent_fiq, flags != 0);
+}
+
+static void icp_pic_set_irq(void *opaque, int irq, int level)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+ if (level)
+ s->level |= 1 << irq;
+ else
+ s->level &= ~(1 << irq);
+ icp_pic_update(s);
+}
+
+static uint64_t icp_pic_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* IRQ_STATUS */
+ return s->level & s->irq_enabled;
+ case 1: /* IRQ_RAWSTAT */
+ return s->level;
+ case 2: /* IRQ_ENABLESET */
+ return s->irq_enabled;
+ case 4: /* INT_SOFTSET */
+ return s->level & 1;
+ case 8: /* FRQ_STATUS */
+ return s->level & s->fiq_enabled;
+ case 9: /* FRQ_RAWSTAT */
+ return s->level;
+ case 10: /* FRQ_ENABLESET */
+ return s->fiq_enabled;
+ case 3: /* IRQ_ENABLECLR */
+ case 5: /* INT_SOFTCLR */
+ case 11: /* FRQ_ENABLECLR */
+ default:
+ printf ("icp_pic_read: Bad register offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void icp_pic_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 2: /* IRQ_ENABLESET */
+ s->irq_enabled |= value;
+ break;
+ case 3: /* IRQ_ENABLECLR */
+ s->irq_enabled &= ~value;
+ break;
+ case 4: /* INT_SOFTSET */
+ if (value & 1)
+ icp_pic_set_irq(s, 0, 1);
+ break;
+ case 5: /* INT_SOFTCLR */
+ if (value & 1)
+ icp_pic_set_irq(s, 0, 0);
+ break;
+ case 10: /* FRQ_ENABLESET */
+ s->fiq_enabled |= value;
+ break;
+ case 11: /* FRQ_ENABLECLR */
+ s->fiq_enabled &= ~value;
+ break;
+ case 0: /* IRQ_STATUS */
+ case 1: /* IRQ_RAWSTAT */
+ case 8: /* FRQ_STATUS */
+ case 9: /* FRQ_RAWSTAT */
+ default:
+ printf ("icp_pic_write: Bad register offset 0x%x\n", (int)offset);
+ return;
+ }
+ icp_pic_update(s);
+}
+
+static const MemoryRegionOps icp_pic_ops = {
+ .read = icp_pic_read,
+ .write = icp_pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int icp_pic_init(SysBusDevice *dev)
+{
+ icp_pic_state *s = FROM_SYSBUS(icp_pic_state, dev);
+
+ qdev_init_gpio_in(&dev->qdev, icp_pic_set_irq, 32);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_fiq);
+ memory_region_init_io(&s->iomem, &icp_pic_ops, s, "icp-pic", 0x00800000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+/* CP control registers. */
+
+static uint64_t icp_control_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ switch (offset >> 2) {
+ case 0: /* CP_IDFIELD */
+ return 0x41034003;
+ case 1: /* CP_FLASHPROG */
+ return 0;
+ case 2: /* CP_INTREG */
+ return 0;
+ case 3: /* CP_DECODE */
+ return 0x11;
+ default:
+ hw_error("icp_control_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void icp_control_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ switch (offset >> 2) {
+ case 1: /* CP_FLASHPROG */
+ case 2: /* CP_INTREG */
+ case 3: /* CP_DECODE */
+ /* Nothing interesting implemented yet. */
+ break;
+ default:
+ hw_error("icp_control_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps icp_control_ops = {
+ .read = icp_control_read,
+ .write = icp_control_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void icp_control_init(hwaddr base)
+{
+ MemoryRegion *io;
+
+ io = (MemoryRegion *)g_malloc0(sizeof(MemoryRegion));
+ memory_region_init_io(io, &icp_control_ops, NULL,
+ "control", 0x00800000);
+ memory_region_add_subregion(get_system_memory(), base, io);
+ /* ??? Save/restore. */
+}
+
+
+/* Board init. */
+
+static struct arm_boot_info integrator_binfo = {
+ .loader_start = 0x0,
+ .board_id = 0x113,
+};
+
+static void integratorcp_init(QEMUMachineInitArgs *args)
+{
+ ram_addr_t ram_size = args->ram_size;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ ARMCPU *cpu;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
+ qemu_irq pic[32];
+ qemu_irq *cpu_pic;
+ DeviceState *dev;
+ int i;
+
+ if (!cpu_model) {
+ cpu_model = "arm926";
+ }
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ memory_region_init_ram(ram, "integrator.ram", ram_size);
+ vmstate_register_ram_global(ram);
+ /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */
+ /* ??? RAM should repeat to fill physical memory space. */
+ /* SDRAM at address zero*/
+ memory_region_add_subregion(address_space_mem, 0, ram);
+ /* And again at address 0x80000000 */
+ memory_region_init_alias(ram_alias, "ram.alias", ram, 0, ram_size);
+ memory_region_add_subregion(address_space_mem, 0x80000000, ram_alias);
+
+ dev = qdev_create(NULL, "integrator_core");
+ qdev_prop_set_uint32(dev, "memsz", ram_size >> 20);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map((SysBusDevice *)dev, 0, 0x10000000);
+
+ cpu_pic = arm_pic_init_cpu(cpu);
+ dev = sysbus_create_varargs("integrator_pic", 0x14000000,
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+ for (i = 0; i < 32; i++) {
+ pic[i] = qdev_get_gpio_in(dev, i);
+ }
+ sysbus_create_simple("integrator_pic", 0xca000000, pic[26]);
+ sysbus_create_varargs("integrator_pit", 0x13000000,
+ pic[5], pic[6], pic[7], NULL);
+ sysbus_create_simple("pl031", 0x15000000, pic[8]);
+ sysbus_create_simple("pl011", 0x16000000, pic[1]);
+ sysbus_create_simple("pl011", 0x17000000, pic[2]);
+ icp_control_init(0xcb000000);
+ sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]);
+ sysbus_create_simple("pl050_mouse", 0x19000000, pic[4]);
+ sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL);
+ if (nd_table[0].used)
+ smc91c111_init(&nd_table[0], 0xc8000000, pic[27]);
+
+ sysbus_create_simple("pl110", 0xc0000000, pic[22]);
+
+ integrator_binfo.ram_size = ram_size;
+ integrator_binfo.kernel_filename = kernel_filename;
+ integrator_binfo.kernel_cmdline = kernel_cmdline;
+ integrator_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(cpu, &integrator_binfo);
+}
+
+static QEMUMachine integratorcp_machine = {
+ .name = "integratorcp",
+ .desc = "ARM Integrator/CP (ARM926EJ-S)",
+ .init = integratorcp_init,
+ .is_default = 1,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void integratorcp_machine_init(void)
+{
+ qemu_register_machine(&integratorcp_machine);
+}
+
+machine_init(integratorcp_machine_init);
+
+static Property core_properties[] = {
+ DEFINE_PROP_UINT32("memsz", integratorcm_state, memsz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void core_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = integratorcm_init;
+ dc->props = core_properties;
+}
+
+static const TypeInfo core_info = {
+ .name = "integrator_core",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(integratorcm_state),
+ .class_init = core_class_init,
+};
+
+static void icp_pic_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = icp_pic_init;
+}
+
+static const TypeInfo icp_pic_info = {
+ .name = "integrator_pic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(icp_pic_state),
+ .class_init = icp_pic_class_init,
+};
+
+static void integratorcp_register_types(void)
+{
+ type_register_static(&icp_pic_info);
+ type_register_static(&core_info);
+}
+
+type_init(integratorcp_register_types)
diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c
new file mode 100644
index 0000000..ec50a31
--- /dev/null
+++ b/hw/arm/kzm.c
@@ -0,0 +1,157 @@
+/*
+ * KZM Board System emulation.
+ *
+ * Copyright (c) 2008 OKL and 2011 NICTA
+ * Written by Hans at OK-Labs
+ * Updated by Peter Chubb.
+ *
+ * This code is licensed under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a Kyoto Microcomputer
+ * KZM-ARM11-01 evaluation board, with a Freescale
+ * i.MX31 SoC
+ */
+
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+#include "hw/hw.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/serial.h"
+#include "hw/imx.h"
+
+ /* Memory map for Kzm Emulation Baseboard:
+ * 0x00000000-0x00003fff 16k secure ROM IGNORED
+ * 0x00004000-0x00407fff Reserved IGNORED
+ * 0x00404000-0x00407fff ROM IGNORED
+ * 0x00408000-0x0fffffff Reserved IGNORED
+ * 0x10000000-0x1fffbfff RAM aliasing IGNORED
+ * 0x1fffc000-0x1fffffff RAM EMULATED
+ * 0x20000000-0x2fffffff Reserved IGNORED
+ * 0x30000000-0x7fffffff I.MX31 Internal Register Space
+ * 0x43f00000 IO_AREA0
+ * 0x43f90000 UART1 EMULATED
+ * 0x43f94000 UART2 EMULATED
+ * 0x68000000 AVIC EMULATED
+ * 0x53f80000 CCM EMULATED
+ * 0x53f94000 PIT 1 EMULATED
+ * 0x53f98000 PIT 2 EMULATED
+ * 0x53f90000 GPT EMULATED
+ * 0x80000000-0x87ffffff RAM EMULATED
+ * 0x88000000-0x8fffffff RAM Aliasing EMULATED
+ * 0xa0000000-0xafffffff NAND Flash IGNORED
+ * 0xb0000000-0xb3ffffff Unavailable IGNORED
+ * 0xb4000000-0xb4000fff 8-bit free space IGNORED
+ * 0xb4001000-0xb400100f Board control IGNORED
+ * 0xb4001003 DIP switch
+ * 0xb4001010-0xb400101f 7-segment LED IGNORED
+ * 0xb4001020-0xb400102f LED IGNORED
+ * 0xb4001030-0xb400103f LED IGNORED
+ * 0xb4001040-0xb400104f FPGA, UART EMULATED
+ * 0xb4001050-0xb400105f FPGA, UART EMULATED
+ * 0xb4001060-0xb40fffff FPGA IGNORED
+ * 0xb6000000-0xb61fffff LAN controller EMULATED
+ * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
+ * 0xb6300000-0xb7ffffff Free IGNORED
+ * 0xb8000000-0xb8004fff Memory control registers IGNORED
+ * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
+ * 0xc4000000-0xffffffff Reserved IGNORED
+ */
+
+#define KZM_RAMADDRESS (0x80000000)
+#define KZM_FPGA (0xb4001040)
+
+static struct arm_boot_info kzm_binfo = {
+ .loader_start = KZM_RAMADDRESS,
+ .board_id = 1722,
+};
+
+static void kzm_init(QEMUMachineInitArgs *args)
+{
+ ram_addr_t ram_size = args->ram_size;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ ARMCPU *cpu;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
+ qemu_irq *cpu_pic;
+ DeviceState *dev;
+ DeviceState *ccm;
+
+ if (!cpu_model) {
+ cpu_model = "arm1136";
+ }
+
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ /* On a real system, the first 16k is a `secure boot rom' */
+
+ memory_region_init_ram(ram, "kzm.ram", ram_size);
+ vmstate_register_ram_global(ram);
+ memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram);
+
+ memory_region_init_alias(ram_alias, "ram.alias", ram, 0, ram_size);
+ memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias);
+
+ memory_region_init_ram(sram, "kzm.sram", 0x4000);
+ memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram);
+
+ cpu_pic = arm_pic_init_cpu(cpu);
+ dev = sysbus_create_varargs("imx_avic", 0x68000000,
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+
+
+ imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45));
+ imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32));
+
+ ccm = sysbus_create_simple("imx_ccm", 0x53f80000, NULL);
+
+ imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm);
+ imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm);
+ imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm);
+
+ if (nd_table[0].used) {
+ lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52));
+ }
+
+ if (serial_hds[2]) { /* touchscreen */
+ serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0,
+ qdev_get_gpio_in(dev, 52),
+ 14745600, serial_hds[2],
+ DEVICE_NATIVE_ENDIAN);
+ }
+
+ kzm_binfo.ram_size = ram_size;
+ kzm_binfo.kernel_filename = kernel_filename;
+ kzm_binfo.kernel_cmdline = kernel_cmdline;
+ kzm_binfo.initrd_filename = initrd_filename;
+ kzm_binfo.nb_cpus = 1;
+ arm_load_kernel(cpu, &kzm_binfo);
+}
+
+static QEMUMachine kzm_machine = {
+ .name = "kzm",
+ .desc = "ARM KZM Emulation Baseboard (ARM1136)",
+ .init = kzm_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void kzm_machine_init(void)
+{
+ qemu_register_machine(&kzm_machine);
+}
+
+machine_init(kzm_machine_init)
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
new file mode 100644
index 0000000..aea908f
--- /dev/null
+++ b/hw/arm/mainstone.c
@@ -0,0 +1,190 @@
+/*
+ * PXA270-based Intel Mainstone platforms.
+ *
+ * Copyright (c) 2007 by Armin Kuster <akuster@kama-aina.net> or
+ * <akuster@mvista.com>
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "hw/arm-misc.h"
+#include "net/net.h"
+#include "hw/devices.h"
+#include "hw/boards.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+/* Device addresses */
+#define MST_FPGA_PHYS 0x08000000
+#define MST_ETH_PHYS 0x10000300
+#define MST_FLASH_0 0x00000000
+#define MST_FLASH_1 0x04000000
+
+/* IRQ definitions */
+#define MMC_IRQ 0
+#define USIM_IRQ 1
+#define USBC_IRQ 2
+#define ETHERNET_IRQ 3
+#define AC97_IRQ 4
+#define PEN_IRQ 5
+#define MSINS_IRQ 6
+#define EXBRD_IRQ 7
+#define S0_CD_IRQ 9
+#define S0_STSCHG_IRQ 10
+#define S0_IRQ 11
+#define S1_CD_IRQ 13
+#define S1_STSCHG_IRQ 14
+#define S1_IRQ 15
+
+static struct keymap map[0xE0] = {
+ [0 ... 0xDF] = { -1, -1 },
+ [0x1e] = {0,0}, /* a */
+ [0x30] = {0,1}, /* b */
+ [0x2e] = {0,2}, /* c */
+ [0x20] = {0,3}, /* d */
+ [0x12] = {0,4}, /* e */
+ [0x21] = {0,5}, /* f */
+ [0x22] = {1,0}, /* g */
+ [0x23] = {1,1}, /* h */
+ [0x17] = {1,2}, /* i */
+ [0x24] = {1,3}, /* j */
+ [0x25] = {1,4}, /* k */
+ [0x26] = {1,5}, /* l */
+ [0x32] = {2,0}, /* m */
+ [0x31] = {2,1}, /* n */
+ [0x18] = {2,2}, /* o */
+ [0x19] = {2,3}, /* p */
+ [0x10] = {2,4}, /* q */
+ [0x13] = {2,5}, /* r */
+ [0x1f] = {3,0}, /* s */
+ [0x14] = {3,1}, /* t */
+ [0x16] = {3,2}, /* u */
+ [0x2f] = {3,3}, /* v */
+ [0x11] = {3,4}, /* w */
+ [0x2d] = {3,5}, /* x */
+ [0x15] = {4,2}, /* y */
+ [0x2c] = {4,3}, /* z */
+ [0xc7] = {5,0}, /* Home */
+ [0x2a] = {5,1}, /* shift */
+ [0x39] = {5,2}, /* space */
+ [0x39] = {5,3}, /* space */
+ [0x1c] = {5,5}, /* enter */
+ [0xc8] = {6,0}, /* up */
+ [0xd0] = {6,1}, /* down */
+ [0xcb] = {6,2}, /* left */
+ [0xcd] = {6,3}, /* right */
+};
+
+enum mainstone_model_e { mainstone };
+
+#define MAINSTONE_RAM 0x04000000
+#define MAINSTONE_ROM 0x00800000
+#define MAINSTONE_FLASH 0x02000000
+
+static struct arm_boot_info mainstone_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void mainstone_common_init(MemoryRegion *address_space_mem,
+ QEMUMachineInitArgs *args,
+ enum mainstone_model_e model, int arm_id)
+{
+ uint32_t sector_len = 256 * 1024;
+ hwaddr mainstone_flash_base[] = { MST_FLASH_0, MST_FLASH_1 };
+ PXA2xxState *mpu;
+ DeviceState *mst_irq;
+ DriveInfo *dinfo;
+ int i;
+ int be;
+ MemoryRegion *rom = g_new(MemoryRegion, 1);
+ const char *cpu_model = args->cpu_model;
+
+ if (!cpu_model)
+ cpu_model = "pxa270-c5";
+
+ /* Setup CPU & memory */
+ mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, cpu_model);
+ memory_region_init_ram(rom, "mainstone.rom", MAINSTONE_ROM);
+ vmstate_register_ram_global(rom);
+ memory_region_set_readonly(rom, true);
+ memory_region_add_subregion(address_space_mem, 0, rom);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ /* There are two 32MiB flash devices on the board */
+ for (i = 0; i < 2; i ++) {
+ dinfo = drive_get(IF_PFLASH, 0, i);
+ if (!dinfo) {
+ fprintf(stderr, "Two flash images must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+ if (!pflash_cfi01_register(mainstone_flash_base[i], NULL,
+ i ? "mainstone.flash1" : "mainstone.flash0",
+ MAINSTONE_FLASH,
+ dinfo->bdrv, sector_len,
+ MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0,
+ be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+ }
+
+ mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS,
+ qdev_get_gpio_in(mpu->gpio, 0));
+
+ /* setup keypad */
+ printf("map addr %p\n", &map);
+ pxa27x_register_keypad(mpu->kp, map, 0xe0);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(mpu->mmc, NULL, qdev_get_gpio_in(mst_irq, MMC_IRQ));
+
+ pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[0],
+ qdev_get_gpio_in(mst_irq, S0_IRQ),
+ qdev_get_gpio_in(mst_irq, S0_CD_IRQ));
+ pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[1],
+ qdev_get_gpio_in(mst_irq, S1_IRQ),
+ qdev_get_gpio_in(mst_irq, S1_CD_IRQ));
+
+ smc91c111_init(&nd_table[0], MST_ETH_PHYS,
+ qdev_get_gpio_in(mst_irq, ETHERNET_IRQ));
+
+ mainstone_binfo.kernel_filename = args->kernel_filename;
+ mainstone_binfo.kernel_cmdline = args->kernel_cmdline;
+ mainstone_binfo.initrd_filename = args->initrd_filename;
+ mainstone_binfo.board_id = arm_id;
+ arm_load_kernel(mpu->cpu, &mainstone_binfo);
+}
+
+static void mainstone_init(QEMUMachineInitArgs *args)
+{
+ mainstone_common_init(get_system_memory(), args, mainstone, 0x196);
+}
+
+static QEMUMachine mainstone2_machine = {
+ .name = "mainstone",
+ .desc = "Mainstone II (PXA27x)",
+ .init = mainstone_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void mainstone_machine_init(void)
+{
+ qemu_register_machine(&mainstone2_machine);
+}
+
+machine_init(mainstone_machine_init);
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
new file mode 100644
index 0000000..a37dbd7
--- /dev/null
+++ b/hw/arm/musicpal.c
@@ -0,0 +1,1697 @@
+/*
+ * Marvell MV88W8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008 Jan Kiszka
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/serial.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "block/block.h"
+#include "hw/flash.h"
+#include "ui/console.h"
+#include "hw/i2c.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+#include "ui/pixel_ops.h"
+
+#define MP_MISC_BASE 0x80002000
+#define MP_MISC_SIZE 0x00001000
+
+#define MP_ETH_BASE 0x80008000
+#define MP_ETH_SIZE 0x00001000
+
+#define MP_WLAN_BASE 0x8000C000
+#define MP_WLAN_SIZE 0x00000800
+
+#define MP_UART1_BASE 0x8000C840
+#define MP_UART2_BASE 0x8000C940
+
+#define MP_GPIO_BASE 0x8000D000
+#define MP_GPIO_SIZE 0x00001000
+
+#define MP_FLASHCFG_BASE 0x90006000
+#define MP_FLASHCFG_SIZE 0x00001000
+
+#define MP_AUDIO_BASE 0x90007000
+
+#define MP_PIC_BASE 0x90008000
+#define MP_PIC_SIZE 0x00001000
+
+#define MP_PIT_BASE 0x90009000
+#define MP_PIT_SIZE 0x00001000
+
+#define MP_LCD_BASE 0x9000c000
+#define MP_LCD_SIZE 0x00001000
+
+#define MP_SRAM_BASE 0xC0000000
+#define MP_SRAM_SIZE 0x00020000
+
+#define MP_RAM_DEFAULT_SIZE 32*1024*1024
+#define MP_FLASH_SIZE_MAX 32*1024*1024
+
+#define MP_TIMER1_IRQ 4
+#define MP_TIMER2_IRQ 5
+#define MP_TIMER3_IRQ 6
+#define MP_TIMER4_IRQ 7
+#define MP_EHCI_IRQ 8
+#define MP_ETH_IRQ 9
+#define MP_UART1_IRQ 11
+#define MP_UART2_IRQ 11
+#define MP_GPIO_IRQ 12
+#define MP_RTC_IRQ 28
+#define MP_AUDIO_IRQ 30
+
+/* Wolfson 8750 I2C address */
+#define MP_WM_ADDR 0x1A
+
+/* Ethernet register offsets */
+#define MP_ETH_SMIR 0x010
+#define MP_ETH_PCXR 0x408
+#define MP_ETH_SDCMR 0x448
+#define MP_ETH_ICR 0x450
+#define MP_ETH_IMR 0x458
+#define MP_ETH_FRDP0 0x480
+#define MP_ETH_FRDP1 0x484
+#define MP_ETH_FRDP2 0x488
+#define MP_ETH_FRDP3 0x48C
+#define MP_ETH_CRDP0 0x4A0
+#define MP_ETH_CRDP1 0x4A4
+#define MP_ETH_CRDP2 0x4A8
+#define MP_ETH_CRDP3 0x4AC
+#define MP_ETH_CTDP0 0x4E0
+#define MP_ETH_CTDP1 0x4E4
+#define MP_ETH_CTDP2 0x4E8
+#define MP_ETH_CTDP3 0x4EC
+
+/* MII PHY access */
+#define MP_ETH_SMIR_DATA 0x0000FFFF
+#define MP_ETH_SMIR_ADDR 0x03FF0000
+#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
+#define MP_ETH_SMIR_RDVALID (1 << 27)
+
+/* PHY registers */
+#define MP_ETH_PHY1_BMSR 0x00210000
+#define MP_ETH_PHY1_PHYSID1 0x00410000
+#define MP_ETH_PHY1_PHYSID2 0x00610000
+
+#define MP_PHY_BMSR_LINK 0x0004
+#define MP_PHY_BMSR_AUTONEG 0x0008
+
+#define MP_PHY_88E3015 0x01410E20
+
+/* TX descriptor status */
+#define MP_ETH_TX_OWN (1 << 31)
+
+/* RX descriptor status */
+#define MP_ETH_RX_OWN (1 << 31)
+
+/* Interrupt cause/mask bits */
+#define MP_ETH_IRQ_RX_BIT 0
+#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
+#define MP_ETH_IRQ_TXHI_BIT 2
+#define MP_ETH_IRQ_TXLO_BIT 3
+
+/* Port config bits */
+#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
+
+/* SDMA command bits */
+#define MP_ETH_CMD_TXHI (1 << 23)
+#define MP_ETH_CMD_TXLO (1 << 22)
+
+typedef struct mv88w8618_tx_desc {
+ uint32_t cmdstat;
+ uint16_t res;
+ uint16_t bytes;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_tx_desc;
+
+typedef struct mv88w8618_rx_desc {
+ uint32_t cmdstat;
+ uint16_t bytes;
+ uint16_t buffer_size;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_rx_desc;
+
+typedef struct mv88w8618_eth_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+ uint32_t smir;
+ uint32_t icr;
+ uint32_t imr;
+ int mmio_index;
+ uint32_t vlan_header;
+ uint32_t tx_queue[2];
+ uint32_t rx_queue[4];
+ uint32_t frx_queue[4];
+ uint32_t cur_rx[4];
+ NICState *nic;
+ NICConf conf;
+} mv88w8618_eth_state;
+
+static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le16s(&desc->buffer_size);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc));
+}
+
+static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
+{
+ cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc));
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->bytes);
+ le16_to_cpus(&desc->buffer_size);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static int eth_can_receive(NetClientState *nc)
+{
+ return 1;
+}
+
+static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
+ uint32_t desc_addr;
+ mv88w8618_rx_desc desc;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ desc_addr = s->cur_rx[i];
+ if (!desc_addr) {
+ continue;
+ }
+ do {
+ eth_rx_desc_get(desc_addr, &desc);
+ if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
+ cpu_physical_memory_write(desc.buffer + s->vlan_header,
+ buf, size);
+ desc.bytes = size + s->vlan_header;
+ desc.cmdstat &= ~MP_ETH_RX_OWN;
+ s->cur_rx[i] = desc.next;
+
+ s->icr |= MP_ETH_IRQ_RX;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ eth_rx_desc_put(desc_addr, &desc);
+ return size;
+ }
+ desc_addr = desc.next;
+ } while (desc_addr != s->rx_queue[i]);
+ }
+ return size;
+}
+
+static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->res);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc));
+}
+
+static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc)
+{
+ cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc));
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->res);
+ le16_to_cpus(&desc->bytes);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static void eth_send(mv88w8618_eth_state *s, int queue_index)
+{
+ uint32_t desc_addr = s->tx_queue[queue_index];
+ mv88w8618_tx_desc desc;
+ uint32_t next_desc;
+ uint8_t buf[2048];
+ int len;
+
+ do {
+ eth_tx_desc_get(desc_addr, &desc);
+ next_desc = desc.next;
+ if (desc.cmdstat & MP_ETH_TX_OWN) {
+ len = desc.bytes;
+ if (len < 2048) {
+ cpu_physical_memory_read(desc.buffer, buf, len);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, len);
+ }
+ desc.cmdstat &= ~MP_ETH_TX_OWN;
+ s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
+ eth_tx_desc_put(desc_addr, &desc);
+ }
+ desc_addr = next_desc;
+ } while (desc_addr != s->tx_queue[queue_index]);
+}
+
+static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ if (s->smir & MP_ETH_SMIR_OPCODE) {
+ switch (s->smir & MP_ETH_SMIR_ADDR) {
+ case MP_ETH_PHY1_BMSR:
+ return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
+ MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID1:
+ return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID2:
+ return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
+ default:
+ return MP_ETH_SMIR_RDVALID;
+ }
+ }
+ return 0;
+
+ case MP_ETH_ICR:
+ return s->icr;
+
+ case MP_ETH_IMR:
+ return s->imr;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ return s->frx_queue[(offset - MP_ETH_FRDP0)/4];
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
+ return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_eth_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ s->smir = value;
+ break;
+
+ case MP_ETH_PCXR:
+ s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
+ break;
+
+ case MP_ETH_SDCMR:
+ if (value & MP_ETH_CMD_TXHI) {
+ eth_send(s, 1);
+ }
+ if (value & MP_ETH_CMD_TXLO) {
+ eth_send(s, 0);
+ }
+ if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_ICR:
+ s->icr &= value;
+ break;
+
+ case MP_ETH_IMR:
+ s->imr = value;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value;
+ break;
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ s->rx_queue[(offset - MP_ETH_CRDP0)/4] =
+ s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
+ break;
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
+ s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps mv88w8618_eth_ops = {
+ .read = mv88w8618_eth_read,
+ .write = mv88w8618_eth_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void eth_cleanup(NetClientState *nc)
+{
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_mv88w8618_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_receive,
+ .receive = eth_receive,
+ .cleanup = eth_cleanup,
+};
+
+static int mv88w8618_eth_init(SysBusDevice *dev)
+{
+ mv88w8618_eth_state *s = FROM_SYSBUS(mv88w8618_eth_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+ memory_region_init_io(&s->iomem, &mv88w8618_eth_ops, s, "mv88w8618-eth",
+ MP_ETH_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_eth_vmsd = {
+ .name = "mv88w8618_eth",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(smir, mv88w8618_eth_state),
+ VMSTATE_UINT32(icr, mv88w8618_eth_state),
+ VMSTATE_UINT32(imr, mv88w8618_eth_state),
+ VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
+ VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
+ VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mv88w8618_eth_properties[] = {
+ DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mv88w8618_eth_init;
+ dc->vmsd = &mv88w8618_eth_vmsd;
+ dc->props = mv88w8618_eth_properties;
+}
+
+static const TypeInfo mv88w8618_eth_info = {
+ .name = "mv88w8618_eth",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mv88w8618_eth_state),
+ .class_init = mv88w8618_eth_class_init,
+};
+
+/* LCD register offsets */
+#define MP_LCD_IRQCTRL 0x180
+#define MP_LCD_IRQSTAT 0x184
+#define MP_LCD_SPICTRL 0x1ac
+#define MP_LCD_INST 0x1bc
+#define MP_LCD_DATA 0x1c0
+
+/* Mode magics */
+#define MP_LCD_SPI_DATA 0x00100011
+#define MP_LCD_SPI_CMD 0x00104011
+#define MP_LCD_SPI_INVALID 0x00000000
+
+/* Commmands */
+#define MP_LCD_INST_SETPAGE0 0xB0
+/* ... */
+#define MP_LCD_INST_SETPAGE7 0xB7
+
+#define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */
+
+typedef struct musicpal_lcd_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t brightness;
+ uint32_t mode;
+ uint32_t irqctrl;
+ uint32_t page;
+ uint32_t page_off;
+ DisplayState *ds;
+ uint8_t video_ram[128*64/8];
+} musicpal_lcd_state;
+
+static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
+{
+ switch (s->brightness) {
+ case 7:
+ return col;
+ case 0:
+ return 0;
+ default:
+ return (col * s->brightness) / 7;
+ }
+}
+
+#define SET_LCD_PIXEL(depth, type) \
+static inline void glue(set_lcd_pixel, depth) \
+ (musicpal_lcd_state *s, int x, int y, type col) \
+{ \
+ int dx, dy; \
+ type *pixel = &((type *) ds_get_data(s->ds))[(y * 128 * 3 + x) * 3]; \
+\
+ for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \
+ for (dx = 0; dx < 3; dx++, pixel++) \
+ *pixel = col; \
+}
+SET_LCD_PIXEL(8, uint8_t)
+SET_LCD_PIXEL(16, uint16_t)
+SET_LCD_PIXEL(32, uint32_t)
+
+static void lcd_refresh(void *opaque)
+{
+ musicpal_lcd_state *s = opaque;
+ int x, y, col;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+#define LCD_REFRESH(depth, func) \
+ case depth: \
+ col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \
+ scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \
+ scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \
+ for (x = 0; x < 128; x++) { \
+ for (y = 0; y < 64; y++) { \
+ if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \
+ glue(set_lcd_pixel, depth)(s, x, y, col); \
+ } else { \
+ glue(set_lcd_pixel, depth)(s, x, y, 0); \
+ } \
+ } \
+ } \
+ break;
+ LCD_REFRESH(8, rgb_to_pixel8)
+ LCD_REFRESH(16, rgb_to_pixel16)
+ LCD_REFRESH(32, (is_surface_bgr(s->ds->surface) ?
+ rgb_to_pixel32bgr : rgb_to_pixel32))
+ default:
+ hw_error("unsupported colour depth %i\n",
+ ds_get_bits_per_pixel(s->ds));
+ }
+
+ dpy_gfx_update(s->ds, 0, 0, 128*3, 64*3);
+}
+
+static void lcd_invalidate(void *opaque)
+{
+}
+
+static void musicpal_lcd_gpio_brigthness_in(void *opaque, int irq, int level)
+{
+ musicpal_lcd_state *s = opaque;
+ s->brightness &= ~(1 << irq);
+ s->brightness |= level << irq;
+}
+
+static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ musicpal_lcd_state *s = opaque;
+
+ switch (offset) {
+ case MP_LCD_IRQCTRL:
+ return s->irqctrl;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_lcd_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ musicpal_lcd_state *s = opaque;
+
+ switch (offset) {
+ case MP_LCD_IRQCTRL:
+ s->irqctrl = value;
+ break;
+
+ case MP_LCD_SPICTRL:
+ if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
+ s->mode = value;
+ } else {
+ s->mode = MP_LCD_SPI_INVALID;
+ }
+ break;
+
+ case MP_LCD_INST:
+ if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
+ s->page = value - MP_LCD_INST_SETPAGE0;
+ s->page_off = 0;
+ }
+ break;
+
+ case MP_LCD_DATA:
+ if (s->mode == MP_LCD_SPI_CMD) {
+ if (value >= MP_LCD_INST_SETPAGE0 &&
+ value <= MP_LCD_INST_SETPAGE7) {
+ s->page = value - MP_LCD_INST_SETPAGE0;
+ s->page_off = 0;
+ }
+ } else if (s->mode == MP_LCD_SPI_DATA) {
+ s->video_ram[s->page*128 + s->page_off] = value;
+ s->page_off = (s->page_off + 1) & 127;
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps musicpal_lcd_ops = {
+ .read = musicpal_lcd_read,
+ .write = musicpal_lcd_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int musicpal_lcd_init(SysBusDevice *dev)
+{
+ musicpal_lcd_state *s = FROM_SYSBUS(musicpal_lcd_state, dev);
+
+ s->brightness = 7;
+
+ memory_region_init_io(&s->iomem, &musicpal_lcd_ops, s,
+ "musicpal-lcd", MP_LCD_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->ds = graphic_console_init(lcd_refresh, lcd_invalidate,
+ NULL, NULL, s);
+ qemu_console_resize(s->ds, 128*3, 64*3);
+
+ qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_lcd_vmsd = {
+ .name = "musicpal_lcd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(brightness, musicpal_lcd_state),
+ VMSTATE_UINT32(mode, musicpal_lcd_state),
+ VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
+ VMSTATE_UINT32(page, musicpal_lcd_state),
+ VMSTATE_UINT32(page_off, musicpal_lcd_state),
+ VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = musicpal_lcd_init;
+ dc->vmsd = &musicpal_lcd_vmsd;
+}
+
+static const TypeInfo musicpal_lcd_info = {
+ .name = "musicpal_lcd",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(musicpal_lcd_state),
+ .class_init = musicpal_lcd_class_init,
+};
+
+/* PIC register offsets */
+#define MP_PIC_STATUS 0x00
+#define MP_PIC_ENABLE_SET 0x08
+#define MP_PIC_ENABLE_CLR 0x0C
+
+typedef struct mv88w8618_pic_state
+{
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t level;
+ uint32_t enabled;
+ qemu_irq parent_irq;
+} mv88w8618_pic_state;
+
+static void mv88w8618_pic_update(mv88w8618_pic_state *s)
+{
+ qemu_set_irq(s->parent_irq, (s->level & s->enabled));
+}
+
+static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ if (level) {
+ s->level |= 1 << irq;
+ } else {
+ s->level &= ~(1 << irq);
+ }
+ mv88w8618_pic_update(s);
+}
+
+static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ switch (offset) {
+ case MP_PIC_STATUS:
+ return s->level & s->enabled;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_pic_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ switch (offset) {
+ case MP_PIC_ENABLE_SET:
+ s->enabled |= value;
+ break;
+
+ case MP_PIC_ENABLE_CLR:
+ s->enabled &= ~value;
+ s->level &= ~value;
+ break;
+ }
+ mv88w8618_pic_update(s);
+}
+
+static void mv88w8618_pic_reset(DeviceState *d)
+{
+ mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state,
+ SYS_BUS_DEVICE(d));
+
+ s->level = 0;
+ s->enabled = 0;
+}
+
+static const MemoryRegionOps mv88w8618_pic_ops = {
+ .read = mv88w8618_pic_read,
+ .write = mv88w8618_pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mv88w8618_pic_init(SysBusDevice *dev)
+{
+ mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, dev);
+
+ qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32);
+ sysbus_init_irq(dev, &s->parent_irq);
+ memory_region_init_io(&s->iomem, &mv88w8618_pic_ops, s,
+ "musicpal-pic", MP_PIC_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_pic_vmsd = {
+ .name = "mv88w8618_pic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, mv88w8618_pic_state),
+ VMSTATE_UINT32(enabled, mv88w8618_pic_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mv88w8618_pic_init;
+ dc->reset = mv88w8618_pic_reset;
+ dc->vmsd = &mv88w8618_pic_vmsd;
+}
+
+static const TypeInfo mv88w8618_pic_info = {
+ .name = "mv88w8618_pic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mv88w8618_pic_state),
+ .class_init = mv88w8618_pic_class_init,
+};
+
+/* PIT register offsets */
+#define MP_PIT_TIMER1_LENGTH 0x00
+/* ... */
+#define MP_PIT_TIMER4_LENGTH 0x0C
+#define MP_PIT_CONTROL 0x10
+#define MP_PIT_TIMER1_VALUE 0x14
+/* ... */
+#define MP_PIT_TIMER4_VALUE 0x20
+#define MP_BOARD_RESET 0x34
+
+/* Magic board reset value (probably some watchdog behind it) */
+#define MP_BOARD_RESET_MAGIC 0x10000
+
+typedef struct mv88w8618_timer_state {
+ ptimer_state *ptimer;
+ uint32_t limit;
+ int freq;
+ qemu_irq irq;
+} mv88w8618_timer_state;
+
+typedef struct mv88w8618_pit_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ mv88w8618_timer_state timer[4];
+} mv88w8618_pit_state;
+
+static void mv88w8618_timer_tick(void *opaque)
+{
+ mv88w8618_timer_state *s = opaque;
+
+ qemu_irq_raise(s->irq);
+}
+
+static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
+ uint32_t freq)
+{
+ QEMUBH *bh;
+
+ sysbus_init_irq(dev, &s->irq);
+ s->freq = freq;
+
+ bh = qemu_bh_new(mv88w8618_timer_tick, s);
+ s->ptimer = ptimer_init(bh);
+}
+
+static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mv88w8618_pit_state *s = opaque;
+ mv88w8618_timer_state *t;
+
+ switch (offset) {
+ case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
+ t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
+ return ptimer_get_count(t->ptimer);
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_pit_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mv88w8618_pit_state *s = opaque;
+ mv88w8618_timer_state *t;
+ int i;
+
+ switch (offset) {
+ case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
+ t = &s->timer[offset >> 2];
+ t->limit = value;
+ if (t->limit > 0) {
+ ptimer_set_limit(t->ptimer, t->limit, 1);
+ } else {
+ ptimer_stop(t->ptimer);
+ }
+ break;
+
+ case MP_PIT_CONTROL:
+ for (i = 0; i < 4; i++) {
+ t = &s->timer[i];
+ if (value & 0xf && t->limit > 0) {
+ ptimer_set_limit(t->ptimer, t->limit, 0);
+ ptimer_set_freq(t->ptimer, t->freq);
+ ptimer_run(t->ptimer, 0);
+ } else {
+ ptimer_stop(t->ptimer);
+ }
+ value >>= 4;
+ }
+ break;
+
+ case MP_BOARD_RESET:
+ if (value == MP_BOARD_RESET_MAGIC) {
+ qemu_system_reset_request();
+ }
+ break;
+ }
+}
+
+static void mv88w8618_pit_reset(DeviceState *d)
+{
+ mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state,
+ SYS_BUS_DEVICE(d));
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ ptimer_stop(s->timer[i].ptimer);
+ s->timer[i].limit = 0;
+ }
+}
+
+static const MemoryRegionOps mv88w8618_pit_ops = {
+ .read = mv88w8618_pit_read,
+ .write = mv88w8618_pit_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mv88w8618_pit_init(SysBusDevice *dev)
+{
+ mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, dev);
+ int i;
+
+ /* Letting them all run at 1 MHz is likely just a pragmatic
+ * simplification. */
+ for (i = 0; i < 4; i++) {
+ mv88w8618_timer_init(dev, &s->timer[i], 1000000);
+ }
+
+ memory_region_init_io(&s->iomem, &mv88w8618_pit_ops, s,
+ "musicpal-pit", MP_PIT_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_timer_vmsd = {
+ .name = "timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
+ VMSTATE_UINT32(limit, mv88w8618_timer_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription mv88w8618_pit_vmsd = {
+ .name = "mv88w8618_pit",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
+ mv88w8618_timer_vmsd, mv88w8618_timer_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mv88w8618_pit_init;
+ dc->reset = mv88w8618_pit_reset;
+ dc->vmsd = &mv88w8618_pit_vmsd;
+}
+
+static const TypeInfo mv88w8618_pit_info = {
+ .name = "mv88w8618_pit",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mv88w8618_pit_state),
+ .class_init = mv88w8618_pit_class_init,
+};
+
+/* Flash config register offsets */
+#define MP_FLASHCFG_CFGR0 0x04
+
+typedef struct mv88w8618_flashcfg_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t cfgr0;
+} mv88w8618_flashcfg_state;
+
+static uint64_t mv88w8618_flashcfg_read(void *opaque,
+ hwaddr offset,
+ unsigned size)
+{
+ mv88w8618_flashcfg_state *s = opaque;
+
+ switch (offset) {
+ case MP_FLASHCFG_CFGR0:
+ return s->cfgr0;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mv88w8618_flashcfg_state *s = opaque;
+
+ switch (offset) {
+ case MP_FLASHCFG_CFGR0:
+ s->cfgr0 = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps mv88w8618_flashcfg_ops = {
+ .read = mv88w8618_flashcfg_read,
+ .write = mv88w8618_flashcfg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mv88w8618_flashcfg_init(SysBusDevice *dev)
+{
+ mv88w8618_flashcfg_state *s = FROM_SYSBUS(mv88w8618_flashcfg_state, dev);
+
+ s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
+ memory_region_init_io(&s->iomem, &mv88w8618_flashcfg_ops, s,
+ "musicpal-flashcfg", MP_FLASHCFG_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_flashcfg_vmsd = {
+ .name = "mv88w8618_flashcfg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mv88w8618_flashcfg_init;
+ dc->vmsd = &mv88w8618_flashcfg_vmsd;
+}
+
+static const TypeInfo mv88w8618_flashcfg_info = {
+ .name = "mv88w8618_flashcfg",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mv88w8618_flashcfg_state),
+ .class_init = mv88w8618_flashcfg_class_init,
+};
+
+/* Misc register offsets */
+#define MP_MISC_BOARD_REVISION 0x18
+
+#define MP_BOARD_REVISION 0x31
+
+static uint64_t musicpal_misc_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ switch (offset) {
+ case MP_MISC_BOARD_REVISION:
+ return MP_BOARD_REVISION;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_misc_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+}
+
+static const MemoryRegionOps musicpal_misc_ops = {
+ .read = musicpal_misc_read,
+ .write = musicpal_misc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void musicpal_misc_init(SysBusDevice *dev)
+{
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+
+ memory_region_init_io(iomem, &musicpal_misc_ops, NULL,
+ "musicpal-misc", MP_MISC_SIZE);
+ sysbus_add_memory(dev, MP_MISC_BASE, iomem);
+}
+
+/* WLAN register offsets */
+#define MP_WLAN_MAGIC1 0x11c
+#define MP_WLAN_MAGIC2 0x124
+
+static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ switch (offset) {
+ /* Workaround to allow loading the binary-only wlandrv.ko crap
+ * from the original Freecom firmware. */
+ case MP_WLAN_MAGIC1:
+ return ~3;
+ case MP_WLAN_MAGIC2:
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_wlan_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+}
+
+static const MemoryRegionOps mv88w8618_wlan_ops = {
+ .read = mv88w8618_wlan_read,
+ .write =mv88w8618_wlan_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mv88w8618_wlan_init(SysBusDevice *dev)
+{
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+
+ memory_region_init_io(iomem, &mv88w8618_wlan_ops, NULL,
+ "musicpal-wlan", MP_WLAN_SIZE);
+ sysbus_init_mmio(dev, iomem);
+ return 0;
+}
+
+/* GPIO register offsets */
+#define MP_GPIO_OE_LO 0x008
+#define MP_GPIO_OUT_LO 0x00c
+#define MP_GPIO_IN_LO 0x010
+#define MP_GPIO_IER_LO 0x014
+#define MP_GPIO_IMR_LO 0x018
+#define MP_GPIO_ISR_LO 0x020
+#define MP_GPIO_OE_HI 0x508
+#define MP_GPIO_OUT_HI 0x50c
+#define MP_GPIO_IN_HI 0x510
+#define MP_GPIO_IER_HI 0x514
+#define MP_GPIO_IMR_HI 0x518
+#define MP_GPIO_ISR_HI 0x520
+
+/* GPIO bits & masks */
+#define MP_GPIO_LCD_BRIGHTNESS 0x00070000
+#define MP_GPIO_I2C_DATA_BIT 29
+#define MP_GPIO_I2C_CLOCK_BIT 30
+
+/* LCD brightness bits in GPIO_OE_HI */
+#define MP_OE_LCD_BRIGHTNESS 0x0007
+
+typedef struct musicpal_gpio_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t lcd_brightness;
+ uint32_t out_state;
+ uint32_t in_state;
+ uint32_t ier;
+ uint32_t imr;
+ uint32_t isr;
+ qemu_irq irq;
+ qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
+} musicpal_gpio_state;
+
+static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
+ int i;
+ uint32_t brightness;
+
+ /* compute brightness ratio */
+ switch (s->lcd_brightness) {
+ case 0x00000007:
+ brightness = 0;
+ break;
+
+ case 0x00020000:
+ brightness = 1;
+ break;
+
+ case 0x00020001:
+ brightness = 2;
+ break;
+
+ case 0x00040000:
+ brightness = 3;
+ break;
+
+ case 0x00010006:
+ brightness = 4;
+ break;
+
+ case 0x00020005:
+ brightness = 5;
+ break;
+
+ case 0x00040003:
+ brightness = 6;
+ break;
+
+ case 0x00030004:
+ default:
+ brightness = 7;
+ }
+
+ /* set lcd brightness GPIOs */
+ for (i = 0; i <= 2; i++) {
+ qemu_set_irq(s->out[i], (brightness >> i) & 1);
+ }
+}
+
+static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
+{
+ musicpal_gpio_state *s = opaque;
+ uint32_t mask = 1 << pin;
+ uint32_t delta = level << pin;
+ uint32_t old = s->in_state & mask;
+
+ s->in_state &= ~mask;
+ s->in_state |= delta;
+
+ if ((old ^ delta) &&
+ ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
+ s->isr = mask;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ musicpal_gpio_state *s = opaque;
+
+ switch (offset) {
+ case MP_GPIO_OE_HI: /* used for LCD brightness control */
+ return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
+
+ case MP_GPIO_OUT_LO:
+ return s->out_state & 0xFFFF;
+ case MP_GPIO_OUT_HI:
+ return s->out_state >> 16;
+
+ case MP_GPIO_IN_LO:
+ return s->in_state & 0xFFFF;
+ case MP_GPIO_IN_HI:
+ return s->in_state >> 16;
+
+ case MP_GPIO_IER_LO:
+ return s->ier & 0xFFFF;
+ case MP_GPIO_IER_HI:
+ return s->ier >> 16;
+
+ case MP_GPIO_IMR_LO:
+ return s->imr & 0xFFFF;
+ case MP_GPIO_IMR_HI:
+ return s->imr >> 16;
+
+ case MP_GPIO_ISR_LO:
+ return s->isr & 0xFFFF;
+ case MP_GPIO_ISR_HI:
+ return s->isr >> 16;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_gpio_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ musicpal_gpio_state *s = opaque;
+ switch (offset) {
+ case MP_GPIO_OE_HI: /* used for LCD brightness control */
+ s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
+ (value & MP_OE_LCD_BRIGHTNESS);
+ musicpal_gpio_brightness_update(s);
+ break;
+
+ case MP_GPIO_OUT_LO:
+ s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_OUT_HI:
+ s->out_state = (s->out_state & 0xFFFF) | (value << 16);
+ s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
+ (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
+ musicpal_gpio_brightness_update(s);
+ qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
+ qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
+ break;
+
+ case MP_GPIO_IER_LO:
+ s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_IER_HI:
+ s->ier = (s->ier & 0xFFFF) | (value << 16);
+ break;
+
+ case MP_GPIO_IMR_LO:
+ s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_IMR_HI:
+ s->imr = (s->imr & 0xFFFF) | (value << 16);
+ break;
+ }
+}
+
+static const MemoryRegionOps musicpal_gpio_ops = {
+ .read = musicpal_gpio_read,
+ .write = musicpal_gpio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void musicpal_gpio_reset(DeviceState *d)
+{
+ musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state,
+ SYS_BUS_DEVICE(d));
+
+ s->lcd_brightness = 0;
+ s->out_state = 0;
+ s->in_state = 0xffffffff;
+ s->ier = 0;
+ s->imr = 0;
+ s->isr = 0;
+}
+
+static int musicpal_gpio_init(SysBusDevice *dev)
+{
+ musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ memory_region_init_io(&s->iomem, &musicpal_gpio_ops, s,
+ "musicpal-gpio", MP_GPIO_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out));
+
+ qdev_init_gpio_in(&dev->qdev, musicpal_gpio_pin_event, 32);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_gpio_vmsd = {
+ .name = "musicpal_gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
+ VMSTATE_UINT32(out_state, musicpal_gpio_state),
+ VMSTATE_UINT32(in_state, musicpal_gpio_state),
+ VMSTATE_UINT32(ier, musicpal_gpio_state),
+ VMSTATE_UINT32(imr, musicpal_gpio_state),
+ VMSTATE_UINT32(isr, musicpal_gpio_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = musicpal_gpio_init;
+ dc->reset = musicpal_gpio_reset;
+ dc->vmsd = &musicpal_gpio_vmsd;
+}
+
+static const TypeInfo musicpal_gpio_info = {
+ .name = "musicpal_gpio",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(musicpal_gpio_state),
+ .class_init = musicpal_gpio_class_init,
+};
+
+/* Keyboard codes & masks */
+#define KEY_RELEASED 0x80
+#define KEY_CODE 0x7f
+
+#define KEYCODE_TAB 0x0f
+#define KEYCODE_ENTER 0x1c
+#define KEYCODE_F 0x21
+#define KEYCODE_M 0x32
+
+#define KEYCODE_EXTENDED 0xe0
+#define KEYCODE_UP 0x48
+#define KEYCODE_DOWN 0x50
+#define KEYCODE_LEFT 0x4b
+#define KEYCODE_RIGHT 0x4d
+
+#define MP_KEY_WHEEL_VOL (1 << 0)
+#define MP_KEY_WHEEL_VOL_INV (1 << 1)
+#define MP_KEY_WHEEL_NAV (1 << 2)
+#define MP_KEY_WHEEL_NAV_INV (1 << 3)
+#define MP_KEY_BTN_FAVORITS (1 << 4)
+#define MP_KEY_BTN_MENU (1 << 5)
+#define MP_KEY_BTN_VOLUME (1 << 6)
+#define MP_KEY_BTN_NAVIGATION (1 << 7)
+
+typedef struct musicpal_key_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t kbd_extended;
+ uint32_t pressed_keys;
+ qemu_irq out[8];
+} musicpal_key_state;
+
+static void musicpal_key_event(void *opaque, int keycode)
+{
+ musicpal_key_state *s = opaque;
+ uint32_t event = 0;
+ int i;
+
+ if (keycode == KEYCODE_EXTENDED) {
+ s->kbd_extended = 1;
+ return;
+ }
+
+ if (s->kbd_extended) {
+ switch (keycode & KEY_CODE) {
+ case KEYCODE_UP:
+ event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
+ break;
+
+ case KEYCODE_DOWN:
+ event = MP_KEY_WHEEL_NAV;
+ break;
+
+ case KEYCODE_LEFT:
+ event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
+ break;
+
+ case KEYCODE_RIGHT:
+ event = MP_KEY_WHEEL_VOL;
+ break;
+ }
+ } else {
+ switch (keycode & KEY_CODE) {
+ case KEYCODE_F:
+ event = MP_KEY_BTN_FAVORITS;
+ break;
+
+ case KEYCODE_TAB:
+ event = MP_KEY_BTN_VOLUME;
+ break;
+
+ case KEYCODE_ENTER:
+ event = MP_KEY_BTN_NAVIGATION;
+ break;
+
+ case KEYCODE_M:
+ event = MP_KEY_BTN_MENU;
+ break;
+ }
+ /* Do not repeat already pressed buttons */
+ if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
+ event = 0;
+ }
+ }
+
+ if (event) {
+ /* Raise GPIO pin first if repeating a key */
+ if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
+ for (i = 0; i <= 7; i++) {
+ if (event & (1 << i)) {
+ qemu_set_irq(s->out[i], 1);
+ }
+ }
+ }
+ for (i = 0; i <= 7; i++) {
+ if (event & (1 << i)) {
+ qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED));
+ }
+ }
+ if (keycode & KEY_RELEASED) {
+ s->pressed_keys &= ~event;
+ } else {
+ s->pressed_keys |= event;
+ }
+ }
+
+ s->kbd_extended = 0;
+}
+
+static int musicpal_key_init(SysBusDevice *dev)
+{
+ musicpal_key_state *s = FROM_SYSBUS(musicpal_key_state, dev);
+
+ memory_region_init(&s->iomem, "dummy", 0);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->kbd_extended = 0;
+ s->pressed_keys = 0;
+
+ qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out));
+
+ qemu_add_kbd_event_handler(musicpal_key_event, s);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_key_vmsd = {
+ .name = "musicpal_key",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(kbd_extended, musicpal_key_state),
+ VMSTATE_UINT32(pressed_keys, musicpal_key_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void musicpal_key_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = musicpal_key_init;
+ dc->vmsd = &musicpal_key_vmsd;
+}
+
+static const TypeInfo musicpal_key_info = {
+ .name = "musicpal_key",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(musicpal_key_state),
+ .class_init = musicpal_key_class_init,
+};
+
+static struct arm_boot_info musicpal_binfo = {
+ .loader_start = 0x0,
+ .board_id = 0x20e,
+};
+
+static void musicpal_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ ARMCPU *cpu;
+ qemu_irq *cpu_pic;
+ qemu_irq pic[32];
+ DeviceState *dev;
+ DeviceState *i2c_dev;
+ DeviceState *lcd_dev;
+ DeviceState *key_dev;
+ DeviceState *wm8750_dev;
+ SysBusDevice *s;
+ i2c_bus *i2c;
+ int i;
+ unsigned long flash_size;
+ DriveInfo *dinfo;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+
+ if (!cpu_model) {
+ cpu_model = "arm926";
+ }
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ cpu_pic = arm_pic_init_cpu(cpu);
+
+ /* For now we use a fixed - the original - RAM size */
+ memory_region_init_ram(ram, "musicpal.ram", MP_RAM_DEFAULT_SIZE);
+ vmstate_register_ram_global(ram);
+ memory_region_add_subregion(address_space_mem, 0, ram);
+
+ memory_region_init_ram(sram, "musicpal.sram", MP_SRAM_SIZE);
+ vmstate_register_ram_global(sram);
+ memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
+
+ dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE,
+ cpu_pic[ARM_PIC_CPU_IRQ]);
+ for (i = 0; i < 32; i++) {
+ pic[i] = qdev_get_gpio_in(dev, i);
+ }
+ sysbus_create_varargs("mv88w8618_pit", MP_PIT_BASE, pic[MP_TIMER1_IRQ],
+ pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ],
+ pic[MP_TIMER4_IRQ], NULL);
+
+ if (serial_hds[0]) {
+ serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ],
+ 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN);
+ }
+ if (serial_hds[1]) {
+ serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ],
+ 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN);
+ }
+
+ /* Register flash */
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (dinfo) {
+ flash_size = bdrv_getlength(dinfo->bdrv);
+ if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 &&
+ flash_size != 32*1024*1024) {
+ fprintf(stderr, "Invalid flash image size\n");
+ exit(1);
+ }
+
+ /*
+ * The original U-Boot accesses the flash at 0xFE000000 instead of
+ * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
+ * image is smaller than 32 MB.
+ */
+#ifdef TARGET_WORDS_BIGENDIAN
+ pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
+ "musicpal.flash", flash_size,
+ dinfo->bdrv, 0x10000,
+ (flash_size + 0xffff) >> 16,
+ MP_FLASH_SIZE_MAX / flash_size,
+ 2, 0x00BF, 0x236D, 0x0000, 0x0000,
+ 0x5555, 0x2AAA, 1);
+#else
+ pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
+ "musicpal.flash", flash_size,
+ dinfo->bdrv, 0x10000,
+ (flash_size + 0xffff) >> 16,
+ MP_FLASH_SIZE_MAX / flash_size,
+ 2, 0x00BF, 0x236D, 0x0000, 0x0000,
+ 0x5555, 0x2AAA, 0);
+#endif
+
+ }
+ sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL);
+
+ qemu_check_nic_model(&nd_table[0], "mv88w8618");
+ dev = qdev_create(NULL, "mv88w8618_eth");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]);
+
+ sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
+
+ musicpal_misc_init(SYS_BUS_DEVICE(dev));
+
+ dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]);
+ i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL);
+ i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c");
+
+ lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL);
+ key_dev = sysbus_create_simple("musicpal_key", -1, NULL);
+
+ /* I2C read data */
+ qdev_connect_gpio_out(i2c_dev, 0,
+ qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
+ /* I2C data */
+ qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
+ /* I2C clock */
+ qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));
+
+ for (i = 0; i < 3; i++) {
+ qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
+ }
+ for (i = 0; i < 4; i++) {
+ qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
+ }
+ for (i = 4; i < 8; i++) {
+ qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
+ }
+
+ wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR);
+ dev = qdev_create(NULL, "mv88w8618_audio");
+ s = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_ptr(dev, "wm8750", wm8750_dev);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
+ sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]);
+
+ musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
+ musicpal_binfo.kernel_filename = kernel_filename;
+ musicpal_binfo.kernel_cmdline = kernel_cmdline;
+ musicpal_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(cpu, &musicpal_binfo);
+}
+
+static QEMUMachine musicpal_machine = {
+ .name = "musicpal",
+ .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)",
+ .init = musicpal_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void musicpal_machine_init(void)
+{
+ qemu_register_machine(&musicpal_machine);
+}
+
+machine_init(musicpal_machine_init);
+
+static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mv88w8618_wlan_init;
+}
+
+static const TypeInfo mv88w8618_wlan_info = {
+ .name = "mv88w8618_wlan",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SysBusDevice),
+ .class_init = mv88w8618_wlan_class_init,
+};
+
+static void musicpal_register_types(void)
+{
+ type_register_static(&mv88w8618_pic_info);
+ type_register_static(&mv88w8618_pit_info);
+ type_register_static(&mv88w8618_flashcfg_info);
+ type_register_static(&mv88w8618_eth_info);
+ type_register_static(&mv88w8618_wlan_info);
+ type_register_static(&musicpal_lcd_info);
+ type_register_static(&musicpal_gpio_info);
+ type_register_static(&musicpal_key_info);
+}
+
+type_init(musicpal_register_types)
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
new file mode 100644
index 0000000..c5bf9f9
--- /dev/null
+++ b/hw/arm/nseries.c
@@ -0,0 +1,1430 @@
+/*
+ * Nokia N-series internet tablets.
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.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 or
+ * (at your option) version 3 of the License.
+ *
+ * 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 "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "hw/omap.h"
+#include "hw/arm-misc.h"
+#include "hw/irq.h"
+#include "ui/console.h"
+#include "hw/boards.h"
+#include "hw/i2c.h"
+#include "hw/devices.h"
+#include "hw/flash.h"
+#include "hw/hw.h"
+#include "hw/bt.h"
+#include "hw/loader.h"
+#include "sysemu/blockdev.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+/* Nokia N8x0 support */
+struct n800_s {
+ struct omap_mpu_state_s *mpu;
+
+ struct rfbi_chip_s blizzard;
+ struct {
+ void *opaque;
+ uint32_t (*txrx)(void *opaque, uint32_t value, int len);
+ uWireSlave *chip;
+ } ts;
+
+ int keymap[0x80];
+ DeviceState *kbd;
+
+ DeviceState *usb;
+ void *retu;
+ void *tahvo;
+ DeviceState *nand;
+};
+
+/* GPIO pins */
+#define N8X0_TUSB_ENABLE_GPIO 0
+#define N800_MMC2_WP_GPIO 8
+#define N800_UNKNOWN_GPIO0 9 /* out */
+#define N810_MMC2_VIOSD_GPIO 9
+#define N810_HEADSET_AMP_GPIO 10
+#define N800_CAM_TURN_GPIO 12
+#define N810_GPS_RESET_GPIO 12
+#define N800_BLIZZARD_POWERDOWN_GPIO 15
+#define N800_MMC1_WP_GPIO 23
+#define N810_MMC2_VSD_GPIO 23
+#define N8X0_ONENAND_GPIO 26
+#define N810_BLIZZARD_RESET_GPIO 30
+#define N800_UNKNOWN_GPIO2 53 /* out */
+#define N8X0_TUSB_INT_GPIO 58
+#define N8X0_BT_WKUP_GPIO 61
+#define N8X0_STI_GPIO 62
+#define N8X0_CBUS_SEL_GPIO 64
+#define N8X0_CBUS_DAT_GPIO 65
+#define N8X0_CBUS_CLK_GPIO 66
+#define N8X0_WLAN_IRQ_GPIO 87
+#define N8X0_BT_RESET_GPIO 92
+#define N8X0_TEA5761_CS_GPIO 93
+#define N800_UNKNOWN_GPIO 94
+#define N810_TSC_RESET_GPIO 94
+#define N800_CAM_ACT_GPIO 95
+#define N810_GPS_WAKEUP_GPIO 95
+#define N8X0_MMC_CS_GPIO 96
+#define N8X0_WLAN_PWR_GPIO 97
+#define N8X0_BT_HOST_WKUP_GPIO 98
+#define N810_SPEAKER_AMP_GPIO 101
+#define N810_KB_LOCK_GPIO 102
+#define N800_TSC_TS_GPIO 103
+#define N810_TSC_TS_GPIO 106
+#define N8X0_HEADPHONE_GPIO 107
+#define N8X0_RETU_GPIO 108
+#define N800_TSC_KP_IRQ_GPIO 109
+#define N810_KEYBOARD_GPIO 109
+#define N800_BAT_COVER_GPIO 110
+#define N810_SLIDE_GPIO 110
+#define N8X0_TAHVO_GPIO 111
+#define N800_UNKNOWN_GPIO4 112 /* out */
+#define N810_SLEEPX_LED_GPIO 112
+#define N800_TSC_RESET_GPIO 118 /* ? */
+#define N810_AIC33_RESET_GPIO 118
+#define N800_TSC_UNKNOWN_GPIO 119 /* out */
+#define N8X0_TMP105_GPIO 125
+
+/* Config */
+#define BT_UART 0
+#define XLDR_LL_UART 1
+
+/* Addresses on the I2C bus 0 */
+#define N810_TLV320AIC33_ADDR 0x18 /* Audio CODEC */
+#define N8X0_TCM825x_ADDR 0x29 /* Camera */
+#define N810_LP5521_ADDR 0x32 /* LEDs */
+#define N810_TSL2563_ADDR 0x3d /* Light sensor */
+#define N810_LM8323_ADDR 0x45 /* Keyboard */
+/* Addresses on the I2C bus 1 */
+#define N8X0_TMP105_ADDR 0x48 /* Temperature sensor */
+#define N8X0_MENELAUS_ADDR 0x72 /* Power management */
+
+/* Chipselects on GPMC NOR interface */
+#define N8X0_ONENAND_CS 0
+#define N8X0_USB_ASYNC_CS 1
+#define N8X0_USB_SYNC_CS 4
+
+#define N8X0_BD_ADDR 0x00, 0x1a, 0x89, 0x9e, 0x3e, 0x81
+
+static void n800_mmc_cs_cb(void *opaque, int line, int level)
+{
+ /* TODO: this seems to actually be connected to the menelaus, to
+ * which also both MMC slots connect. */
+ omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
+
+ printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
+}
+
+static void n8x0_gpio_setup(struct n800_s *s)
+{
+ qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->mpu->mmc, 1);
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]);
+
+ qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO));
+}
+
+#define MAEMO_CAL_HEADER(...) \
+ 'C', 'o', 'n', 'F', 0x02, 0x00, 0x04, 0x00, \
+ __VA_ARGS__, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+static const uint8_t n8x0_cal_wlan_mac[] = {
+ MAEMO_CAL_HEADER('w', 'l', 'a', 'n', '-', 'm', 'a', 'c')
+ 0x1c, 0x00, 0x00, 0x00, 0x47, 0xd6, 0x69, 0xb3,
+ 0x30, 0x08, 0xa0, 0x83, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x89, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00,
+ 0x5d, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t n8x0_cal_bt_id[] = {
+ MAEMO_CAL_HEADER('b', 't', '-', 'i', 'd', 0, 0, 0)
+ 0x0a, 0x00, 0x00, 0x00, 0xa3, 0x4b, 0xf6, 0x96,
+ 0xa8, 0xeb, 0xb2, 0x41, 0x00, 0x00, 0x00, 0x00,
+ N8X0_BD_ADDR,
+};
+
+static void n8x0_nand_setup(struct n800_s *s)
+{
+ char *otp_region;
+ DriveInfo *dinfo;
+
+ s->nand = qdev_create(NULL, "onenand");
+ qdev_prop_set_uint16(s->nand, "manufacturer_id", NAND_MFR_SAMSUNG);
+ /* Either 0x40 or 0x48 are OK for the device ID */
+ qdev_prop_set_uint16(s->nand, "device_id", 0x48);
+ qdev_prop_set_uint16(s->nand, "version_id", 0);
+ qdev_prop_set_int32(s->nand, "shift", 1);
+ dinfo = drive_get(IF_MTD, 0, 0);
+ if (dinfo && dinfo->bdrv) {
+ qdev_prop_set_drive_nofail(s->nand, "drive", dinfo->bdrv);
+ }
+ qdev_init_nofail(s->nand);
+ sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0,
+ qdev_get_gpio_in(s->mpu->gpio, N8X0_ONENAND_GPIO));
+ omap_gpmc_attach(s->mpu->gpmc, N8X0_ONENAND_CS,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(s->nand), 0));
+ otp_region = onenand_raw_otp(s->nand);
+
+ memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac));
+ memcpy(otp_region + 0x800, n8x0_cal_bt_id, sizeof(n8x0_cal_bt_id));
+ /* XXX: in theory should also update the OOB for both pages */
+}
+
+static qemu_irq n8x0_system_powerdown;
+
+static void n8x0_powerdown_req(Notifier *n, void *opaque)
+{
+ qemu_irq_raise(n8x0_system_powerdown);
+}
+
+static Notifier n8x0_system_powerdown_notifier = {
+ .notify = n8x0_powerdown_req
+};
+
+static void n8x0_i2c_setup(struct n800_s *s)
+{
+ DeviceState *dev;
+ qemu_irq tmp_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TMP105_GPIO);
+ i2c_bus *i2c = omap_i2c_bus(s->mpu->i2c[0]);
+
+ /* Attach a menelaus PM chip */
+ dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR);
+ qdev_connect_gpio_out(dev, 3,
+ qdev_get_gpio_in(s->mpu->ih[0],
+ OMAP_INT_24XX_SYS_NIRQ));
+
+ n8x0_system_powerdown = qdev_get_gpio_in(dev, 3);
+ qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier);
+
+ /* Attach a TMP105 PM chip (A0 wired to ground) */
+ dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR);
+ qdev_connect_gpio_out(dev, 0, tmp_irq);
+}
+
+/* Touchscreen and keypad controller */
+static MouseTransformInfo n800_pointercal = {
+ .x = 800,
+ .y = 480,
+ .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
+};
+
+static MouseTransformInfo n810_pointercal = {
+ .x = 800,
+ .y = 480,
+ .a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 },
+};
+
+#define RETU_KEYCODE 61 /* F3 */
+
+static void n800_key_event(void *opaque, int keycode)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ int code = s->keymap[keycode & 0x7f];
+
+ if (code == -1) {
+ if ((keycode & 0x7f) == RETU_KEYCODE)
+ retu_key_event(s->retu, !(keycode & 0x80));
+ return;
+ }
+
+ tsc210x_key_event(s->ts.chip, code, !(keycode & 0x80));
+}
+
+static const int n800_keys[16] = {
+ -1,
+ 72, /* Up */
+ 63, /* Home (F5) */
+ -1,
+ 75, /* Left */
+ 28, /* Enter */
+ 77, /* Right */
+ -1,
+ 1, /* Cycle (ESC) */
+ 80, /* Down */
+ 62, /* Menu (F4) */
+ -1,
+ 66, /* Zoom- (F8) */
+ 64, /* FullScreen (F6) */
+ 65, /* Zoom+ (F7) */
+ -1,
+};
+
+static void n800_tsc_kbd_setup(struct n800_s *s)
+{
+ int i;
+
+ /* XXX: are the three pins inverted inside the chip between the
+ * tsc and the cpu (N4111)? */
+ qemu_irq penirq = NULL; /* NC */
+ qemu_irq kbirq = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_KP_IRQ_GPIO);
+ qemu_irq dav = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_TS_GPIO);
+
+ s->ts.chip = tsc2301_init(penirq, kbirq, dav);
+ s->ts.opaque = s->ts.chip->opaque;
+ s->ts.txrx = tsc210x_txrx;
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < 0x10; i ++)
+ if (n800_keys[i] >= 0)
+ s->keymap[n800_keys[i]] = i;
+
+ qemu_add_kbd_event_handler(n800_key_event, s);
+
+ tsc210x_set_transform(s->ts.chip, &n800_pointercal);
+}
+
+static void n810_tsc_setup(struct n800_s *s)
+{
+ qemu_irq pintdav = qdev_get_gpio_in(s->mpu->gpio, N810_TSC_TS_GPIO);
+
+ s->ts.opaque = tsc2005_init(pintdav);
+ s->ts.txrx = tsc2005_txrx;
+
+ tsc2005_set_transform(s->ts.opaque, &n810_pointercal);
+}
+
+/* N810 Keyboard controller */
+static void n810_key_event(void *opaque, int keycode)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ int code = s->keymap[keycode & 0x7f];
+
+ if (code == -1) {
+ if ((keycode & 0x7f) == RETU_KEYCODE)
+ retu_key_event(s->retu, !(keycode & 0x80));
+ return;
+ }
+
+ lm832x_key_event(s->kbd, code, !(keycode & 0x80));
+}
+
+#define M 0
+
+static int n810_keys[0x80] = {
+ [0x01] = 16, /* Q */
+ [0x02] = 37, /* K */
+ [0x03] = 24, /* O */
+ [0x04] = 25, /* P */
+ [0x05] = 14, /* Backspace */
+ [0x06] = 30, /* A */
+ [0x07] = 31, /* S */
+ [0x08] = 32, /* D */
+ [0x09] = 33, /* F */
+ [0x0a] = 34, /* G */
+ [0x0b] = 35, /* H */
+ [0x0c] = 36, /* J */
+
+ [0x11] = 17, /* W */
+ [0x12] = 62, /* Menu (F4) */
+ [0x13] = 38, /* L */
+ [0x14] = 40, /* ' (Apostrophe) */
+ [0x16] = 44, /* Z */
+ [0x17] = 45, /* X */
+ [0x18] = 46, /* C */
+ [0x19] = 47, /* V */
+ [0x1a] = 48, /* B */
+ [0x1b] = 49, /* N */
+ [0x1c] = 42, /* Shift (Left shift) */
+ [0x1f] = 65, /* Zoom+ (F7) */
+
+ [0x21] = 18, /* E */
+ [0x22] = 39, /* ; (Semicolon) */
+ [0x23] = 12, /* - (Minus) */
+ [0x24] = 13, /* = (Equal) */
+ [0x2b] = 56, /* Fn (Left Alt) */
+ [0x2c] = 50, /* M */
+ [0x2f] = 66, /* Zoom- (F8) */
+
+ [0x31] = 19, /* R */
+ [0x32] = 29 | M, /* Right Ctrl */
+ [0x34] = 57, /* Space */
+ [0x35] = 51, /* , (Comma) */
+ [0x37] = 72 | M, /* Up */
+ [0x3c] = 82 | M, /* Compose (Insert) */
+ [0x3f] = 64, /* FullScreen (F6) */
+
+ [0x41] = 20, /* T */
+ [0x44] = 52, /* . (Dot) */
+ [0x46] = 77 | M, /* Right */
+ [0x4f] = 63, /* Home (F5) */
+ [0x51] = 21, /* Y */
+ [0x53] = 80 | M, /* Down */
+ [0x55] = 28, /* Enter */
+ [0x5f] = 1, /* Cycle (ESC) */
+
+ [0x61] = 22, /* U */
+ [0x64] = 75 | M, /* Left */
+
+ [0x71] = 23, /* I */
+#if 0
+ [0x75] = 28 | M, /* KP Enter (KP Enter) */
+#else
+ [0x75] = 15, /* KP Enter (Tab) */
+#endif
+};
+
+#undef M
+
+static void n810_kbd_setup(struct n800_s *s)
+{
+ qemu_irq kbd_irq = qdev_get_gpio_in(s->mpu->gpio, N810_KEYBOARD_GPIO);
+ int i;
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < 0x80; i ++)
+ if (n810_keys[i] > 0)
+ s->keymap[n810_keys[i]] = i;
+
+ qemu_add_kbd_event_handler(n810_key_event, s);
+
+ /* Attach the LM8322 keyboard to the I2C bus,
+ * should happen in n8x0_i2c_setup and s->kbd be initialised here. */
+ s->kbd = i2c_create_slave(omap_i2c_bus(s->mpu->i2c[0]),
+ "lm8323", N810_LM8323_ADDR);
+ qdev_connect_gpio_out(s->kbd, 0, kbd_irq);
+}
+
+/* LCD MIPI DBI-C controller (URAL) */
+struct mipid_s {
+ int resp[4];
+ int param[4];
+ int p;
+ int pm;
+ int cmd;
+
+ int sleep;
+ int booster;
+ int te;
+ int selfcheck;
+ int partial;
+ int normal;
+ int vscr;
+ int invert;
+ int onoff;
+ int gamma;
+ uint32_t id;
+};
+
+static void mipid_reset(struct mipid_s *s)
+{
+ if (!s->sleep)
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+
+ s->pm = 0;
+ s->cmd = 0;
+
+ s->sleep = 1;
+ s->booster = 0;
+ s->selfcheck =
+ (1 << 7) | /* Register loading OK. */
+ (1 << 5) | /* The chip is attached. */
+ (1 << 4); /* Display glass still in one piece. */
+ s->te = 0;
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ s->invert = 0;
+ s->onoff = 1;
+ s->gamma = 0;
+}
+
+static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len)
+{
+ struct mipid_s *s = (struct mipid_s *) opaque;
+ uint8_t ret;
+
+ if (len > 9)
+ hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
+
+ if (s->p >= ARRAY_SIZE(s->resp))
+ ret = 0;
+ else
+ ret = s->resp[s->p ++];
+ if (s->pm --> 0)
+ s->param[s->pm] = cmd;
+ else
+ s->cmd = cmd;
+
+ switch (s->cmd) {
+ case 0x00: /* NOP */
+ break;
+
+ case 0x01: /* SWRESET */
+ mipid_reset(s);
+ break;
+
+ case 0x02: /* BSTROFF */
+ s->booster = 0;
+ break;
+ case 0x03: /* BSTRON */
+ s->booster = 1;
+ break;
+
+ case 0x04: /* RDDID */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ s->resp[1] = (s->id >> 8) & 0xff;
+ s->resp[2] = (s->id >> 0) & 0xff;
+ break;
+
+ case 0x06: /* RD_RED */
+ case 0x07: /* RD_GREEN */
+ /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so
+ * for the bootloader one needs to change this. */
+ case 0x08: /* RD_BLUE */
+ s->p = 0;
+ /* TODO: return first pixel components */
+ s->resp[0] = 0x01;
+ break;
+
+ case 0x09: /* RDDST */
+ s->p = 0;
+ s->resp[0] = s->booster << 7;
+ s->resp[1] = (5 << 4) | (s->partial << 2) |
+ (s->sleep << 1) | s->normal;
+ s->resp[2] = (s->vscr << 7) | (s->invert << 5) |
+ (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2);
+ s->resp[3] = s->gamma << 6;
+ break;
+
+ case 0x0a: /* RDDPM */
+ s->p = 0;
+ s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) |
+ (s->partial << 5) | (s->sleep << 6) | (s->booster << 7);
+ break;
+ case 0x0b: /* RDDMADCTR */
+ s->p = 0;
+ s->resp[0] = 0;
+ break;
+ case 0x0c: /* RDDCOLMOD */
+ s->p = 0;
+ s->resp[0] = 5; /* 65K colours */
+ break;
+ case 0x0d: /* RDDIM */
+ s->p = 0;
+ s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma;
+ break;
+ case 0x0e: /* RDDSM */
+ s->p = 0;
+ s->resp[0] = s->te << 7;
+ break;
+ case 0x0f: /* RDDSDR */
+ s->p = 0;
+ s->resp[0] = s->selfcheck;
+ break;
+
+ case 0x10: /* SLPIN */
+ s->sleep = 1;
+ break;
+ case 0x11: /* SLPOUT */
+ s->sleep = 0;
+ s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */
+ break;
+
+ case 0x12: /* PTLON */
+ s->partial = 1;
+ s->normal = 0;
+ s->vscr = 0;
+ break;
+ case 0x13: /* NORON */
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ break;
+
+ case 0x20: /* INVOFF */
+ s->invert = 0;
+ break;
+ case 0x21: /* INVON */
+ s->invert = 1;
+ break;
+
+ case 0x22: /* APOFF */
+ case 0x23: /* APON */
+ goto bad_cmd;
+
+ case 0x25: /* WRCNTR */
+ if (s->pm < 0)
+ s->pm = 1;
+ goto bad_cmd;
+
+ case 0x26: /* GAMSET */
+ if (!s->pm)
+ s->gamma = ffs(s->param[0] & 0xf) - 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x28: /* DISPOFF */
+ s->onoff = 0;
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+ break;
+ case 0x29: /* DISPON */
+ s->onoff = 1;
+ fprintf(stderr, "%s: Display on\n", __FUNCTION__);
+ break;
+
+ case 0x2a: /* CASET */
+ case 0x2b: /* RASET */
+ case 0x2c: /* RAMWR */
+ case 0x2d: /* RGBSET */
+ case 0x2e: /* RAMRD */
+ case 0x30: /* PTLAR */
+ case 0x33: /* SCRLAR */
+ goto bad_cmd;
+
+ case 0x34: /* TEOFF */
+ s->te = 0;
+ break;
+ case 0x35: /* TEON */
+ if (!s->pm)
+ s->te = 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x36: /* MADCTR */
+ goto bad_cmd;
+
+ case 0x37: /* VSCSAD */
+ s->partial = 0;
+ s->normal = 0;
+ s->vscr = 1;
+ break;
+
+ case 0x38: /* IDMOFF */
+ case 0x39: /* IDMON */
+ case 0x3a: /* COLMOD */
+ goto bad_cmd;
+
+ case 0xb0: /* CLKINT / DISCTL */
+ case 0xb1: /* CLKEXT */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xb4: /* FRMSEL */
+ break;
+
+ case 0xb5: /* FRM8SEL */
+ case 0xb6: /* TMPRNG / INIESC */
+ case 0xb7: /* TMPHIS / NOP2 */
+ case 0xb8: /* TMPREAD / MADCTL */
+ case 0xba: /* DISTCTR */
+ case 0xbb: /* EPVOL */
+ goto bad_cmd;
+
+ case 0xbd: /* Unknown */
+ s->p = 0;
+ s->resp[0] = 0;
+ s->resp[1] = 1;
+ break;
+
+ case 0xc2: /* IFMOD */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xc6: /* PWRCTL */
+ case 0xc7: /* PPWRCTL */
+ case 0xd0: /* EPWROUT */
+ case 0xd1: /* EPWRIN */
+ case 0xd4: /* RDEV */
+ case 0xd5: /* RDRR */
+ goto bad_cmd;
+
+ case 0xda: /* RDID1 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ break;
+ case 0xdb: /* RDID2 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 8) & 0xff;
+ break;
+ case 0xdc: /* RDID3 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 0) & 0xff;
+ break;
+
+ default:
+ bad_cmd:
+ fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static void *mipid_init(void)
+{
+ struct mipid_s *s = (struct mipid_s *) g_malloc0(sizeof(*s));
+
+ s->id = 0x838f03;
+ mipid_reset(s);
+
+ return s;
+}
+
+static void n8x0_spi_setup(struct n800_s *s)
+{
+ void *tsc = s->ts.opaque;
+ void *mipid = mipid_init();
+
+ omap_mcspi_attach(s->mpu->mcspi[0], s->ts.txrx, tsc, 0);
+ omap_mcspi_attach(s->mpu->mcspi[0], mipid_txrx, mipid, 1);
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to enable the Blizzard ourselves. */
+static void n800_dss_init(struct rfbi_chip_s *chip)
+{
+ uint8_t *fb_blank;
+
+ chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */
+ chip->write(chip->opaque, 1, 0x64);
+ chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */
+ chip->write(chip->opaque, 1, 0x1e);
+ chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */
+ chip->write(chip->opaque, 1, 0xe0);
+ chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */
+ chip->write(chip->opaque, 1, 0x01);
+ chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */
+ chip->write(chip->opaque, 1, 0x06);
+ chip->write(chip->opaque, 0, 0x68); /* Display Mode register */
+ chip->write(chip->opaque, 1, 1); /* Enable bit */
+
+ chip->write(chip->opaque, 0, 0x6c);
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Data Format */
+ chip->write(chip->opaque, 1, 0x01); /* Data Source Select */
+
+ fb_blank = memset(g_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2);
+ /* Display Memory Data Port */
+ chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800);
+ g_free(fb_blank);
+}
+
+static void n8x0_dss_setup(struct n800_s *s)
+{
+ s->blizzard.opaque = s1d13745_init(NULL);
+ s->blizzard.block = s1d13745_write_block;
+ s->blizzard.write = s1d13745_write;
+ s->blizzard.read = s1d13745_read;
+
+ omap_rfbi_attach(s->mpu->dss, 0, &s->blizzard);
+}
+
+static void n8x0_cbus_setup(struct n800_s *s)
+{
+ qemu_irq dat_out = qdev_get_gpio_in(s->mpu->gpio, N8X0_CBUS_DAT_GPIO);
+ qemu_irq retu_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_RETU_GPIO);
+ qemu_irq tahvo_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TAHVO_GPIO);
+
+ CBus *cbus = cbus_init(dat_out);
+
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk);
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat);
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel);
+
+ cbus_attach(cbus, s->retu = retu_init(retu_irq, 1));
+ cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1));
+}
+
+static void n8x0_uart_setup(struct n800_s *s)
+{
+ CharDriverState *radio = uart_hci_init(
+ qdev_get_gpio_in(s->mpu->gpio, N8X0_BT_HOST_WKUP_GPIO));
+
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO,
+ csrhci_pins_get(radio)[csrhci_pin_reset]);
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_WKUP_GPIO,
+ csrhci_pins_get(radio)[csrhci_pin_wakeup]);
+
+ omap_uart_attach(s->mpu->uart[BT_UART], radio);
+}
+
+static void n8x0_usb_setup(struct n800_s *s)
+{
+ SysBusDevice *dev;
+ s->usb = qdev_create(NULL, "tusb6010");
+ dev = SYS_BUS_DEVICE(s->usb);
+ qdev_init_nofail(s->usb);
+ sysbus_connect_irq(dev, 0,
+ qdev_get_gpio_in(s->mpu->gpio, N8X0_TUSB_INT_GPIO));
+ /* Using the NOR interface */
+ omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_ASYNC_CS,
+ sysbus_mmio_get_region(dev, 0));
+ omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_SYNC_CS,
+ sysbus_mmio_get_region(dev, 1));
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_TUSB_ENABLE_GPIO,
+ qdev_get_gpio_in(s->usb, 0)); /* tusb_pwr */
+}
+
+/* Setup done before the main bootloader starts by some early setup code
+ * - used when we want to run the main bootloader in emulation. This
+ * isn't documented. */
+static uint32_t n800_pinout[104] = {
+ 0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0,
+ 0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808,
+ 0x08080808, 0x180800c4, 0x00b80000, 0x08080808,
+ 0x080800bc, 0x00cc0808, 0x08081818, 0x18180128,
+ 0x01241800, 0x18181818, 0x000000f0, 0x01300000,
+ 0x00001b0b, 0x1b0f0138, 0x00e0181b, 0x1b031b0b,
+ 0x180f0078, 0x00740018, 0x0f0f0f1a, 0x00000080,
+ 0x007c0000, 0x00000000, 0x00000088, 0x00840000,
+ 0x00000000, 0x00000094, 0x00980300, 0x0f180003,
+ 0x0000008c, 0x00900f0f, 0x0f0f1b00, 0x0f00009c,
+ 0x01140000, 0x1b1b0f18, 0x0818013c, 0x01400008,
+ 0x00001818, 0x000b0110, 0x010c1800, 0x0b030b0f,
+ 0x181800f4, 0x00f81818, 0x00000018, 0x000000fc,
+ 0x00401808, 0x00000000, 0x0f1b0030, 0x003c0008,
+ 0x00000000, 0x00000038, 0x00340000, 0x00000000,
+ 0x1a080070, 0x00641a1a, 0x08080808, 0x08080060,
+ 0x005c0808, 0x08080808, 0x08080058, 0x00540808,
+ 0x08080808, 0x0808006c, 0x00680808, 0x08080808,
+ 0x000000a8, 0x00b00000, 0x08080808, 0x000000a0,
+ 0x00a40000, 0x00000000, 0x08ff0050, 0x004c0808,
+ 0xffffffff, 0xffff0048, 0x0044ffff, 0xffffffff,
+ 0x000000ac, 0x01040800, 0x08080b0f, 0x18180100,
+ 0x01081818, 0x0b0b1808, 0x1a0300e4, 0x012c0b1a,
+ 0x02020018, 0x0b000134, 0x011c0800, 0x0b1b1b00,
+ 0x0f0000c8, 0x00ec181b, 0x000f0f02, 0x00180118,
+ 0x01200000, 0x0f0b1b1b, 0x0f0200e8, 0x0000020b,
+};
+
+static void n800_setup_nolo_tags(void *sram_base)
+{
+ int i;
+ uint32_t *p = sram_base + 0x8000;
+ uint32_t *v = sram_base + 0xa000;
+
+ memset(p, 0, 0x3000);
+
+ strcpy((void *) (p + 0), "QEMU N800");
+
+ strcpy((void *) (p + 8), "F5");
+
+ stl_raw(p + 10, 0x04f70000);
+ strcpy((void *) (p + 9), "RX-34");
+
+ /* RAM size in MB? */
+ stl_raw(p + 12, 0x80);
+
+ /* Pointer to the list of tags */
+ stl_raw(p + 13, OMAP2_SRAM_BASE + 0x9000);
+
+ /* The NOLO tags start here */
+ p = sram_base + 0x9000;
+#define ADD_TAG(tag, len) \
+ stw_raw((uint16_t *) p + 0, tag); \
+ stw_raw((uint16_t *) p + 1, len); p ++; \
+ stl_raw(p ++, OMAP2_SRAM_BASE | (((void *) v - sram_base) & 0xffff));
+
+ /* OMAP STI console? Pin out settings? */
+ ADD_TAG(0x6e01, 414);
+ for (i = 0; i < ARRAY_SIZE(n800_pinout); i ++)
+ stl_raw(v ++, n800_pinout[i]);
+
+ /* Kernel memsize? */
+ ADD_TAG(0x6e05, 1);
+ stl_raw(v ++, 2);
+
+ /* NOLO serial console */
+ ADD_TAG(0x6e02, 4);
+ stl_raw(v ++, XLDR_LL_UART); /* UART number (1 - 3) */
+
+#if 0
+ /* CBUS settings (Retu/AVilma) */
+ ADD_TAG(0x6e03, 6);
+ stw_raw((uint16_t *) v + 0, 65); /* CBUS GPIO0 */
+ stw_raw((uint16_t *) v + 1, 66); /* CBUS GPIO1 */
+ stw_raw((uint16_t *) v + 2, 64); /* CBUS GPIO2 */
+ v += 2;
+#endif
+
+ /* Nokia ASIC BB5 (Retu/Tahvo) */
+ ADD_TAG(0x6e0a, 4);
+ stw_raw((uint16_t *) v + 0, 111); /* "Retu" interrupt GPIO */
+ stw_raw((uint16_t *) v + 1, 108); /* "Tahvo" interrupt GPIO */
+ v ++;
+
+ /* LCD console? */
+ ADD_TAG(0x6e04, 4);
+ stw_raw((uint16_t *) v + 0, 30); /* ??? */
+ stw_raw((uint16_t *) v + 1, 24); /* ??? */
+ v ++;
+
+#if 0
+ /* LCD settings */
+ ADD_TAG(0x6e06, 2);
+ stw_raw((uint16_t *) (v ++), 15); /* ??? */
+#endif
+
+ /* I^2C (Menelaus) */
+ ADD_TAG(0x6e07, 4);
+ stl_raw(v ++, 0x00720000); /* ??? */
+
+ /* Unknown */
+ ADD_TAG(0x6e0b, 6);
+ stw_raw((uint16_t *) v + 0, 94); /* ??? */
+ stw_raw((uint16_t *) v + 1, 23); /* ??? */
+ stw_raw((uint16_t *) v + 2, 0); /* ??? */
+ v += 2;
+
+ /* OMAP gpio switch info */
+ ADD_TAG(0x6e0c, 80);
+ strcpy((void *) v, "bat_cover"); v += 3;
+ stw_raw((uint16_t *) v + 0, 110); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 1); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "cam_act"); v += 3;
+ stw_raw((uint16_t *) v + 0, 95); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 32); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "cam_turn"); v += 3;
+ stw_raw((uint16_t *) v + 0, 12); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 33); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "headphone"); v += 3;
+ stw_raw((uint16_t *) v + 0, 107); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 17); /* GPIO num ??? */
+ v += 2;
+
+ /* Bluetooth */
+ ADD_TAG(0x6e0e, 12);
+ stl_raw(v ++, 0x5c623d01); /* ??? */
+ stl_raw(v ++, 0x00000201); /* ??? */
+ stl_raw(v ++, 0x00000000); /* ??? */
+
+ /* CX3110x WLAN settings */
+ ADD_TAG(0x6e0f, 8);
+ stl_raw(v ++, 0x00610025); /* ??? */
+ stl_raw(v ++, 0xffff0057); /* ??? */
+
+ /* MMC host settings */
+ ADD_TAG(0x6e10, 12);
+ stl_raw(v ++, 0xffff000f); /* ??? */
+ stl_raw(v ++, 0xffffffff); /* ??? */
+ stl_raw(v ++, 0x00000060); /* ??? */
+
+ /* OneNAND chip select */
+ ADD_TAG(0x6e11, 10);
+ stl_raw(v ++, 0x00000401); /* ??? */
+ stl_raw(v ++, 0x0002003a); /* ??? */
+ stl_raw(v ++, 0x00000002); /* ??? */
+
+ /* TEA5761 sensor settings */
+ ADD_TAG(0x6e12, 2);
+ stl_raw(v ++, 93); /* GPIO num ??? */
+
+#if 0
+ /* Unknown tag */
+ ADD_TAG(6e09, 0);
+
+ /* Kernel UART / console */
+ ADD_TAG(6e12, 0);
+#endif
+
+ /* End of the list */
+ stl_raw(p ++, 0x00000000);
+ stl_raw(p ++, 0x00000000);
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to set up GPMC mappings ourselves. */
+static void n800_gpmc_init(struct n800_s *s)
+{
+ uint32_t config7 =
+ (0xf << 8) | /* MASKADDRESS */
+ (1 << 6) | /* CSVALID */
+ (4 << 0); /* BASEADDRESS */
+
+ cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */
+ (void *) &config7, sizeof(config7));
+}
+
+/* Setup sequence done by the bootloader */
+static void n8x0_boot_init(void *opaque)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ uint32_t buf;
+
+ /* PRCM setup */
+#define omap_writel(addr, val) \
+ buf = (val); \
+ cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf))
+
+ omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */
+ omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */
+ omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */
+ omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */
+ omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */
+ omap_writel(0x48008098, 0); /* PRCM_POLCTRL */
+ omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */
+ omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */
+ omap_writel(0x48008158, 1); /* RM_RSTST_MPU */
+ omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */
+ omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */
+ omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */
+ omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */
+ omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */
+ omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */
+ omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */
+ omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */
+ omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */
+ omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */
+ omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */
+ omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */
+ omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */
+ omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */
+ omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */
+ omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */
+ omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */
+ omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */
+ omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */
+ omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */
+ omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */
+ omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */
+ omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */
+ omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */
+ omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */
+ omap_writel(0x48008540, /* CM_CLKSEL1_PLL */
+ (0x78 << 12) | (6 << 8));
+ omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */
+
+ /* GPMC setup */
+ n800_gpmc_init(s);
+
+ /* Video setup */
+ n800_dss_init(&s->blizzard);
+
+ /* CPU setup */
+ s->mpu->cpu->env.GE = 0x5;
+
+ /* If the machine has a slided keyboard, open it */
+ if (s->kbd)
+ qemu_irq_raise(qdev_get_gpio_in(s->mpu->gpio, N810_SLIDE_GPIO));
+}
+
+#define OMAP_TAG_NOKIA_BT 0x4e01
+#define OMAP_TAG_WLAN_CX3110X 0x4e02
+#define OMAP_TAG_CBUS 0x4e03
+#define OMAP_TAG_EM_ASIC_BB5 0x4e04
+
+static struct omap_gpiosw_info_s {
+ const char *name;
+ int line;
+ int type;
+} n800_gpiosw_info[] = {
+ {
+ "bat_cover", N800_BAT_COVER_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ }, {
+ "cam_act", N800_CAM_ACT_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY,
+ }, {
+ "cam_turn", N800_CAM_TURN_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED,
+ }, {
+ "headphone", N8X0_HEADPHONE_GPIO,
+ OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED,
+ },
+ { NULL }
+}, n810_gpiosw_info[] = {
+ {
+ "gps_reset", N810_GPS_RESET_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "gps_wakeup", N810_GPS_WAKEUP_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "headphone", N8X0_HEADPHONE_GPIO,
+ OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED,
+ }, {
+ "kb_lock", N810_KB_LOCK_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ }, {
+ "sleepx_led", N810_SLEEPX_LED_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "slide", N810_SLIDE_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ },
+ { NULL }
+};
+
+static struct omap_partition_info_s {
+ uint32_t offset;
+ uint32_t size;
+ int mask;
+ const char *name;
+} n800_part_info[] = {
+ { 0x00000000, 0x00020000, 0x3, "bootloader" },
+ { 0x00020000, 0x00060000, 0x0, "config" },
+ { 0x00080000, 0x00200000, 0x0, "kernel" },
+ { 0x00280000, 0x00200000, 0x3, "initfs" },
+ { 0x00480000, 0x0fb80000, 0x3, "rootfs" },
+
+ { 0, 0, 0, NULL }
+}, n810_part_info[] = {
+ { 0x00000000, 0x00020000, 0x3, "bootloader" },
+ { 0x00020000, 0x00060000, 0x0, "config" },
+ { 0x00080000, 0x00220000, 0x0, "kernel" },
+ { 0x002a0000, 0x00400000, 0x0, "initfs" },
+ { 0x006a0000, 0x0f960000, 0x0, "rootfs" },
+
+ { 0, 0, 0, NULL }
+};
+
+static bdaddr_t n8x0_bd_addr = {{ N8X0_BD_ADDR }};
+
+static int n8x0_atag_setup(void *p, int model)
+{
+ uint8_t *b;
+ uint16_t *w;
+ uint32_t *l;
+ struct omap_gpiosw_info_s *gpiosw;
+ struct omap_partition_info_s *partition;
+ const char *tag;
+
+ w = p;
+
+ stw_raw(w ++, OMAP_TAG_UART); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */
+ w ++;
+
+#if 0
+ stw_raw(w ++, OMAP_TAG_SERIAL_CONSOLE); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, XLDR_LL_UART + 1); /* u8 console_uart */
+ stw_raw(w ++, 115200); /* u32 console_speed */
+#endif
+
+ stw_raw(w ++, OMAP_TAG_LCD); /* u16 tag */
+ stw_raw(w ++, 36); /* u16 len */
+ strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */
+ w += 8;
+ strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */
+ w += 8;
+ stw_raw(w ++, N810_BLIZZARD_RESET_GPIO); /* TODO: n800 s16 nreset_gpio */
+ stw_raw(w ++, 24); /* u8 data_lines */
+
+ stw_raw(w ++, OMAP_TAG_CBUS); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */
+ stw_raw(w ++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */
+ stw_raw(w ++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */
+ w ++;
+
+ stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */
+ stw_raw(w ++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */
+
+ gpiosw = (model == 810) ? n810_gpiosw_info : n800_gpiosw_info;
+ for (; gpiosw->name; gpiosw ++) {
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, gpiosw->name); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, gpiosw->line); /* u16 gpio */
+ stw_raw(w ++, gpiosw->type);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+ }
+
+ stw_raw(w ++, OMAP_TAG_NOKIA_BT); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+ b = (void *) w;
+ stb_raw(b ++, 0x01); /* u8 chip_type (CSR) */
+ stb_raw(b ++, N8X0_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */
+ stb_raw(b ++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */
+ stb_raw(b ++, N8X0_BT_RESET_GPIO); /* u8 reset_gpio */
+ stb_raw(b ++, BT_UART + 1); /* u8 bt_uart */
+ memcpy(b, &n8x0_bd_addr, 6); /* u8 bd_addr[6] */
+ b += 6;
+ stb_raw(b ++, 0x02); /* u8 bt_sysclk (38.4) */
+ w = (void *) b;
+
+ stw_raw(w ++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, 0x25); /* u8 chip_type */
+ stw_raw(w ++, N8X0_WLAN_PWR_GPIO); /* s16 power_gpio */
+ stw_raw(w ++, N8X0_WLAN_IRQ_GPIO); /* s16 irq_gpio */
+ stw_raw(w ++, -1); /* s16 spi_cs_gpio */
+
+ stw_raw(w ++, OMAP_TAG_MMC); /* u16 tag */
+ stw_raw(w ++, 16); /* u16 len */
+ if (model == 810) {
+ stw_raw(w ++, 0x23f); /* unsigned flags */
+ stw_raw(w ++, -1); /* s16 power_pin */
+ stw_raw(w ++, -1); /* s16 switch_pin */
+ stw_raw(w ++, -1); /* s16 wp_pin */
+ stw_raw(w ++, 0x240); /* unsigned flags */
+ stw_raw(w ++, 0xc000); /* s16 power_pin */
+ stw_raw(w ++, 0x0248); /* s16 switch_pin */
+ stw_raw(w ++, 0xc000); /* s16 wp_pin */
+ } else {
+ stw_raw(w ++, 0xf); /* unsigned flags */
+ stw_raw(w ++, -1); /* s16 power_pin */
+ stw_raw(w ++, -1); /* s16 switch_pin */
+ stw_raw(w ++, -1); /* s16 wp_pin */
+ stw_raw(w ++, 0); /* unsigned flags */
+ stw_raw(w ++, 0); /* s16 power_pin */
+ stw_raw(w ++, 0); /* s16 switch_pin */
+ stw_raw(w ++, 0); /* s16 wp_pin */
+ }
+
+ stw_raw(w ++, OMAP_TAG_TEA5761); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N8X0_TEA5761_CS_GPIO); /* u16 enable_gpio */
+ w ++;
+
+ partition = (model == 810) ? n810_part_info : n800_part_info;
+ for (; partition->name; partition ++) {
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, partition->name); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, partition->size); /* unsigned int size */
+ stl_raw(l ++, partition->offset); /* unsigned int offset */
+ stl_raw(l ++, partition->mask); /* unsigned int mask_flags */
+ w = (void *) l;
+ }
+
+ stw_raw(w ++, OMAP_TAG_BOOT_REASON); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+#if 0
+ strcpy((void *) w, "por"); /* char reason_str[12] */
+ strcpy((void *) w, "charger"); /* char reason_str[12] */
+ strcpy((void *) w, "32wd_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sw_rst"); /* char reason_str[12] */
+ strcpy((void *) w, "mbus"); /* char reason_str[12] */
+ strcpy((void *) w, "unknown"); /* char reason_str[12] */
+ strcpy((void *) w, "swdg_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sec_vio"); /* char reason_str[12] */
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+ strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */
+#else
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+#endif
+ w += 6;
+
+ tag = (model == 810) ? "RX-44" : "RX-34";
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "product"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, tag); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "hw-build"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "QEMU ");
+ pstrcat((void *) w, 12, qemu_get_version()); /* char version[12] */
+ w += 6;
+
+ tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu";
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "nolo"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, tag); /* char version[12] */
+ w += 6;
+
+ return (void *) w - p;
+}
+
+static int n800_atag_setup(const struct arm_boot_info *info, void *p)
+{
+ return n8x0_atag_setup(p, 800);
+}
+
+static int n810_atag_setup(const struct arm_boot_info *info, void *p)
+{
+ return n8x0_atag_setup(p, 810);
+}
+
+static void n8x0_init(QEMUMachineInitArgs *args,
+ struct arm_boot_info *binfo, int model)
+{
+ MemoryRegion *sysmem = get_system_memory();
+ struct n800_s *s = (struct n800_s *) g_malloc0(sizeof(*s));
+ int sdram_size = binfo->ram_size;
+ DisplayState *ds;
+
+ s->mpu = omap2420_mpu_init(sysmem, sdram_size, args->cpu_model);
+
+ /* Setup peripherals
+ *
+ * Believed external peripherals layout in the N810:
+ * (spi bus 1)
+ * tsc2005
+ * lcd_mipid
+ * (spi bus 2)
+ * Conexant cx3110x (WLAN)
+ * optional: pc2400m (WiMAX)
+ * (i2c bus 0)
+ * TLV320AIC33 (audio codec)
+ * TCM825x (camera by Toshiba)
+ * lp5521 (clever LEDs)
+ * tsl2563 (light sensor, hwmon, model 7, rev. 0)
+ * lm8323 (keypad, manf 00, rev 04)
+ * (i2c bus 1)
+ * tmp105 (temperature sensor, hwmon)
+ * menelaus (pm)
+ * (somewhere on i2c - maybe N800-only)
+ * tea5761 (FM tuner)
+ * (serial 0)
+ * GPS
+ * (some serial port)
+ * csr41814 (Bluetooth)
+ */
+ n8x0_gpio_setup(s);
+ n8x0_nand_setup(s);
+ n8x0_i2c_setup(s);
+ if (model == 800)
+ n800_tsc_kbd_setup(s);
+ else if (model == 810) {
+ n810_tsc_setup(s);
+ n810_kbd_setup(s);
+ }
+ n8x0_spi_setup(s);
+ n8x0_dss_setup(s);
+ n8x0_cbus_setup(s);
+ n8x0_uart_setup(s);
+ if (usb_enabled(false)) {
+ n8x0_usb_setup(s);
+ }
+
+ if (args->kernel_filename) {
+ /* Or at the linux loader. */
+ binfo->kernel_filename = args->kernel_filename;
+ binfo->kernel_cmdline = args->kernel_cmdline;
+ binfo->initrd_filename = args->initrd_filename;
+ arm_load_kernel(s->mpu->cpu, binfo);
+
+ qemu_register_reset(n8x0_boot_init, s);
+ }
+
+ if (option_rom[0].name &&
+ (args->boot_device[0] == 'n' || !args->kernel_filename)) {
+ int rom_size;
+ uint8_t nolo_tags[0x10000];
+ /* No, wait, better start at the ROM. */
+ s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000;
+
+ /* This is intended for loading the `secondary.bin' program from
+ * Nokia images (the NOLO bootloader). The entry point seems
+ * to be at OMAP2_Q2_BASE + 0x400000.
+ *
+ * The `2nd.bin' files contain some kind of earlier boot code and
+ * for them the entry point needs to be set to OMAP2_SRAM_BASE.
+ *
+ * The code above is for loading the `zImage' file from Nokia
+ * images. */
+ rom_size = load_image_targphys(option_rom[0].name,
+ OMAP2_Q2_BASE + 0x400000,
+ sdram_size - 0x400000);
+ printf("%i bytes of image loaded\n", rom_size);
+
+ n800_setup_nolo_tags(nolo_tags);
+ cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000);
+ }
+ /* FIXME: We shouldn't really be doing this here. The LCD controller
+ will set the size once configured, so this just sets an initial
+ size until the guest activates the display. */
+ ds = get_displaystate();
+ ds->surface = qemu_resize_displaysurface(ds, 800, 480);
+ dpy_gfx_resize(ds);
+}
+
+static struct arm_boot_info n800_binfo = {
+ .loader_start = OMAP2_Q2_BASE,
+ /* Actually two chips of 0x4000000 bytes each */
+ .ram_size = 0x08000000,
+ .board_id = 0x4f7,
+ .atag_board = n800_atag_setup,
+};
+
+static struct arm_boot_info n810_binfo = {
+ .loader_start = OMAP2_Q2_BASE,
+ /* Actually two chips of 0x4000000 bytes each */
+ .ram_size = 0x08000000,
+ /* 0x60c and 0x6bf (WiMAX Edition) have been assigned but are not
+ * used by some older versions of the bootloader and 5555 is used
+ * instead (including versions that shipped with many devices). */
+ .board_id = 0x60c,
+ .atag_board = n810_atag_setup,
+};
+
+static void n800_init(QEMUMachineInitArgs *args)
+{
+ return n8x0_init(args, &n800_binfo, 800);
+}
+
+static void n810_init(QEMUMachineInitArgs *args)
+{
+ return n8x0_init(args, &n810_binfo, 810);
+}
+
+static QEMUMachine n800_machine = {
+ .name = "n800",
+ .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)",
+ .init = n800_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine n810_machine = {
+ .name = "n810",
+ .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)",
+ .init = n810_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void nseries_machine_init(void)
+{
+ qemu_register_machine(&n800_machine);
+ qemu_register_machine(&n810_machine);
+}
+
+machine_init(nseries_machine_init);
diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c
new file mode 100644
index 0000000..8598233
--- /dev/null
+++ b/hw/arm/omap_sx1.c
@@ -0,0 +1,238 @@
+/* omap_sx1.c Support for the Siemens SX1 smartphone emulation.
+ *
+ * Copyright (C) 2008
+ * Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ * Copyright (C) 2007 Vladimir Ananiev <vovan888@gmail.com>
+ *
+ * based on PalmOne's (TM) PDAs support (palm.c)
+ */
+
+/*
+ * PalmOne's (TM) PDAs.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 "hw/hw.h"
+#include "ui/console.h"
+#include "hw/omap.h"
+#include "hw/boards.h"
+#include "hw/arm-misc.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+
+/*****************************************************************************/
+/* Siemens SX1 Cellphone V1 */
+/* - ARM OMAP310 processor
+ * - SRAM 192 kB
+ * - SDRAM 32 MB at 0x10000000
+ * - Boot flash 16 MB at 0x00000000
+ * - Application flash 8 MB at 0x04000000
+ * - 3 serial ports
+ * - 1 SecureDigital
+ * - 1 LCD display
+ * - 1 RTC
+ */
+
+/*****************************************************************************/
+/* Siemens SX1 Cellphone V2 */
+/* - ARM OMAP310 processor
+ * - SRAM 192 kB
+ * - SDRAM 32 MB at 0x10000000
+ * - Boot flash 32 MB at 0x00000000
+ * - 3 serial ports
+ * - 1 SecureDigital
+ * - 1 LCD display
+ * - 1 RTC
+ */
+
+static uint64_t static_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ uint32_t mask = (4 / size) - 1;
+
+ return *val >> ((offset & mask) << 3);
+}
+
+static void static_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+#ifdef SPY
+ printf("%s: value %" PRIx64 " %u bytes written at 0x%x\n",
+ __func__, value, size, (int)offset);
+#endif
+}
+
+static const MemoryRegionOps static_ops = {
+ .read = static_read,
+ .write = static_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+#define sdram_size 0x02000000
+#define sector_size (128 * 1024)
+#define flash0_size (16 * 1024 * 1024)
+#define flash1_size ( 8 * 1024 * 1024)
+#define flash2_size (32 * 1024 * 1024)
+#define total_ram_v1 (sdram_size + flash0_size + flash1_size + OMAP15XX_SRAM_SIZE)
+#define total_ram_v2 (sdram_size + flash2_size + OMAP15XX_SRAM_SIZE)
+
+static struct arm_boot_info sx1_binfo = {
+ .loader_start = OMAP_EMIFF_BASE,
+ .ram_size = sdram_size,
+ .board_id = 0x265,
+};
+
+static void sx1_init(QEMUMachineInitArgs *args, const int version)
+{
+ struct omap_mpu_state_s *mpu;
+ MemoryRegion *address_space = get_system_memory();
+ MemoryRegion *flash = g_new(MemoryRegion, 1);
+ MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
+ MemoryRegion *cs = g_new(MemoryRegion, 4);
+ static uint32_t cs0val = 0x00213090;
+ static uint32_t cs1val = 0x00215070;
+ static uint32_t cs2val = 0x00001139;
+ static uint32_t cs3val = 0x00001139;
+ DriveInfo *dinfo;
+ int fl_idx;
+ uint32_t flash_size = flash0_size;
+ int be;
+
+ if (version == 2) {
+ flash_size = flash2_size;
+ }
+
+ mpu = omap310_mpu_init(address_space, sx1_binfo.ram_size, args->cpu_model);
+
+ /* External Flash (EMIFS) */
+ memory_region_init_ram(flash, "omap_sx1.flash0-0", flash_size);
+ vmstate_register_ram_global(flash);
+ memory_region_set_readonly(flash, true);
+ memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash);
+
+ memory_region_init_io(&cs[0], &static_ops, &cs0val,
+ "sx1.cs0", OMAP_CS0_SIZE - flash_size);
+ memory_region_add_subregion(address_space,
+ OMAP_CS0_BASE + flash_size, &cs[0]);
+
+
+ memory_region_init_io(&cs[2], &static_ops, &cs2val,
+ "sx1.cs2", OMAP_CS2_SIZE);
+ memory_region_add_subregion(address_space,
+ OMAP_CS2_BASE, &cs[2]);
+
+ memory_region_init_io(&cs[3], &static_ops, &cs3val,
+ "sx1.cs3", OMAP_CS3_SIZE);
+ memory_region_add_subregion(address_space,
+ OMAP_CS2_BASE, &cs[3]);
+
+ fl_idx = 0;
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+
+ if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ if (!pflash_cfi01_register(OMAP_CS0_BASE, NULL,
+ "omap_sx1.flash0-1", flash_size,
+ dinfo->bdrv, sector_size,
+ flash_size / sector_size,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory %d.\n",
+ fl_idx);
+ }
+ fl_idx++;
+ }
+
+ if ((version == 1) &&
+ (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ memory_region_init_ram(flash_1, "omap_sx1.flash1-0", flash1_size);
+ vmstate_register_ram_global(flash_1);
+ memory_region_set_readonly(flash_1, true);
+ memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1);
+
+ memory_region_init_io(&cs[1], &static_ops, &cs1val,
+ "sx1.cs1", OMAP_CS1_SIZE - flash1_size);
+ memory_region_add_subregion(address_space,
+ OMAP_CS1_BASE + flash1_size, &cs[1]);
+
+ if (!pflash_cfi01_register(OMAP_CS1_BASE, NULL,
+ "omap_sx1.flash1-1", flash1_size,
+ dinfo->bdrv, sector_size,
+ flash1_size / sector_size,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory %d.\n",
+ fl_idx);
+ }
+ fl_idx++;
+ } else {
+ memory_region_init_io(&cs[1], &static_ops, &cs1val,
+ "sx1.cs1", OMAP_CS1_SIZE);
+ memory_region_add_subregion(address_space,
+ OMAP_CS1_BASE, &cs[1]);
+ }
+
+ if (!args->kernel_filename && !fl_idx) {
+ fprintf(stderr, "Kernel or Flash image must be specified\n");
+ exit(1);
+ }
+
+ /* Load the kernel. */
+ if (args->kernel_filename) {
+ sx1_binfo.kernel_filename = args->kernel_filename;
+ sx1_binfo.kernel_cmdline = args->kernel_cmdline;
+ sx1_binfo.initrd_filename = args->initrd_filename;
+ arm_load_kernel(mpu->cpu, &sx1_binfo);
+ }
+
+ /* TODO: fix next line */
+ //~ qemu_console_resize(ds, 640, 480);
+}
+
+static void sx1_init_v1(QEMUMachineInitArgs *args)
+{
+ sx1_init(args, 1);
+}
+
+static void sx1_init_v2(QEMUMachineInitArgs *args)
+{
+ sx1_init(args, 2);
+}
+
+static QEMUMachine sx1_machine_v2 = {
+ .name = "sx1",
+ .desc = "Siemens SX1 (OMAP310) V2",
+ .init = sx1_init_v2,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine sx1_machine_v1 = {
+ .name = "sx1-v1",
+ .desc = "Siemens SX1 (OMAP310) V1",
+ .init = sx1_init_v1,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void sx1_machine_init(void)
+{
+ qemu_register_machine(&sx1_machine_v2);
+ qemu_register_machine(&sx1_machine_v1);
+}
+
+machine_init(sx1_machine_init);
diff --git a/hw/arm/palm.c b/hw/arm/palm.c
new file mode 100644
index 0000000..91bc74a
--- /dev/null
+++ b/hw/arm/palm.c
@@ -0,0 +1,291 @@
+/*
+ * PalmOne's (TM) PDAs.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 or
+ * (at your option) version 3 of the License.
+ *
+ * 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 "hw/hw.h"
+#include "audio/audio.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "hw/omap.h"
+#include "hw/boards.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/loader.h"
+#include "exec/address-spaces.h"
+
+static uint32_t static_readb(void *opaque, hwaddr offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 3) << 3);
+}
+
+static uint32_t static_readh(void *opaque, hwaddr offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 1) << 3);
+}
+
+static uint32_t static_readw(void *opaque, hwaddr offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 0) << 3);
+}
+
+static void static_write(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+#ifdef SPY
+ printf("%s: value %08lx written at " PA_FMT "\n",
+ __FUNCTION__, value, offset);
+#endif
+}
+
+static const MemoryRegionOps static_ops = {
+ .old_mmio = {
+ .read = { static_readb, static_readh, static_readw, },
+ .write = { static_write, static_write, static_write, },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* Palm Tunsgten|E support */
+
+/* Shared GPIOs */
+#define PALMTE_USBDETECT_GPIO 0
+#define PALMTE_USB_OR_DC_GPIO 1
+#define PALMTE_TSC_GPIO 4
+#define PALMTE_PINTDAV_GPIO 6
+#define PALMTE_MMC_WP_GPIO 8
+#define PALMTE_MMC_POWER_GPIO 9
+#define PALMTE_HDQ_GPIO 11
+#define PALMTE_HEADPHONES_GPIO 14
+#define PALMTE_SPEAKER_GPIO 15
+/* MPU private GPIOs */
+#define PALMTE_DC_GPIO 2
+#define PALMTE_MMC_SWITCH_GPIO 4
+#define PALMTE_MMC1_GPIO 6
+#define PALMTE_MMC2_GPIO 7
+#define PALMTE_MMC3_GPIO 11
+
+static MouseTransformInfo palmte_pointercal = {
+ .x = 320,
+ .y = 320,
+ .a = { -5909, 8, 22465308, 104, 7644, -1219972, 65536 },
+};
+
+static void palmte_microwire_setup(struct omap_mpu_state_s *cpu)
+{
+ uWireSlave *tsc;
+
+ tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO));
+
+ omap_uwire_attach(cpu->microwire, tsc, 0);
+ omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc));
+
+ tsc210x_set_transform(tsc, &palmte_pointercal);
+}
+
+static struct {
+ int row;
+ int column;
+} palmte_keymap[0x80] = {
+ [0 ... 0x7f] = { -1, -1 },
+ [0x3b] = { 0, 0 }, /* F1 -> Calendar */
+ [0x3c] = { 1, 0 }, /* F2 -> Contacts */
+ [0x3d] = { 2, 0 }, /* F3 -> Tasks List */
+ [0x3e] = { 3, 0 }, /* F4 -> Note Pad */
+ [0x01] = { 4, 0 }, /* Esc -> Power */
+ [0x4b] = { 0, 1 }, /* Left */
+ [0x50] = { 1, 1 }, /* Down */
+ [0x48] = { 2, 1 }, /* Up */
+ [0x4d] = { 3, 1 }, /* Right */
+ [0x4c] = { 4, 1 }, /* Centre */
+ [0x39] = { 4, 1 }, /* Spc -> Centre */
+};
+
+static void palmte_button_event(void *opaque, int keycode)
+{
+ struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque;
+
+ if (palmte_keymap[keycode & 0x7f].row != -1)
+ omap_mpuio_key(cpu->mpuio,
+ palmte_keymap[keycode & 0x7f].row,
+ palmte_keymap[keycode & 0x7f].column,
+ !(keycode & 0x80));
+}
+
+static void palmte_onoff_gpios(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ printf("%s: current to MMC/SD card %sabled.\n",
+ __FUNCTION__, level ? "dis" : "en");
+ break;
+ case 1:
+ printf("%s: internal speaker amplifier %s.\n",
+ __FUNCTION__, level ? "down" : "on");
+ break;
+
+ /* These LCD & Audio output signals have not been identified yet. */
+ case 2:
+ case 3:
+ case 4:
+ printf("%s: LCD GPIO%i %s.\n",
+ __FUNCTION__, line - 1, level ? "high" : "low");
+ break;
+ case 5:
+ case 6:
+ printf("%s: Audio GPIO%i %s.\n",
+ __FUNCTION__, line - 4, level ? "high" : "low");
+ break;
+ }
+}
+
+static void palmte_gpio_setup(struct omap_mpu_state_s *cpu)
+{
+ qemu_irq *misc_gpio;
+
+ omap_mmc_handlers(cpu->mmc,
+ qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO),
+ qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio)
+ [PALMTE_MMC_SWITCH_GPIO]));
+
+ misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7);
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]);
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]);
+ qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]);
+ qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]);
+ qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]);
+ omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]);
+ omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]);
+
+ /* Reset some inputs to initial state. */
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO));
+ qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[11]);
+}
+
+static struct arm_boot_info palmte_binfo = {
+ .loader_start = OMAP_EMIFF_BASE,
+ .ram_size = 0x02000000,
+ .board_id = 0x331,
+};
+
+static void palmte_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ struct omap_mpu_state_s *mpu;
+ int flash_size = 0x00800000;
+ int sdram_size = palmte_binfo.ram_size;
+ static uint32_t cs0val = 0xffffffff;
+ static uint32_t cs1val = 0x0000e1a0;
+ static uint32_t cs2val = 0x0000e1a0;
+ static uint32_t cs3val = 0xe1a0e1a0;
+ int rom_size, rom_loaded = 0;
+ DisplayState *ds = get_displaystate();
+ MemoryRegion *flash = g_new(MemoryRegion, 1);
+ MemoryRegion *cs = g_new(MemoryRegion, 4);
+
+ mpu = omap310_mpu_init(address_space_mem, sdram_size, cpu_model);
+
+ /* External Flash (EMIFS) */
+ memory_region_init_ram(flash, "palmte.flash", flash_size);
+ vmstate_register_ram_global(flash);
+ memory_region_set_readonly(flash, true);
+ memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash);
+
+ memory_region_init_io(&cs[0], &static_ops, &cs0val, "palmte-cs0",
+ OMAP_CS0_SIZE - flash_size);
+ memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE + flash_size,
+ &cs[0]);
+ memory_region_init_io(&cs[1], &static_ops, &cs1val, "palmte-cs1",
+ OMAP_CS1_SIZE);
+ memory_region_add_subregion(address_space_mem, OMAP_CS1_BASE, &cs[1]);
+ memory_region_init_io(&cs[2], &static_ops, &cs2val, "palmte-cs2",
+ OMAP_CS2_SIZE);
+ memory_region_add_subregion(address_space_mem, OMAP_CS2_BASE, &cs[2]);
+ memory_region_init_io(&cs[3], &static_ops, &cs3val, "palmte-cs3",
+ OMAP_CS3_SIZE);
+ memory_region_add_subregion(address_space_mem, OMAP_CS3_BASE, &cs[3]);
+
+ palmte_microwire_setup(mpu);
+
+ qemu_add_kbd_event_handler(palmte_button_event, mpu);
+
+ palmte_gpio_setup(mpu);
+
+ /* Setup initial (reset) machine state */
+ if (nb_option_roms) {
+ rom_size = get_image_size(option_rom[0].name);
+ if (rom_size > flash_size) {
+ fprintf(stderr, "%s: ROM image too big (%x > %x)\n",
+ __FUNCTION__, rom_size, flash_size);
+ rom_size = 0;
+ }
+ if (rom_size > 0) {
+ rom_size = load_image_targphys(option_rom[0].name, OMAP_CS0_BASE,
+ flash_size);
+ rom_loaded = 1;
+ }
+ if (rom_size < 0) {
+ fprintf(stderr, "%s: error loading '%s'\n",
+ __FUNCTION__, option_rom[0].name);
+ }
+ }
+
+ if (!rom_loaded && !kernel_filename) {
+ fprintf(stderr, "Kernel or ROM image must be specified\n");
+ exit(1);
+ }
+
+ /* Load the kernel. */
+ if (kernel_filename) {
+ palmte_binfo.kernel_filename = kernel_filename;
+ palmte_binfo.kernel_cmdline = kernel_cmdline;
+ palmte_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(mpu->cpu, &palmte_binfo);
+ }
+
+ /* FIXME: We shouldn't really be doing this here. The LCD controller
+ will set the size once configured, so this just sets an initial
+ size until the guest activates the display. */
+ ds->surface = qemu_resize_displaysurface(ds, 320, 320);
+ dpy_gfx_resize(ds);
+}
+
+static QEMUMachine palmte_machine = {
+ .name = "cheetah",
+ .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)",
+ .init = palmte_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void palmte_machine_init(void)
+{
+ qemu_register_machine(&palmte_machine);
+}
+
+machine_init(palmte_machine_init);
diff --git a/hw/arm/pic_cpu.c b/hw/arm/pic_cpu.c
new file mode 100644
index 0000000..a7ad893
--- /dev/null
+++ b/hw/arm/pic_cpu.c
@@ -0,0 +1,40 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL
+ */
+
+#include "hw/hw.h"
+#include "hw/arm-misc.h"
+
+/* Input 0 is IRQ and input 1 is FIQ. */
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ ARMCPU *cpu = opaque;
+ CPUARMState *env = &cpu->env;
+
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ);
+ break;
+ default:
+ hw_error("arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
+ }
+}
+
+qemu_irq *arm_pic_init_cpu(ARMCPU *cpu)
+{
+ return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2);
+}
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
new file mode 100644
index 0000000..5fb490c
--- /dev/null
+++ b/hw/arm/realview.c
@@ -0,0 +1,404 @@
+/*
+ * ARM RealView Baseboard System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/primecell.h"
+#include "hw/devices.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/i2c.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+
+#define SMP_BOOT_ADDR 0xe0000000
+#define SMP_BOOTREG_ADDR 0x10000030
+
+/* Board init. */
+
+static struct arm_boot_info realview_binfo = {
+ .smp_loader_start = SMP_BOOT_ADDR,
+ .smp_bootreg_addr = SMP_BOOTREG_ADDR,
+};
+
+/* The following two lists must be consistent. */
+enum realview_board_type {
+ BOARD_EB,
+ BOARD_EB_MPCORE,
+ BOARD_PB_A8,
+ BOARD_PBX_A9,
+};
+
+static const int realview_board_id[] = {
+ 0x33b,
+ 0x33b,
+ 0x769,
+ 0x76d
+};
+
+static void realview_init(QEMUMachineInitArgs *args,
+ enum realview_board_type board_type)
+{
+ ARMCPU *cpu = NULL;
+ CPUARMState *env;
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_hack = g_new(MemoryRegion, 1);
+ DeviceState *dev, *sysctl, *gpio2, *pl041;
+ SysBusDevice *busdev;
+ qemu_irq *irqp;
+ qemu_irq pic[64];
+ qemu_irq mmc_irq[2];
+ PCIBus *pci_bus;
+ NICInfo *nd;
+ i2c_bus *i2c;
+ int n;
+ int done_nic = 0;
+ qemu_irq cpu_irq[4];
+ int is_mpcore = 0;
+ int is_pb = 0;
+ uint32_t proc_id = 0;
+ uint32_t sys_id;
+ ram_addr_t low_ram_size;
+ ram_addr_t ram_size = args->ram_size;
+
+ switch (board_type) {
+ case BOARD_EB:
+ break;
+ case BOARD_EB_MPCORE:
+ is_mpcore = 1;
+ break;
+ case BOARD_PB_A8:
+ is_pb = 1;
+ break;
+ case BOARD_PBX_A9:
+ is_mpcore = 1;
+ is_pb = 1;
+ break;
+ }
+ for (n = 0; n < smp_cpus; n++) {
+ cpu = cpu_arm_init(args->cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(cpu);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+ env = &cpu->env;
+ if (arm_feature(env, ARM_FEATURE_V7)) {
+ if (is_mpcore) {
+ proc_id = 0x0c000000;
+ } else {
+ proc_id = 0x0e000000;
+ }
+ } else if (arm_feature(env, ARM_FEATURE_V6K)) {
+ proc_id = 0x06000000;
+ } else if (arm_feature(env, ARM_FEATURE_V6)) {
+ proc_id = 0x04000000;
+ } else {
+ proc_id = 0x02000000;
+ }
+
+ if (is_pb && ram_size > 0x20000000) {
+ /* Core tile RAM. */
+ low_ram_size = ram_size - 0x20000000;
+ ram_size = 0x20000000;
+ memory_region_init_ram(ram_lo, "realview.lowmem", low_ram_size);
+ vmstate_register_ram_global(ram_lo);
+ memory_region_add_subregion(sysmem, 0x20000000, ram_lo);
+ }
+
+ memory_region_init_ram(ram_hi, "realview.highmem", ram_size);
+ vmstate_register_ram_global(ram_hi);
+ low_ram_size = ram_size;
+ if (low_ram_size > 0x10000000)
+ low_ram_size = 0x10000000;
+ /* SDRAM at address zero. */
+ memory_region_init_alias(ram_alias, "realview.alias",
+ ram_hi, 0, low_ram_size);
+ memory_region_add_subregion(sysmem, 0, ram_alias);
+ if (is_pb) {
+ /* And again at a high address. */
+ memory_region_add_subregion(sysmem, 0x70000000, ram_hi);
+ } else {
+ ram_size = low_ram_size;
+ }
+
+ sys_id = is_pb ? 0x01780500 : 0xc1400400;
+ 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(SYS_BUS_DEVICE(sysctl), 0, 0x10000000);
+
+ if (is_mpcore) {
+ hwaddr 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 = SYS_BUS_DEVICE(dev);
+ if (is_pb) {
+ periphbase = 0x1f000000;
+ } else {
+ periphbase = 0x10100000;
+ }
+ 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", 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. */
+ dev = sysbus_create_simple("realview_gic", gic_addr, cpu_irq[0]);
+ }
+ for (n = 0; n < 64; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ pl041 = qdev_create(NULL, "pl041");
+ qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
+ qdev_init_nofail(pl041);
+ sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[19]);
+
+ sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]);
+ sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]);
+
+ sysbus_create_simple("pl011", 0x10009000, pic[12]);
+ sysbus_create_simple("pl011", 0x1000a000, pic[13]);
+ sysbus_create_simple("pl011", 0x1000b000, pic[14]);
+ sysbus_create_simple("pl011", 0x1000c000, pic[15]);
+
+ /* DMA controller is optional, apparently. */
+ sysbus_create_simple("pl081", 0x10030000, pic[24]);
+
+ sysbus_create_simple("sp804", 0x10011000, pic[4]);
+ sysbus_create_simple("sp804", 0x10012000, pic[5]);
+
+ sysbus_create_simple("pl061", 0x10013000, pic[6]);
+ sysbus_create_simple("pl061", 0x10014000, pic[7]);
+ gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]);
+
+ sysbus_create_simple("pl111", 0x10020000, pic[23]);
+
+ dev = sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL);
+ /* Wire up MMC card detect and read-only signals. These have
+ * to go to both the PL061 GPIO and the sysctl register.
+ * Note that the PL181 orders these lines (readonly,inserted)
+ * and the PL061 has them the other way about. Also the card
+ * detect line is inverted.
+ */
+ mmc_irq[0] = qemu_irq_split(
+ qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT),
+ qdev_get_gpio_in(gpio2, 1));
+ mmc_irq[1] = qemu_irq_split(
+ qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN),
+ qemu_irq_invert(qdev_get_gpio_in(gpio2, 0)));
+ qdev_connect_gpio_out(dev, 0, mmc_irq[0]);
+ qdev_connect_gpio_out(dev, 1, mmc_irq[1]);
+
+ sysbus_create_simple("pl031", 0x10017000, pic[10]);
+
+ if (!is_pb) {
+ dev = qdev_create(NULL, "realview_pci");
+ busdev = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(busdev, 0, 0x61000000); /* PCI self-config */
+ sysbus_mmio_map(busdev, 1, 0x62000000); /* PCI config */
+ sysbus_mmio_map(busdev, 2, 0x63000000); /* PCI I/O */
+ sysbus_connect_irq(busdev, 0, pic[48]);
+ sysbus_connect_irq(busdev, 1, pic[49]);
+ sysbus_connect_irq(busdev, 2, pic[50]);
+ sysbus_connect_irq(busdev, 3, pic[51]);
+ pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
+ if (usb_enabled(false)) {
+ pci_create_simple(pci_bus, -1, "pci-ohci");
+ }
+ n = drive_get_max_bus(IF_SCSI);
+ while (n >= 0) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ n--;
+ }
+ }
+ for(n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+
+ if (!done_nic && (!nd->model ||
+ strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0)) {
+ if (is_pb) {
+ lan9118_init(nd, 0x4e000000, pic[28]);
+ } else {
+ smc91c111_init(nd, 0x4e000000, pic[28]);
+ }
+ done_nic = 1;
+ } else {
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
+ }
+ }
+
+ dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL);
+ i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ i2c_create_slave(i2c, "ds1338", 0x68);
+
+ /* Memory map for RealView Emulation Baseboard: */
+ /* 0x10000000 System registers. */
+ /* 0x10001000 System controller. */
+ /* 0x10002000 Two-Wire Serial Bus. */
+ /* 0x10003000 Reserved. */
+ /* 0x10004000 AACI. */
+ /* 0x10005000 MCI. */
+ /* 0x10006000 KMI0. */
+ /* 0x10007000 KMI1. */
+ /* 0x10008000 Character LCD. (EB) */
+ /* 0x10009000 UART0. */
+ /* 0x1000a000 UART1. */
+ /* 0x1000b000 UART2. */
+ /* 0x1000c000 UART3. */
+ /* 0x1000d000 SSPI. */
+ /* 0x1000e000 SCI. */
+ /* 0x1000f000 Reserved. */
+ /* 0x10010000 Watchdog. */
+ /* 0x10011000 Timer 0+1. */
+ /* 0x10012000 Timer 2+3. */
+ /* 0x10013000 GPIO 0. */
+ /* 0x10014000 GPIO 1. */
+ /* 0x10015000 GPIO 2. */
+ /* 0x10002000 Two-Wire Serial Bus - DVI. (PB) */
+ /* 0x10017000 RTC. */
+ /* 0x10018000 DMC. */
+ /* 0x10019000 PCI controller config. */
+ /* 0x10020000 CLCD. */
+ /* 0x10030000 DMA Controller. */
+ /* 0x10040000 GIC1. (EB) */
+ /* 0x10050000 GIC2. (EB) */
+ /* 0x10060000 GIC3. (EB) */
+ /* 0x10070000 GIC4. (EB) */
+ /* 0x10080000 SMC. */
+ /* 0x1e000000 GIC1. (PB) */
+ /* 0x1e001000 GIC2. (PB) */
+ /* 0x1e002000 GIC3. (PB) */
+ /* 0x1e003000 GIC4. (PB) */
+ /* 0x40000000 NOR flash. */
+ /* 0x44000000 DoC flash. */
+ /* 0x48000000 SRAM. */
+ /* 0x4c000000 Configuration flash. */
+ /* 0x4e000000 Ethernet. */
+ /* 0x4f000000 USB. */
+ /* 0x50000000 PISMO. */
+ /* 0x54000000 PISMO. */
+ /* 0x58000000 PISMO. */
+ /* 0x5c000000 PISMO. */
+ /* 0x60000000 PCI. */
+ /* 0x61000000 PCI Self Config. */
+ /* 0x62000000 PCI Config. */
+ /* 0x63000000 PCI IO. */
+ /* 0x64000000 PCI mem 0. */
+ /* 0x68000000 PCI mem 1. */
+ /* 0x6c000000 PCI mem 2. */
+
+ /* ??? 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(ram_hack, "realview.hack", 0x1000);
+ vmstate_register_ram_global(ram_hack);
+ memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack);
+
+ realview_binfo.ram_size = ram_size;
+ realview_binfo.kernel_filename = args->kernel_filename;
+ realview_binfo.kernel_cmdline = args->kernel_cmdline;
+ realview_binfo.initrd_filename = args->initrd_filename;
+ realview_binfo.nb_cpus = smp_cpus;
+ realview_binfo.board_id = realview_board_id[board_type];
+ realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0);
+ arm_load_kernel(arm_env_get_cpu(first_cpu), &realview_binfo);
+}
+
+static void realview_eb_init(QEMUMachineInitArgs *args)
+{
+ if (!args->cpu_model) {
+ args->cpu_model = "arm926";
+ }
+ realview_init(args, BOARD_EB);
+}
+
+static void realview_eb_mpcore_init(QEMUMachineInitArgs *args)
+{
+ if (!args->cpu_model) {
+ args->cpu_model = "arm11mpcore";
+ }
+ realview_init(args, BOARD_EB_MPCORE);
+}
+
+static void realview_pb_a8_init(QEMUMachineInitArgs *args)
+{
+ if (!args->cpu_model) {
+ args->cpu_model = "cortex-a8";
+ }
+ realview_init(args, BOARD_PB_A8);
+}
+
+static void realview_pbx_a9_init(QEMUMachineInitArgs *args)
+{
+ if (!args->cpu_model) {
+ args->cpu_model = "cortex-a9";
+ }
+ realview_init(args, BOARD_PBX_A9);
+}
+
+static QEMUMachine realview_eb_machine = {
+ .name = "realview-eb",
+ .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
+ .init = realview_eb_init,
+ .block_default_type = IF_SCSI,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine realview_eb_mpcore_machine = {
+ .name = "realview-eb-mpcore",
+ .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
+ .init = realview_eb_mpcore_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 4,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine realview_pb_a8_machine = {
+ .name = "realview-pb-a8",
+ .desc = "ARM RealView Platform Baseboard for Cortex-A8",
+ .init = realview_pb_a8_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine realview_pbx_a9_machine = {
+ .name = "realview-pbx-a9",
+ .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
+ .init = realview_pbx_a9_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 4,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void realview_machine_init(void)
+{
+ qemu_register_machine(&realview_eb_machine);
+ qemu_register_machine(&realview_eb_mpcore_machine);
+ qemu_register_machine(&realview_pb_a8_machine);
+ qemu_register_machine(&realview_pbx_a9_machine);
+}
+
+machine_init(realview_machine_init);
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
new file mode 100644
index 0000000..f5832be
--- /dev/null
+++ b/hw/arm/spitz.c
@@ -0,0 +1,1138 @@
+/*
+ * PXA270-based Clamshell PDA platforms.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "hw/arm-misc.h"
+#include "sysemu/sysemu.h"
+#include "hw/pcmcia.h"
+#include "hw/i2c.h"
+#include "hw/ssi.h"
+#include "hw/flash.h"
+#include "qemu/timer.h"
+#include "hw/devices.h"
+#include "hw/sharpsl.h"
+#include "ui/console.h"
+#include "block/block.h"
+#include "audio/audio.h"
+#include "hw/boards.h"
+#include "sysemu/blockdev.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+#undef REG_FMT
+#define REG_FMT "0x%02lx"
+
+/* Spitz Flash */
+#define FLASH_BASE 0x0c000000
+#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */
+#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */
+#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */
+#define FLASH_ECCCNTR 0x0c /* ECC byte counter */
+#define FLASH_ECCCLRR 0x10 /* Clear ECC */
+#define FLASH_FLASHIO 0x14 /* Flash I/O */
+#define FLASH_FLASHCTL 0x18 /* Flash Control */
+
+#define FLASHCTL_CE0 (1 << 0)
+#define FLASHCTL_CLE (1 << 1)
+#define FLASHCTL_ALE (1 << 2)
+#define FLASHCTL_WP (1 << 3)
+#define FLASHCTL_CE1 (1 << 4)
+#define FLASHCTL_RYBY (1 << 5)
+#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1)
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ DeviceState *nand;
+ uint8_t ctl;
+ uint8_t manf_id;
+ uint8_t chip_id;
+ ECCState ecc;
+} SLNANDState;
+
+static uint64_t sl_read(void *opaque, hwaddr addr, unsigned size)
+{
+ SLNANDState *s = (SLNANDState *) opaque;
+ int ryby;
+
+ switch (addr) {
+#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to))
+ case FLASH_ECCLPLB:
+ return BSHR(0, 4, 0) | BSHR(0, 5, 2) | BSHR(0, 6, 4) | BSHR(0, 7, 6) |
+ BSHR(1, 4, 1) | BSHR(1, 5, 3) | BSHR(1, 6, 5) | BSHR(1, 7, 7);
+
+#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to))
+ case FLASH_ECCLPUB:
+ return BSHL(0, 0, 0) | BSHL(0, 1, 2) | BSHL(0, 2, 4) | BSHL(0, 3, 6) |
+ BSHL(1, 0, 1) | BSHL(1, 1, 3) | BSHL(1, 2, 5) | BSHL(1, 3, 7);
+
+ case FLASH_ECCCP:
+ return s->ecc.cp;
+
+ case FLASH_ECCCNTR:
+ return s->ecc.count & 0xff;
+
+ case FLASH_FLASHCTL:
+ nand_getpins(s->nand, &ryby);
+ if (ryby)
+ return s->ctl | FLASHCTL_RYBY;
+ else
+ return s->ctl;
+
+ case FLASH_FLASHIO:
+ if (size == 4) {
+ return ecc_digest(&s->ecc, nand_getio(s->nand)) |
+ (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16);
+ }
+ return ecc_digest(&s->ecc, nand_getio(s->nand));
+
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+ return 0;
+}
+
+static void sl_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ SLNANDState *s = (SLNANDState *) opaque;
+
+ switch (addr) {
+ case FLASH_ECCCLRR:
+ /* Value is ignored. */
+ ecc_reset(&s->ecc);
+ break;
+
+ case FLASH_FLASHCTL:
+ s->ctl = value & 0xff & ~FLASHCTL_RYBY;
+ nand_setpins(s->nand,
+ s->ctl & FLASHCTL_CLE,
+ s->ctl & FLASHCTL_ALE,
+ s->ctl & FLASHCTL_NCE,
+ s->ctl & FLASHCTL_WP,
+ 0);
+ break;
+
+ case FLASH_FLASHIO:
+ nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff));
+ break;
+
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+}
+
+enum {
+ FLASH_128M,
+ FLASH_1024M,
+};
+
+static const MemoryRegionOps sl_ops = {
+ .read = sl_read,
+ .write = sl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void sl_flash_register(PXA2xxState *cpu, int size)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "sl-nand");
+
+ qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG);
+ if (size == FLASH_128M)
+ qdev_prop_set_uint8(dev, "chip_id", 0x73);
+ else if (size == FLASH_1024M)
+ qdev_prop_set_uint8(dev, "chip_id", 0xf1);
+
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE);
+}
+
+static int sl_nand_init(SysBusDevice *dev) {
+ SLNANDState *s;
+ DriveInfo *nand;
+
+ s = FROM_SYSBUS(SLNANDState, dev);
+
+ s->ctl = 0;
+ nand = drive_get(IF_MTD, 0, 0);
+ s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id);
+
+ memory_region_init_io(&s->iomem, &sl_ops, s, "sl", 0x40);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+/* Spitz Keyboard */
+
+#define SPITZ_KEY_STROBE_NUM 11
+#define SPITZ_KEY_SENSE_NUM 7
+
+static const int spitz_gpio_key_sense[SPITZ_KEY_SENSE_NUM] = {
+ 12, 17, 91, 34, 36, 38, 39
+};
+
+static const int spitz_gpio_key_strobe[SPITZ_KEY_STROBE_NUM] = {
+ 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114
+};
+
+/* Eighth additional row maps the special keys */
+static int spitz_keymap[SPITZ_KEY_SENSE_NUM + 1][SPITZ_KEY_STROBE_NUM] = {
+ { 0x1d, 0x02, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0e, 0x3f, 0x40 },
+ { -1 , 0x03, 0x05, 0x13, 0x15, 0x09, 0x17, 0x18, 0x19, 0x41, 0x42 },
+ { 0x0f, 0x10, 0x12, 0x14, 0x22, 0x16, 0x24, 0x25, -1 , -1 , -1 },
+ { 0x3c, 0x11, 0x1f, 0x21, 0x2f, 0x23, 0x32, 0x26, -1 , 0x36, -1 },
+ { 0x3b, 0x1e, 0x20, 0x2e, 0x30, 0x31, 0x34, -1 , 0x1c, 0x2a, -1 },
+ { 0x44, 0x2c, 0x2d, 0x0c, 0x39, 0x33, -1 , 0x48, -1 , -1 , 0x38 },
+ { 0x37, 0x3d, -1 , 0x45, 0x57, 0x58, 0x4b, 0x50, 0x4d, -1 , -1 },
+ { 0x52, 0x43, 0x01, 0x47, 0x49, -1 , -1 , -1 , -1 , -1 , -1 },
+};
+
+#define SPITZ_GPIO_AK_INT 13 /* Remote control */
+#define SPITZ_GPIO_SYNC 16 /* Sync button */
+#define SPITZ_GPIO_ON_KEY 95 /* Power button */
+#define SPITZ_GPIO_SWA 97 /* Lid */
+#define SPITZ_GPIO_SWB 96 /* Tablet mode */
+
+/* The special buttons are mapped to unused keys */
+static const int spitz_gpiomap[5] = {
+ SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY,
+ SPITZ_GPIO_SWA, SPITZ_GPIO_SWB,
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq sense[SPITZ_KEY_SENSE_NUM];
+ qemu_irq gpiomap[5];
+ int keymap[0x80];
+ uint16_t keyrow[SPITZ_KEY_SENSE_NUM];
+ uint16_t strobe_state;
+ uint16_t sense_state;
+
+ uint16_t pre_map[0x100];
+ uint16_t modifiers;
+ uint16_t imodifiers;
+ uint8_t fifo[16];
+ int fifopos, fifolen;
+ QEMUTimer *kbdtimer;
+} SpitzKeyboardState;
+
+static void spitz_keyboard_sense_update(SpitzKeyboardState *s)
+{
+ int i;
+ uint16_t strobe, sense = 0;
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) {
+ strobe = s->keyrow[i] & s->strobe_state;
+ if (strobe) {
+ sense |= 1 << i;
+ if (!(s->sense_state & (1 << i)))
+ qemu_irq_raise(s->sense[i]);
+ } else if (s->sense_state & (1 << i))
+ qemu_irq_lower(s->sense[i]);
+ }
+
+ s->sense_state = sense;
+}
+
+static void spitz_keyboard_strobe(void *opaque, int line, int level)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ if (level)
+ s->strobe_state |= 1 << line;
+ else
+ s->strobe_state &= ~(1 << line);
+ spitz_keyboard_sense_update(s);
+}
+
+static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode)
+{
+ int spitz_keycode = s->keymap[keycode & 0x7f];
+ if (spitz_keycode == -1)
+ return;
+
+ /* Handle the additional keys */
+ if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) {
+ qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80));
+ return;
+ }
+
+ if (keycode & 0x80)
+ s->keyrow[spitz_keycode >> 4] &= ~(1 << (spitz_keycode & 0xf));
+ else
+ s->keyrow[spitz_keycode >> 4] |= 1 << (spitz_keycode & 0xf);
+
+ spitz_keyboard_sense_update(s);
+}
+
+#define SHIFT (1 << 7)
+#define CTRL (1 << 8)
+#define FN (1 << 9)
+
+#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c
+
+static void spitz_keyboard_handler(void *opaque, int keycode)
+{
+ SpitzKeyboardState *s = opaque;
+ uint16_t code;
+ int mapcode;
+ switch (keycode) {
+ case 0x2a: /* Left Shift */
+ s->modifiers |= 1;
+ break;
+ case 0xaa:
+ s->modifiers &= ~1;
+ break;
+ case 0x36: /* Right Shift */
+ s->modifiers |= 2;
+ break;
+ case 0xb6:
+ s->modifiers &= ~2;
+ break;
+ case 0x1d: /* Control */
+ s->modifiers |= 4;
+ break;
+ case 0x9d:
+ s->modifiers &= ~4;
+ break;
+ case 0x38: /* Alt */
+ s->modifiers |= 8;
+ break;
+ case 0xb8:
+ s->modifiers &= ~8;
+ break;
+ }
+
+ code = s->pre_map[mapcode = ((s->modifiers & 3) ?
+ (keycode | SHIFT) :
+ (keycode & ~SHIFT))];
+
+ if (code != mapcode) {
+#if 0
+ if ((code & SHIFT) && !(s->modifiers & 1))
+ QUEUE_KEY(0x2a | (keycode & 0x80));
+ if ((code & CTRL ) && !(s->modifiers & 4))
+ QUEUE_KEY(0x1d | (keycode & 0x80));
+ if ((code & FN ) && !(s->modifiers & 8))
+ QUEUE_KEY(0x38 | (keycode & 0x80));
+ if ((code & FN ) && (s->modifiers & 1))
+ QUEUE_KEY(0x2a | (~keycode & 0x80));
+ if ((code & FN ) && (s->modifiers & 2))
+ QUEUE_KEY(0x36 | (~keycode & 0x80));
+#else
+ if (keycode & 0x80) {
+ if ((s->imodifiers & 1 ) && !(s->modifiers & 1))
+ QUEUE_KEY(0x2a | 0x80);
+ if ((s->imodifiers & 4 ) && !(s->modifiers & 4))
+ QUEUE_KEY(0x1d | 0x80);
+ if ((s->imodifiers & 8 ) && !(s->modifiers & 8))
+ QUEUE_KEY(0x38 | 0x80);
+ if ((s->imodifiers & 0x10) && (s->modifiers & 1))
+ QUEUE_KEY(0x2a);
+ if ((s->imodifiers & 0x20) && (s->modifiers & 2))
+ QUEUE_KEY(0x36);
+ s->imodifiers = 0;
+ } else {
+ if ((code & SHIFT) && !((s->modifiers | s->imodifiers) & 1)) {
+ QUEUE_KEY(0x2a);
+ s->imodifiers |= 1;
+ }
+ if ((code & CTRL ) && !((s->modifiers | s->imodifiers) & 4)) {
+ QUEUE_KEY(0x1d);
+ s->imodifiers |= 4;
+ }
+ if ((code & FN ) && !((s->modifiers | s->imodifiers) & 8)) {
+ QUEUE_KEY(0x38);
+ s->imodifiers |= 8;
+ }
+ if ((code & FN ) && (s->modifiers & 1) &&
+ !(s->imodifiers & 0x10)) {
+ QUEUE_KEY(0x2a | 0x80);
+ s->imodifiers |= 0x10;
+ }
+ if ((code & FN ) && (s->modifiers & 2) &&
+ !(s->imodifiers & 0x20)) {
+ QUEUE_KEY(0x36 | 0x80);
+ s->imodifiers |= 0x20;
+ }
+ }
+#endif
+ }
+
+ QUEUE_KEY((code & 0x7f) | (keycode & 0x80));
+}
+
+static void spitz_keyboard_tick(void *opaque)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ if (s->fifolen) {
+ spitz_keyboard_keydown(s, s->fifo[s->fifopos ++]);
+ s->fifolen --;
+ if (s->fifopos >= 16)
+ s->fifopos = 0;
+ }
+
+ qemu_mod_timer(s->kbdtimer, qemu_get_clock_ns(vm_clock) +
+ get_ticks_per_sec() / 32);
+}
+
+static void spitz_keyboard_pre_map(SpitzKeyboardState *s)
+{
+ int i;
+ for (i = 0; i < 0x100; i ++)
+ s->pre_map[i] = i;
+ s->pre_map[0x02 | SHIFT ] = 0x02 | SHIFT; /* exclam */
+ s->pre_map[0x28 | SHIFT ] = 0x03 | SHIFT; /* quotedbl */
+ s->pre_map[0x04 | SHIFT ] = 0x04 | SHIFT; /* numbersign */
+ s->pre_map[0x05 | SHIFT ] = 0x05 | SHIFT; /* dollar */
+ s->pre_map[0x06 | SHIFT ] = 0x06 | SHIFT; /* percent */
+ s->pre_map[0x08 | SHIFT ] = 0x07 | SHIFT; /* ampersand */
+ s->pre_map[0x28 ] = 0x08 | SHIFT; /* apostrophe */
+ s->pre_map[0x0a | SHIFT ] = 0x09 | SHIFT; /* parenleft */
+ s->pre_map[0x0b | SHIFT ] = 0x0a | SHIFT; /* parenright */
+ s->pre_map[0x29 | SHIFT ] = 0x0b | SHIFT; /* asciitilde */
+ s->pre_map[0x03 | SHIFT ] = 0x0c | SHIFT; /* at */
+ s->pre_map[0xd3 ] = 0x0e | FN; /* Delete */
+ s->pre_map[0x3a ] = 0x0f | FN; /* Caps_Lock */
+ s->pre_map[0x07 | SHIFT ] = 0x11 | FN; /* asciicircum */
+ s->pre_map[0x0d ] = 0x12 | FN; /* equal */
+ s->pre_map[0x0d | SHIFT ] = 0x13 | FN; /* plus */
+ s->pre_map[0x1a ] = 0x14 | FN; /* bracketleft */
+ s->pre_map[0x1b ] = 0x15 | FN; /* bracketright */
+ s->pre_map[0x1a | SHIFT ] = 0x16 | FN; /* braceleft */
+ s->pre_map[0x1b | SHIFT ] = 0x17 | FN; /* braceright */
+ s->pre_map[0x27 ] = 0x22 | FN; /* semicolon */
+ s->pre_map[0x27 | SHIFT ] = 0x23 | FN; /* colon */
+ s->pre_map[0x09 | SHIFT ] = 0x24 | FN; /* asterisk */
+ s->pre_map[0x2b ] = 0x25 | FN; /* backslash */
+ s->pre_map[0x2b | SHIFT ] = 0x26 | FN; /* bar */
+ s->pre_map[0x0c | SHIFT ] = 0x30 | FN; /* underscore */
+ s->pre_map[0x33 | SHIFT ] = 0x33 | FN; /* less */
+ s->pre_map[0x35 ] = 0x33 | SHIFT; /* slash */
+ s->pre_map[0x34 | SHIFT ] = 0x34 | FN; /* greater */
+ s->pre_map[0x35 | SHIFT ] = 0x34 | SHIFT; /* question */
+ s->pre_map[0x49 ] = 0x48 | FN; /* Page_Up */
+ s->pre_map[0x51 ] = 0x50 | FN; /* Page_Down */
+
+ s->modifiers = 0;
+ s->imodifiers = 0;
+ s->fifopos = 0;
+ s->fifolen = 0;
+}
+
+#undef SHIFT
+#undef CTRL
+#undef FN
+
+static int spitz_keyboard_post_load(void *opaque, int version_id)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ /* Release all pressed keys */
+ memset(s->keyrow, 0, sizeof(s->keyrow));
+ spitz_keyboard_sense_update(s);
+ s->modifiers = 0;
+ s->imodifiers = 0;
+ s->fifopos = 0;
+ s->fifolen = 0;
+
+ return 0;
+}
+
+static void spitz_keyboard_register(PXA2xxState *cpu)
+{
+ int i;
+ DeviceState *dev;
+ SpitzKeyboardState *s;
+
+ dev = sysbus_create_simple("spitz-keyboard", -1, NULL);
+ s = FROM_SYSBUS(SpitzKeyboardState, SYS_BUS_DEVICE(dev));
+
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++)
+ qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i]));
+
+ for (i = 0; i < 5; i ++)
+ s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]);
+
+ if (!graphic_rotate)
+ s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]);
+
+ for (i = 0; i < 5; i++)
+ qemu_set_irq(s->gpiomap[i], 0);
+
+ for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++)
+ qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i],
+ qdev_get_gpio_in(dev, i));
+
+ qemu_mod_timer(s->kbdtimer, qemu_get_clock_ns(vm_clock));
+
+ qemu_add_kbd_event_handler(spitz_keyboard_handler, s);
+}
+
+static int spitz_keyboard_init(SysBusDevice *dev)
+{
+ SpitzKeyboardState *s;
+ int i, j;
+
+ s = FROM_SYSBUS(SpitzKeyboardState, dev);
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM + 1; i ++)
+ for (j = 0; j < SPITZ_KEY_STROBE_NUM; j ++)
+ if (spitz_keymap[i][j] != -1)
+ s->keymap[spitz_keymap[i][j]] = (i << 4) | j;
+
+ spitz_keyboard_pre_map(s);
+
+ s->kbdtimer = qemu_new_timer_ns(vm_clock, spitz_keyboard_tick, s);
+ qdev_init_gpio_in(&dev->qdev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM);
+ qdev_init_gpio_out(&dev->qdev, s->sense, SPITZ_KEY_SENSE_NUM);
+
+ return 0;
+}
+
+/* LCD backlight controller */
+
+#define LCDTG_RESCTL 0x00
+#define LCDTG_PHACTRL 0x01
+#define LCDTG_DUTYCTRL 0x02
+#define LCDTG_POWERREG0 0x03
+#define LCDTG_POWERREG1 0x04
+#define LCDTG_GPOR3 0x05
+#define LCDTG_PICTRL 0x06
+#define LCDTG_POLCTRL 0x07
+
+typedef struct {
+ SSISlave ssidev;
+ uint32_t bl_intensity;
+ uint32_t bl_power;
+} SpitzLCDTG;
+
+static void spitz_bl_update(SpitzLCDTG *s)
+{
+ if (s->bl_power && s->bl_intensity)
+ zaurus_printf("LCD Backlight now at %i/63\n", s->bl_intensity);
+ else
+ zaurus_printf("LCD Backlight now off\n");
+}
+
+/* FIXME: Implement GPIO properly and remove this hack. */
+static SpitzLCDTG *spitz_lcdtg;
+
+static inline void spitz_bl_bit5(void *opaque, int line, int level)
+{
+ SpitzLCDTG *s = spitz_lcdtg;
+ int prev = s->bl_intensity;
+
+ if (level)
+ s->bl_intensity &= ~0x20;
+ else
+ s->bl_intensity |= 0x20;
+
+ if (s->bl_power && prev != s->bl_intensity)
+ spitz_bl_update(s);
+}
+
+static inline void spitz_bl_power(void *opaque, int line, int level)
+{
+ SpitzLCDTG *s = spitz_lcdtg;
+ s->bl_power = !!level;
+ spitz_bl_update(s);
+}
+
+static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value)
+{
+ SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
+ int addr;
+ addr = value >> 5;
+ value &= 0x1f;
+
+ switch (addr) {
+ case LCDTG_RESCTL:
+ if (value)
+ zaurus_printf("LCD in QVGA mode\n");
+ else
+ zaurus_printf("LCD in VGA mode\n");
+ break;
+
+ case LCDTG_DUTYCTRL:
+ s->bl_intensity &= ~0x1f;
+ s->bl_intensity |= value;
+ if (s->bl_power)
+ spitz_bl_update(s);
+ break;
+
+ case LCDTG_POWERREG0:
+ /* Set common voltage to M62332FP */
+ break;
+ }
+ return 0;
+}
+
+static int spitz_lcdtg_init(SSISlave *dev)
+{
+ SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
+
+ spitz_lcdtg = s;
+ s->bl_power = 0;
+ s->bl_intensity = 0x20;
+
+ return 0;
+}
+
+/* SSP devices */
+
+#define CORGI_SSP_PORT 2
+
+#define SPITZ_GPIO_LCDCON_CS 53
+#define SPITZ_GPIO_ADS7846_CS 14
+#define SPITZ_GPIO_MAX1111_CS 20
+#define SPITZ_GPIO_TP_INT 11
+
+static DeviceState *max1111;
+
+/* "Demux" the signal based on current chipselect */
+typedef struct {
+ SSISlave ssidev;
+ SSIBus *bus[3];
+ uint32_t enable[3];
+} CorgiSSPState;
+
+static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value)
+{
+ CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev);
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (s->enable[i]) {
+ return ssi_transfer(s->bus[i], value);
+ }
+ }
+ return 0;
+}
+
+static void corgi_ssp_gpio_cs(void *opaque, int line, int level)
+{
+ CorgiSSPState *s = (CorgiSSPState *)opaque;
+ assert(line >= 0 && line < 3);
+ s->enable[line] = !level;
+}
+
+#define MAX1111_BATT_VOLT 1
+#define MAX1111_BATT_TEMP 2
+#define MAX1111_ACIN_VOLT 3
+
+#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */
+#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */
+#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */
+
+static void spitz_adc_temp_on(void *opaque, int line, int level)
+{
+ if (!max1111)
+ return;
+
+ if (level)
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, SPITZ_BATTERY_TEMP);
+ else
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
+}
+
+static int corgi_ssp_init(SSISlave *dev)
+{
+ CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev);
+
+ qdev_init_gpio_in(&dev->qdev, corgi_ssp_gpio_cs, 3);
+ s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0");
+ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1");
+ s->bus[2] = ssi_create_bus(&dev->qdev, "ssi2");
+
+ return 0;
+}
+
+static void spitz_ssp_attach(PXA2xxState *cpu)
+{
+ DeviceState *mux;
+ DeviceState *dev;
+ void *bus;
+
+ mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp");
+
+ bus = qdev_get_child_bus(mux, "ssi0");
+ ssi_create_slave(bus, "spitz-lcdtg");
+
+ bus = qdev_get_child_bus(mux, "ssi1");
+ dev = ssi_create_slave(bus, "ads7846");
+ qdev_connect_gpio_out(dev, 0,
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT));
+
+ bus = qdev_get_child_bus(mux, "ssi2");
+ max1111 = ssi_create_slave(bus, "max1111");
+ max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT);
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
+ max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN);
+
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS,
+ qdev_get_gpio_in(mux, 0));
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS,
+ qdev_get_gpio_in(mux, 1));
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS,
+ qdev_get_gpio_in(mux, 2));
+}
+
+/* CF Microdrive */
+
+static void spitz_microdrive_attach(PXA2xxState *cpu, int slot)
+{
+ PCMCIACardState *md;
+ DriveInfo *dinfo;
+
+ dinfo = drive_get(IF_IDE, 0, 0);
+ if (!dinfo || dinfo->media_cd)
+ return;
+ md = dscm1xxxx_init(dinfo);
+ pxa2xx_pcmcia_attach(cpu->pcmcia[slot], md);
+}
+
+/* Wm8750 and Max7310 on I2C */
+
+#define AKITA_MAX_ADDR 0x18
+#define SPITZ_WM_ADDRL 0x1b
+#define SPITZ_WM_ADDRH 0x1a
+
+#define SPITZ_GPIO_WM 5
+
+static void spitz_wm8750_addr(void *opaque, int line, int level)
+{
+ I2CSlave *wm = (I2CSlave *) opaque;
+ if (level)
+ i2c_set_slave_address(wm, SPITZ_WM_ADDRH);
+ else
+ i2c_set_slave_address(wm, SPITZ_WM_ADDRL);
+}
+
+static void spitz_i2c_setup(PXA2xxState *cpu)
+{
+ /* Attach the CPU on one end of our I2C bus. */
+ i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
+
+ DeviceState *wm;
+
+ /* Attach a WM8750 to the bus */
+ wm = i2c_create_slave(bus, "wm8750", 0);
+
+ spitz_wm8750_addr(wm, 0, 0);
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM,
+ qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]);
+ /* .. and to the sound interface. */
+ cpu->i2s->opaque = wm;
+ cpu->i2s->codec_out = wm8750_dac_dat;
+ cpu->i2s->codec_in = wm8750_adc_dat;
+ wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
+}
+
+static void spitz_akita_i2c_setup(PXA2xxState *cpu)
+{
+ /* Attach a Max7310 to Akita I2C bus. */
+ i2c_create_slave(pxa2xx_i2c_bus(cpu->i2c[0]), "max7310",
+ AKITA_MAX_ADDR);
+}
+
+/* Other peripherals */
+
+static void spitz_out_switch(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ zaurus_printf("Charging %s.\n", level ? "off" : "on");
+ break;
+ case 1:
+ zaurus_printf("Discharging %s.\n", level ? "on" : "off");
+ break;
+ case 2:
+ zaurus_printf("Green LED %s.\n", level ? "on" : "off");
+ break;
+ case 3:
+ zaurus_printf("Orange LED %s.\n", level ? "on" : "off");
+ break;
+ case 4:
+ spitz_bl_bit5(opaque, line, level);
+ break;
+ case 5:
+ spitz_bl_power(opaque, line, level);
+ break;
+ case 6:
+ spitz_adc_temp_on(opaque, line, level);
+ break;
+ }
+}
+
+#define SPITZ_SCP_LED_GREEN 1
+#define SPITZ_SCP_JK_B 2
+#define SPITZ_SCP_CHRG_ON 3
+#define SPITZ_SCP_MUTE_L 4
+#define SPITZ_SCP_MUTE_R 5
+#define SPITZ_SCP_CF_POWER 6
+#define SPITZ_SCP_LED_ORANGE 7
+#define SPITZ_SCP_JK_A 8
+#define SPITZ_SCP_ADC_TEMP_ON 9
+#define SPITZ_SCP2_IR_ON 1
+#define SPITZ_SCP2_AKIN_PULLUP 2
+#define SPITZ_SCP2_BACKLIGHT_CONT 7
+#define SPITZ_SCP2_BACKLIGHT_ON 8
+#define SPITZ_SCP2_MIC_BIAS 9
+
+static void spitz_scoop_gpio_setup(PXA2xxState *cpu,
+ DeviceState *scp0, DeviceState *scp1)
+{
+ qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8);
+
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]);
+
+ if (scp1) {
+ qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]);
+ qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]);
+ }
+
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]);
+}
+
+#define SPITZ_GPIO_HSYNC 22
+#define SPITZ_GPIO_SD_DETECT 9
+#define SPITZ_GPIO_SD_WP 81
+#define SPITZ_GPIO_ON_RESET 89
+#define SPITZ_GPIO_BAT_COVER 90
+#define SPITZ_GPIO_CF1_IRQ 105
+#define SPITZ_GPIO_CF1_CD 94
+#define SPITZ_GPIO_CF2_IRQ 106
+#define SPITZ_GPIO_CF2_CD 93
+
+static int spitz_hsync;
+
+static void spitz_lcd_hsync_handler(void *opaque, int line, int level)
+{
+ PXA2xxState *cpu = (PXA2xxState *) opaque;
+ qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync);
+ spitz_hsync ^= 1;
+}
+
+static void spitz_gpio_setup(PXA2xxState *cpu, int slots)
+{
+ qemu_irq lcd_hsync;
+ /*
+ * Bad hack: We toggle the LCD hsync GPIO on every GPIO status
+ * read to satisfy broken guests that poll-wait for hsync.
+ * Simulating a real hsync event would be less practical and
+ * wouldn't guarantee that a guest ever exits the loop.
+ */
+ spitz_hsync = 0;
+ lcd_hsync = qemu_allocate_irqs(spitz_lcd_hsync_handler, cpu, 1)[0];
+ pxa2xx_gpio_read_notifier(cpu->gpio, lcd_hsync);
+ pxa2xx_lcd_vsync_notifier(cpu->lcd, lcd_hsync);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc,
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT));
+
+ /* Battery lock always closed */
+ qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER));
+
+ /* Handle reset */
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset);
+
+ /* PCMCIA signals: card's IRQ and Card-Detect */
+ if (slots >= 1)
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0],
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD));
+ if (slots >= 2)
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1],
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD));
+}
+
+/* Board init. */
+enum spitz_model_e { spitz, akita, borzoi, terrier };
+
+#define SPITZ_RAM 0x04000000
+#define SPITZ_ROM 0x00800000
+
+static struct arm_boot_info spitz_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void spitz_common_init(QEMUMachineInitArgs *args,
+ enum spitz_model_e model, int arm_id)
+{
+ PXA2xxState *mpu;
+ DeviceState *scp0, *scp1 = NULL;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *rom = g_new(MemoryRegion, 1);
+ const char *cpu_model = args->cpu_model;
+
+ if (!cpu_model)
+ cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0";
+
+ /* Setup CPU & memory */
+ mpu = pxa270_init(address_space_mem, spitz_binfo.ram_size, cpu_model);
+
+ sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M);
+
+ memory_region_init_ram(rom, "spitz.rom", SPITZ_ROM);
+ vmstate_register_ram_global(rom);
+ memory_region_set_readonly(rom, true);
+ memory_region_add_subregion(address_space_mem, 0, rom);
+
+ /* Setup peripherals */
+ spitz_keyboard_register(mpu);
+
+ spitz_ssp_attach(mpu);
+
+ scp0 = sysbus_create_simple("scoop", 0x10800000, NULL);
+ if (model != akita) {
+ scp1 = sysbus_create_simple("scoop", 0x08800040, NULL);
+ }
+
+ spitz_scoop_gpio_setup(mpu, scp0, scp1);
+
+ spitz_gpio_setup(mpu, (model == akita) ? 1 : 2);
+
+ spitz_i2c_setup(mpu);
+
+ if (model == akita)
+ spitz_akita_i2c_setup(mpu);
+
+ if (model == terrier)
+ /* A 6.0 GB microdrive is permanently sitting in CF slot 1. */
+ spitz_microdrive_attach(mpu, 1);
+ else if (model != akita)
+ /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */
+ spitz_microdrive_attach(mpu, 0);
+
+ spitz_binfo.kernel_filename = args->kernel_filename;
+ spitz_binfo.kernel_cmdline = args->kernel_cmdline;
+ spitz_binfo.initrd_filename = args->initrd_filename;
+ spitz_binfo.board_id = arm_id;
+ arm_load_kernel(mpu->cpu, &spitz_binfo);
+ sl_bootparam_write(SL_PXA_PARAM_BASE);
+}
+
+static void spitz_init(QEMUMachineInitArgs *args)
+{
+ spitz_common_init(args, spitz, 0x2c9);
+}
+
+static void borzoi_init(QEMUMachineInitArgs *args)
+{
+ spitz_common_init(args, borzoi, 0x33f);
+}
+
+static void akita_init(QEMUMachineInitArgs *args)
+{
+ spitz_common_init(args, akita, 0x2e8);
+}
+
+static void terrier_init(QEMUMachineInitArgs *args)
+{
+ spitz_common_init(args, terrier, 0x33f);
+}
+
+static QEMUMachine akitapda_machine = {
+ .name = "akita",
+ .desc = "Akita PDA (PXA270)",
+ .init = akita_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine spitzpda_machine = {
+ .name = "spitz",
+ .desc = "Spitz PDA (PXA270)",
+ .init = spitz_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine borzoipda_machine = {
+ .name = "borzoi",
+ .desc = "Borzoi PDA (PXA270)",
+ .init = borzoi_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine terrierpda_machine = {
+ .name = "terrier",
+ .desc = "Terrier PDA (PXA270)",
+ .init = terrier_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void spitz_machine_init(void)
+{
+ qemu_register_machine(&akitapda_machine);
+ qemu_register_machine(&spitzpda_machine);
+ qemu_register_machine(&borzoipda_machine);
+ qemu_register_machine(&terrierpda_machine);
+}
+
+machine_init(spitz_machine_init);
+
+static bool is_version_0(void *opaque, int version_id)
+{
+ return version_id == 0;
+}
+
+static VMStateDescription vmstate_sl_nand_info = {
+ .name = "sl-nand",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(ctl, SLNANDState),
+ VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static Property sl_nand_properties[] = {
+ DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG),
+ DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sl_nand_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = sl_nand_init;
+ dc->vmsd = &vmstate_sl_nand_info;
+ dc->props = sl_nand_properties;
+}
+
+static const TypeInfo sl_nand_info = {
+ .name = "sl-nand",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SLNANDState),
+ .class_init = sl_nand_class_init,
+};
+
+static VMStateDescription vmstate_spitz_kbd = {
+ .name = "spitz-keyboard",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = spitz_keyboard_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(sense_state, SpitzKeyboardState),
+ VMSTATE_UINT16(strobe_state, SpitzKeyboardState),
+ VMSTATE_UNUSED_TEST(is_version_0, 5),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static Property spitz_keyboard_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spitz_keyboard_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = spitz_keyboard_init;
+ dc->vmsd = &vmstate_spitz_kbd;
+ dc->props = spitz_keyboard_properties;
+}
+
+static const TypeInfo spitz_keyboard_info = {
+ .name = "spitz-keyboard",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpitzKeyboardState),
+ .class_init = spitz_keyboard_class_init,
+};
+
+static const VMStateDescription vmstate_corgi_ssp_regs = {
+ .name = "corgi-ssp",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, CorgiSSPState),
+ VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void corgi_ssp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = corgi_ssp_init;
+ k->transfer = corgi_ssp_transfer;
+ dc->vmsd = &vmstate_corgi_ssp_regs;
+}
+
+static const TypeInfo corgi_ssp_info = {
+ .name = "corgi-ssp",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(CorgiSSPState),
+ .class_init = corgi_ssp_class_init,
+};
+
+static const VMStateDescription vmstate_spitz_lcdtg_regs = {
+ .name = "spitz-lcdtg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, SpitzLCDTG),
+ VMSTATE_UINT32(bl_intensity, SpitzLCDTG),
+ VMSTATE_UINT32(bl_power, SpitzLCDTG),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void spitz_lcdtg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = spitz_lcdtg_init;
+ k->transfer = spitz_lcdtg_transfer;
+ dc->vmsd = &vmstate_spitz_lcdtg_regs;
+}
+
+static const TypeInfo spitz_lcdtg_info = {
+ .name = "spitz-lcdtg",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(SpitzLCDTG),
+ .class_init = spitz_lcdtg_class_init,
+};
+
+static void spitz_register_types(void)
+{
+ type_register_static(&corgi_ssp_info);
+ type_register_static(&spitz_lcdtg_info);
+ type_register_static(&spitz_keyboard_info);
+ type_register_static(&sl_nand_info);
+}
+
+type_init(spitz_register_types)
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
new file mode 100644
index 0000000..f4ce794
--- /dev/null
+++ b/hw/arm/stellaris.c
@@ -0,0 +1,1401 @@
+/*
+ * Luminary Micro Stellaris peripherals
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/ssi.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "qemu/timer.h"
+#include "hw/i2c.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+
+#define GPIO_A 0
+#define GPIO_B 1
+#define GPIO_C 2
+#define GPIO_D 3
+#define GPIO_E 4
+#define GPIO_F 5
+#define GPIO_G 6
+
+#define BP_OLED_I2C 0x01
+#define BP_OLED_SSI 0x02
+#define BP_GAMEPAD 0x04
+
+typedef const struct {
+ const char *name;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc0;
+ uint32_t dc1;
+ uint32_t dc2;
+ uint32_t dc3;
+ uint32_t dc4;
+ uint32_t peripherals;
+} stellaris_board_info;
+
+/* General purpose timer module. */
+
+typedef struct gptm_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t config;
+ uint32_t mode[2];
+ uint32_t control;
+ uint32_t state;
+ uint32_t mask;
+ uint32_t load[2];
+ uint32_t match[2];
+ uint32_t prescale[2];
+ uint32_t match_prescale[2];
+ uint32_t rtc;
+ int64_t tick[2];
+ struct gptm_state *opaque[2];
+ QEMUTimer *timer[2];
+ /* The timers have an alternate output used to trigger the ADC. */
+ qemu_irq trigger;
+ qemu_irq irq;
+} gptm_state;
+
+static void gptm_update_irq(gptm_state *s)
+{
+ int level;
+ level = (s->state & s->mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void gptm_stop(gptm_state *s, int n)
+{
+ qemu_del_timer(s->timer[n]);
+}
+
+static void gptm_reload(gptm_state *s, int n, int reset)
+{
+ int64_t tick;
+ if (reset)
+ tick = qemu_get_clock_ns(vm_clock);
+ else
+ tick = s->tick[n];
+
+ if (s->config == 0) {
+ /* 32-bit CountDown. */
+ uint32_t count;
+ count = s->load[0] | (s->load[1] << 16);
+ tick += (int64_t)count * system_clock_scale;
+ } else if (s->config == 1) {
+ /* 32-bit RTC. 1Hz tick. */
+ tick += get_ticks_per_sec();
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
+ }
+ s->tick[n] = tick;
+ qemu_mod_timer(s->timer[n], tick);
+}
+
+static void gptm_tick(void *opaque)
+{
+ gptm_state **p = (gptm_state **)opaque;
+ gptm_state *s;
+ int n;
+
+ s = *p;
+ n = p - s->opaque;
+ if (s->config == 0) {
+ s->state |= 1;
+ if ((s->control & 0x20)) {
+ /* Output trigger. */
+ qemu_irq_pulse(s->trigger);
+ }
+ if (s->mode[0] & 1) {
+ /* One-shot. */
+ s->control &= ~1;
+ } else {
+ /* Periodic. */
+ gptm_reload(s, 0, 0);
+ }
+ } else if (s->config == 1) {
+ /* RTC. */
+ uint32_t match;
+ s->rtc++;
+ match = s->match[0] | (s->match[1] << 16);
+ if (s->rtc > match)
+ s->rtc = 0;
+ if (s->rtc == 0) {
+ s->state |= 8;
+ }
+ gptm_reload(s, 0, 0);
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
+ }
+ gptm_update_irq(s);
+}
+
+static uint64_t gptm_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* CFG */
+ return s->config;
+ case 0x04: /* TAMR */
+ return s->mode[0];
+ case 0x08: /* TBMR */
+ return s->mode[1];
+ case 0x0c: /* CTL */
+ return s->control;
+ case 0x18: /* IMR */
+ return s->mask;
+ case 0x1c: /* RIS */
+ return s->state;
+ case 0x20: /* MIS */
+ return s->state & s->mask;
+ case 0x24: /* CR */
+ return 0;
+ case 0x28: /* TAILR */
+ return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
+ case 0x2c: /* TBILR */
+ return s->load[1];
+ case 0x30: /* TAMARCHR */
+ return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
+ case 0x34: /* TBMATCHR */
+ return s->match[1];
+ case 0x38: /* TAPR */
+ return s->prescale[0];
+ case 0x3c: /* TBPR */
+ return s->prescale[1];
+ case 0x40: /* TAPMR */
+ return s->match_prescale[0];
+ case 0x44: /* TBPMR */
+ return s->match_prescale[1];
+ case 0x48: /* TAR */
+ if (s->control == 1)
+ return s->rtc;
+ case 0x4c: /* TBR */
+ hw_error("TODO: Timer value read\n");
+ default:
+ hw_error("gptm_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gptm_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ gptm_state *s = (gptm_state *)opaque;
+ uint32_t oldval;
+
+ /* The timers should be disabled before changing the configuration.
+ We take advantage of this and defer everything until the timer
+ is enabled. */
+ switch (offset) {
+ case 0x00: /* CFG */
+ s->config = value;
+ break;
+ case 0x04: /* TAMR */
+ s->mode[0] = value;
+ break;
+ case 0x08: /* TBMR */
+ s->mode[1] = value;
+ break;
+ case 0x0c: /* CTL */
+ oldval = s->control;
+ s->control = value;
+ /* TODO: Implement pause. */
+ if ((oldval ^ value) & 1) {
+ if (value & 1) {
+ gptm_reload(s, 0, 1);
+ } else {
+ gptm_stop(s, 0);
+ }
+ }
+ if (((oldval ^ value) & 0x100) && s->config >= 4) {
+ if (value & 0x100) {
+ gptm_reload(s, 1, 1);
+ } else {
+ gptm_stop(s, 1);
+ }
+ }
+ break;
+ case 0x18: /* IMR */
+ s->mask = value & 0x77;
+ gptm_update_irq(s);
+ break;
+ case 0x24: /* CR */
+ s->state &= ~value;
+ break;
+ case 0x28: /* TAILR */
+ s->load[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->load[1] = value >> 16;
+ }
+ break;
+ case 0x2c: /* TBILR */
+ s->load[1] = value & 0xffff;
+ break;
+ case 0x30: /* TAMARCHR */
+ s->match[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->match[1] = value >> 16;
+ }
+ break;
+ case 0x34: /* TBMATCHR */
+ s->match[1] = value >> 16;
+ break;
+ case 0x38: /* TAPR */
+ s->prescale[0] = value;
+ break;
+ case 0x3c: /* TBPR */
+ s->prescale[1] = value;
+ break;
+ case 0x40: /* TAPMR */
+ s->match_prescale[0] = value;
+ break;
+ case 0x44: /* TBPMR */
+ s->match_prescale[0] = value;
+ break;
+ default:
+ hw_error("gptm_write: Bad offset 0x%x\n", (int)offset);
+ }
+ gptm_update_irq(s);
+}
+
+static const MemoryRegionOps gptm_ops = {
+ .read = gptm_read,
+ .write = gptm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_stellaris_gptm = {
+ .name = "stellaris_gptm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(config, gptm_state),
+ VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
+ VMSTATE_UINT32(control, gptm_state),
+ VMSTATE_UINT32(state, gptm_state),
+ VMSTATE_UINT32(mask, gptm_state),
+ VMSTATE_UNUSED(8),
+ VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
+ VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
+ VMSTATE_UINT32(rtc, gptm_state),
+ VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
+ VMSTATE_TIMER_ARRAY(timer, gptm_state, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int stellaris_gptm_init(SysBusDevice *dev)
+{
+ gptm_state *s = FROM_SYSBUS(gptm_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_out(&dev->qdev, &s->trigger, 1);
+
+ memory_region_init_io(&s->iomem, &gptm_ops, s,
+ "gptm", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->opaque[0] = s->opaque[1] = s;
+ s->timer[0] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[0]);
+ s->timer[1] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[1]);
+ vmstate_register(&dev->qdev, -1, &vmstate_stellaris_gptm, s);
+ return 0;
+}
+
+
+/* System controller. */
+
+typedef struct {
+ MemoryRegion iomem;
+ uint32_t pborctl;
+ uint32_t ldopctl;
+ uint32_t int_status;
+ uint32_t int_mask;
+ uint32_t resc;
+ uint32_t rcc;
+ uint32_t rcc2;
+ uint32_t rcgc[3];
+ uint32_t scgc[3];
+ uint32_t dcgc[3];
+ uint32_t clkvclr;
+ uint32_t ldoarst;
+ uint32_t user0;
+ uint32_t user1;
+ qemu_irq irq;
+ stellaris_board_info *board;
+} ssys_state;
+
+static void ssys_update(ssys_state *s)
+{
+ qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0);
+}
+
+static uint32_t pllcfg_sandstorm[16] = {
+ 0x31c0, /* 1 Mhz */
+ 0x1ae0, /* 1.8432 Mhz */
+ 0x18c0, /* 2 Mhz */
+ 0xd573, /* 2.4576 Mhz */
+ 0x37a6, /* 3.57954 Mhz */
+ 0x1ae2, /* 3.6864 Mhz */
+ 0x0c40, /* 4 Mhz */
+ 0x98bc, /* 4.906 Mhz */
+ 0x935b, /* 4.9152 Mhz */
+ 0x09c0, /* 5 Mhz */
+ 0x4dee, /* 5.12 Mhz */
+ 0x0c41, /* 6 Mhz */
+ 0x75db, /* 6.144 Mhz */
+ 0x1ae6, /* 7.3728 Mhz */
+ 0x0600, /* 8 Mhz */
+ 0x585b /* 8.192 Mhz */
+};
+
+static uint32_t pllcfg_fury[16] = {
+ 0x3200, /* 1 Mhz */
+ 0x1b20, /* 1.8432 Mhz */
+ 0x1900, /* 2 Mhz */
+ 0xf42b, /* 2.4576 Mhz */
+ 0x37e3, /* 3.57954 Mhz */
+ 0x1b21, /* 3.6864 Mhz */
+ 0x0c80, /* 4 Mhz */
+ 0x98ee, /* 4.906 Mhz */
+ 0xd5b4, /* 4.9152 Mhz */
+ 0x0a00, /* 5 Mhz */
+ 0x4e27, /* 5.12 Mhz */
+ 0x1902, /* 6 Mhz */
+ 0xec1c, /* 6.144 Mhz */
+ 0x1b23, /* 7.3728 Mhz */
+ 0x0640, /* 8 Mhz */
+ 0xb11c /* 8.192 Mhz */
+};
+
+#define DID0_VER_MASK 0x70000000
+#define DID0_VER_0 0x00000000
+#define DID0_VER_1 0x10000000
+
+#define DID0_CLASS_MASK 0x00FF0000
+#define DID0_CLASS_SANDSTORM 0x00000000
+#define DID0_CLASS_FURY 0x00010000
+
+static int ssys_board_class(const ssys_state *s)
+{
+ uint32_t did0 = s->board->did0;
+ switch (did0 & DID0_VER_MASK) {
+ case DID0_VER_0:
+ return DID0_CLASS_SANDSTORM;
+ case DID0_VER_1:
+ switch (did0 & DID0_CLASS_MASK) {
+ case DID0_CLASS_SANDSTORM:
+ case DID0_CLASS_FURY:
+ return did0 & DID0_CLASS_MASK;
+ }
+ /* for unknown classes, fall through */
+ default:
+ hw_error("ssys_board_class: Unknown class 0x%08x\n", did0);
+ }
+}
+
+static uint64_t ssys_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ switch (offset) {
+ case 0x000: /* DID0 */
+ return s->board->did0;
+ case 0x004: /* DID1 */
+ return s->board->did1;
+ case 0x008: /* DC0 */
+ return s->board->dc0;
+ case 0x010: /* DC1 */
+ return s->board->dc1;
+ case 0x014: /* DC2 */
+ return s->board->dc2;
+ case 0x018: /* DC3 */
+ return s->board->dc3;
+ case 0x01c: /* DC4 */
+ return s->board->dc4;
+ case 0x030: /* PBORCTL */
+ return s->pborctl;
+ case 0x034: /* LDOPCTL */
+ return s->ldopctl;
+ case 0x040: /* SRCR0 */
+ return 0;
+ case 0x044: /* SRCR1 */
+ return 0;
+ case 0x048: /* SRCR2 */
+ return 0;
+ case 0x050: /* RIS */
+ return s->int_status;
+ case 0x054: /* IMC */
+ return s->int_mask;
+ case 0x058: /* MISC */
+ return s->int_status & s->int_mask;
+ case 0x05c: /* RESC */
+ return s->resc;
+ case 0x060: /* RCC */
+ return s->rcc;
+ case 0x064: /* PLLCFG */
+ {
+ int xtal;
+ xtal = (s->rcc >> 6) & 0xf;
+ switch (ssys_board_class(s)) {
+ case DID0_CLASS_FURY:
+ return pllcfg_fury[xtal];
+ case DID0_CLASS_SANDSTORM:
+ return pllcfg_sandstorm[xtal];
+ default:
+ hw_error("ssys_read: Unhandled class for PLLCFG read.\n");
+ return 0;
+ }
+ }
+ case 0x070: /* RCC2 */
+ return s->rcc2;
+ case 0x100: /* RCGC0 */
+ return s->rcgc[0];
+ case 0x104: /* RCGC1 */
+ return s->rcgc[1];
+ case 0x108: /* RCGC2 */
+ return s->rcgc[2];
+ case 0x110: /* SCGC0 */
+ return s->scgc[0];
+ case 0x114: /* SCGC1 */
+ return s->scgc[1];
+ case 0x118: /* SCGC2 */
+ return s->scgc[2];
+ case 0x120: /* DCGC0 */
+ return s->dcgc[0];
+ case 0x124: /* DCGC1 */
+ return s->dcgc[1];
+ case 0x128: /* DCGC2 */
+ return s->dcgc[2];
+ case 0x150: /* CLKVCLR */
+ return s->clkvclr;
+ case 0x160: /* LDOARST */
+ return s->ldoarst;
+ case 0x1e0: /* USER0 */
+ return s->user0;
+ case 0x1e4: /* USER1 */
+ return s->user1;
+ default:
+ hw_error("ssys_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static bool ssys_use_rcc2(ssys_state *s)
+{
+ return (s->rcc2 >> 31) & 0x1;
+}
+
+/*
+ * Caculate the sys. clock period in ms.
+ */
+static void ssys_calculate_system_clock(ssys_state *s)
+{
+ if (ssys_use_rcc2(s)) {
+ system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
+ } else {
+ system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
+ }
+}
+
+static void ssys_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ switch (offset) {
+ case 0x030: /* PBORCTL */
+ s->pborctl = value & 0xffff;
+ break;
+ case 0x034: /* LDOPCTL */
+ s->ldopctl = value & 0x1f;
+ break;
+ case 0x040: /* SRCR0 */
+ case 0x044: /* SRCR1 */
+ case 0x048: /* SRCR2 */
+ fprintf(stderr, "Peripheral reset not implemented\n");
+ break;
+ case 0x054: /* IMC */
+ s->int_mask = value & 0x7f;
+ break;
+ case 0x058: /* MISC */
+ s->int_status &= ~value;
+ break;
+ case 0x05c: /* RESC */
+ s->resc = value & 0x3f;
+ break;
+ case 0x060: /* RCC */
+ if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) {
+ /* PLL enable. */
+ s->int_status |= (1 << 6);
+ }
+ s->rcc = value;
+ ssys_calculate_system_clock(s);
+ break;
+ case 0x070: /* RCC2 */
+ if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) {
+ break;
+ }
+
+ if ((s->rcc2 & (1 << 13)) != 0 && (value & (1 << 13)) == 0) {
+ /* PLL enable. */
+ s->int_status |= (1 << 6);
+ }
+ s->rcc2 = value;
+ ssys_calculate_system_clock(s);
+ break;
+ case 0x100: /* RCGC0 */
+ s->rcgc[0] = value;
+ break;
+ case 0x104: /* RCGC1 */
+ s->rcgc[1] = value;
+ break;
+ case 0x108: /* RCGC2 */
+ s->rcgc[2] = value;
+ break;
+ case 0x110: /* SCGC0 */
+ s->scgc[0] = value;
+ break;
+ case 0x114: /* SCGC1 */
+ s->scgc[1] = value;
+ break;
+ case 0x118: /* SCGC2 */
+ s->scgc[2] = value;
+ break;
+ case 0x120: /* DCGC0 */
+ s->dcgc[0] = value;
+ break;
+ case 0x124: /* DCGC1 */
+ s->dcgc[1] = value;
+ break;
+ case 0x128: /* DCGC2 */
+ s->dcgc[2] = value;
+ break;
+ case 0x150: /* CLKVCLR */
+ s->clkvclr = value;
+ break;
+ case 0x160: /* LDOARST */
+ s->ldoarst = value;
+ break;
+ default:
+ hw_error("ssys_write: Bad offset 0x%x\n", (int)offset);
+ }
+ ssys_update(s);
+}
+
+static const MemoryRegionOps ssys_ops = {
+ .read = ssys_read,
+ .write = ssys_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void ssys_reset(void *opaque)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ s->pborctl = 0x7ffd;
+ s->rcc = 0x078e3ac0;
+
+ if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) {
+ s->rcc2 = 0;
+ } else {
+ s->rcc2 = 0x07802810;
+ }
+ s->rcgc[0] = 1;
+ s->scgc[0] = 1;
+ s->dcgc[0] = 1;
+ ssys_calculate_system_clock(s);
+}
+
+static int stellaris_sys_post_load(void *opaque, int version_id)
+{
+ ssys_state *s = opaque;
+
+ ssys_calculate_system_clock(s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_stellaris_sys = {
+ .name = "stellaris_sys",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = stellaris_sys_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pborctl, ssys_state),
+ VMSTATE_UINT32(ldopctl, ssys_state),
+ VMSTATE_UINT32(int_mask, ssys_state),
+ VMSTATE_UINT32(int_status, ssys_state),
+ VMSTATE_UINT32(resc, ssys_state),
+ VMSTATE_UINT32(rcc, ssys_state),
+ VMSTATE_UINT32_V(rcc2, ssys_state, 2),
+ VMSTATE_UINT32_ARRAY(rcgc, ssys_state, 3),
+ VMSTATE_UINT32_ARRAY(scgc, ssys_state, 3),
+ VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3),
+ VMSTATE_UINT32(clkvclr, ssys_state),
+ VMSTATE_UINT32(ldoarst, ssys_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int stellaris_sys_init(uint32_t base, qemu_irq irq,
+ stellaris_board_info * board,
+ uint8_t *macaddr)
+{
+ ssys_state *s;
+
+ s = (ssys_state *)g_malloc0(sizeof(ssys_state));
+ s->irq = irq;
+ s->board = board;
+ /* Most devices come preprogrammed with a MAC address in the user data. */
+ s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
+ s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+
+ memory_region_init_io(&s->iomem, &ssys_ops, s, "ssys", 0x00001000);
+ memory_region_add_subregion(get_system_memory(), base, &s->iomem);
+ ssys_reset(s);
+ vmstate_register(NULL, -1, &vmstate_stellaris_sys, s);
+ return 0;
+}
+
+
+/* I2C controller. */
+
+typedef struct {
+ SysBusDevice busdev;
+ i2c_bus *bus;
+ qemu_irq irq;
+ MemoryRegion iomem;
+ uint32_t msa;
+ uint32_t mcs;
+ uint32_t mdr;
+ uint32_t mtpr;
+ uint32_t mimr;
+ uint32_t mris;
+ uint32_t mcr;
+} stellaris_i2c_state;
+
+#define STELLARIS_I2C_MCS_BUSY 0x01
+#define STELLARIS_I2C_MCS_ERROR 0x02
+#define STELLARIS_I2C_MCS_ADRACK 0x04
+#define STELLARIS_I2C_MCS_DATACK 0x08
+#define STELLARIS_I2C_MCS_ARBLST 0x10
+#define STELLARIS_I2C_MCS_IDLE 0x20
+#define STELLARIS_I2C_MCS_BUSBSY 0x40
+
+static uint64_t stellaris_i2c_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* MSA */
+ return s->msa;
+ case 0x04: /* MCS */
+ /* We don't emulate timing, so the controller is never busy. */
+ return s->mcs | STELLARIS_I2C_MCS_IDLE;
+ case 0x08: /* MDR */
+ return s->mdr;
+ case 0x0c: /* MTPR */
+ return s->mtpr;
+ case 0x10: /* MIMR */
+ return s->mimr;
+ case 0x14: /* MRIS */
+ return s->mris;
+ case 0x18: /* MMIS */
+ return s->mris & s->mimr;
+ case 0x20: /* MCR */
+ return s->mcr;
+ default:
+ hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_i2c_update(stellaris_i2c_state *s)
+{
+ int level;
+
+ level = (s->mris & s->mimr) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_i2c_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* MSA */
+ s->msa = value & 0xff;
+ break;
+ case 0x04: /* MCS */
+ if ((s->mcr & 0x10) == 0) {
+ /* Disabled. Do nothing. */
+ break;
+ }
+ /* Grab the bus if this is starting a transfer. */
+ if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) {
+ s->mcs |= STELLARIS_I2C_MCS_ARBLST;
+ } else {
+ s->mcs &= ~STELLARIS_I2C_MCS_ARBLST;
+ s->mcs |= STELLARIS_I2C_MCS_BUSBSY;
+ }
+ }
+ /* If we don't have the bus then indicate an error. */
+ if (!i2c_bus_busy(s->bus)
+ || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ s->mcs |= STELLARIS_I2C_MCS_ERROR;
+ break;
+ }
+ s->mcs &= ~STELLARIS_I2C_MCS_ERROR;
+ if (value & 1) {
+ /* Transfer a byte. */
+ /* TODO: Handle errors. */
+ if (s->msa & 1) {
+ /* Recv */
+ s->mdr = i2c_recv(s->bus) & 0xff;
+ } else {
+ /* Send */
+ i2c_send(s->bus, s->mdr);
+ }
+ /* Raise an interrupt. */
+ s->mris |= 1;
+ }
+ if (value & 4) {
+ /* Finish transfer. */
+ i2c_end_transfer(s->bus);
+ s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY;
+ }
+ break;
+ case 0x08: /* MDR */
+ s->mdr = value & 0xff;
+ break;
+ case 0x0c: /* MTPR */
+ s->mtpr = value & 0xff;
+ break;
+ case 0x10: /* MIMR */
+ s->mimr = 1;
+ break;
+ case 0x1c: /* MICR */
+ s->mris &= ~value;
+ break;
+ case 0x20: /* MCR */
+ if (value & 1)
+ hw_error(
+ "stellaris_i2c_write: Loopback not implemented\n");
+ if (value & 0x20)
+ hw_error(
+ "stellaris_i2c_write: Slave mode not implemented\n");
+ s->mcr = value & 0x31;
+ break;
+ default:
+ hw_error("stellaris_i2c_write: Bad offset 0x%x\n",
+ (int)offset);
+ }
+ stellaris_i2c_update(s);
+}
+
+static void stellaris_i2c_reset(stellaris_i2c_state *s)
+{
+ if (s->mcs & STELLARIS_I2C_MCS_BUSBSY)
+ i2c_end_transfer(s->bus);
+
+ s->msa = 0;
+ s->mcs = 0;
+ s->mdr = 0;
+ s->mtpr = 1;
+ s->mimr = 0;
+ s->mris = 0;
+ s->mcr = 0;
+ stellaris_i2c_update(s);
+}
+
+static const MemoryRegionOps stellaris_i2c_ops = {
+ .read = stellaris_i2c_read,
+ .write = stellaris_i2c_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_stellaris_i2c = {
+ .name = "stellaris_i2c",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(msa, stellaris_i2c_state),
+ VMSTATE_UINT32(mcs, stellaris_i2c_state),
+ VMSTATE_UINT32(mdr, stellaris_i2c_state),
+ VMSTATE_UINT32(mtpr, stellaris_i2c_state),
+ VMSTATE_UINT32(mimr, stellaris_i2c_state),
+ VMSTATE_UINT32(mris, stellaris_i2c_state),
+ VMSTATE_UINT32(mcr, stellaris_i2c_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int stellaris_i2c_init(SysBusDevice * dev)
+{
+ stellaris_i2c_state *s = FROM_SYSBUS(stellaris_i2c_state, dev);
+ i2c_bus *bus;
+
+ sysbus_init_irq(dev, &s->irq);
+ bus = i2c_init_bus(&dev->qdev, "i2c");
+ s->bus = bus;
+
+ memory_region_init_io(&s->iomem, &stellaris_i2c_ops, s,
+ "i2c", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ /* ??? For now we only implement the master interface. */
+ stellaris_i2c_reset(s);
+ vmstate_register(&dev->qdev, -1, &vmstate_stellaris_i2c, s);
+ return 0;
+}
+
+/* Analogue to Digital Converter. This is only partially implemented,
+ enough for applications that use a combined ADC and timer tick. */
+
+#define STELLARIS_ADC_EM_CONTROLLER 0
+#define STELLARIS_ADC_EM_COMP 1
+#define STELLARIS_ADC_EM_EXTERNAL 4
+#define STELLARIS_ADC_EM_TIMER 5
+#define STELLARIS_ADC_EM_PWM0 6
+#define STELLARIS_ADC_EM_PWM1 7
+#define STELLARIS_ADC_EM_PWM2 8
+
+#define STELLARIS_ADC_FIFO_EMPTY 0x0100
+#define STELLARIS_ADC_FIFO_FULL 0x1000
+
+typedef struct
+{
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t actss;
+ uint32_t ris;
+ uint32_t im;
+ uint32_t emux;
+ uint32_t ostat;
+ uint32_t ustat;
+ uint32_t sspri;
+ uint32_t sac;
+ struct {
+ uint32_t state;
+ uint32_t data[16];
+ } fifo[4];
+ uint32_t ssmux[4];
+ uint32_t ssctl[4];
+ uint32_t noise;
+ qemu_irq irq[4];
+} stellaris_adc_state;
+
+static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n)
+{
+ int tail;
+
+ tail = s->fifo[n].state & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) {
+ s->ustat |= 1 << n;
+ } else {
+ s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf);
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL;
+ if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf))
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY;
+ }
+ return s->fifo[n].data[tail];
+}
+
+static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n,
+ uint32_t value)
+{
+ int head;
+
+ /* TODO: Real hardware has limited size FIFOs. We have a full 16 entry
+ FIFO fir each sequencer. */
+ head = (s->fifo[n].state >> 4) & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) {
+ s->ostat |= 1 << n;
+ return;
+ }
+ s->fifo[n].data[head] = value;
+ head = (head + 1) & 0xf;
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY;
+ s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4);
+ if ((s->fifo[n].state & 0xf) == head)
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL;
+}
+
+static void stellaris_adc_update(stellaris_adc_state *s)
+{
+ int level;
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ level = (s->ris & s->im & (1 << n)) != 0;
+ qemu_set_irq(s->irq[n], level);
+ }
+}
+
+static void stellaris_adc_trigger(void *opaque, int irq, int level)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ if ((s->actss & (1 << n)) == 0) {
+ continue;
+ }
+
+ if (((s->emux >> (n * 4)) & 0xff) != 5) {
+ continue;
+ }
+
+ /* Some applications use the ADC as a random number source, so introduce
+ some variation into the signal. */
+ s->noise = s->noise * 314159 + 1;
+ /* ??? actual inputs not implemented. Return an arbitrary value. */
+ stellaris_adc_fifo_write(s, n, 0x200 + ((s->noise >> 16) & 7));
+ s->ris |= (1 << n);
+ stellaris_adc_update(s);
+ }
+}
+
+static void stellaris_adc_reset(stellaris_adc_state *s)
+{
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ s->ssmux[n] = 0;
+ s->ssctl[n] = 0;
+ s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY;
+ }
+}
+
+static uint64_t stellaris_adc_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ return s->ssmux[n];
+ case 0x04: /* SSCTL */
+ return s->ssctl[n];
+ case 0x08: /* SSFIFO */
+ return stellaris_adc_fifo_read(s, n);
+ case 0x0c: /* SSFSTAT */
+ return s->fifo[n].state;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ return s->actss;
+ case 0x04: /* RIS */
+ return s->ris;
+ case 0x08: /* IM */
+ return s->im;
+ case 0x0c: /* ISC */
+ return s->ris & s->im;
+ case 0x10: /* OSTAT */
+ return s->ostat;
+ case 0x14: /* EMUX */
+ return s->emux;
+ case 0x18: /* USTAT */
+ return s->ustat;
+ case 0x20: /* SSPRI */
+ return s->sspri;
+ case 0x30: /* SAC */
+ return s->sac;
+ default:
+ hw_error("strllaris_adc_read: Bad offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_adc_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ s->ssmux[n] = value & 0x33333333;
+ return;
+ case 0x04: /* SSCTL */
+ if (value != 6) {
+ hw_error("ADC: Unimplemented sequence %" PRIx64 "\n",
+ value);
+ }
+ s->ssctl[n] = value;
+ return;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ s->actss = value & 0xf;
+ break;
+ case 0x08: /* IM */
+ s->im = value;
+ break;
+ case 0x0c: /* ISC */
+ s->ris &= ~value;
+ break;
+ case 0x10: /* OSTAT */
+ s->ostat &= ~value;
+ break;
+ case 0x14: /* EMUX */
+ s->emux = value;
+ break;
+ case 0x18: /* USTAT */
+ s->ustat &= ~value;
+ break;
+ case 0x20: /* SSPRI */
+ s->sspri = value;
+ break;
+ case 0x28: /* PSSI */
+ hw_error("Not implemented: ADC sample initiate\n");
+ break;
+ case 0x30: /* SAC */
+ s->sac = value;
+ break;
+ default:
+ hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset);
+ }
+ stellaris_adc_update(s);
+}
+
+static const MemoryRegionOps stellaris_adc_ops = {
+ .read = stellaris_adc_read,
+ .write = stellaris_adc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_stellaris_adc = {
+ .name = "stellaris_adc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(actss, stellaris_adc_state),
+ VMSTATE_UINT32(ris, stellaris_adc_state),
+ VMSTATE_UINT32(im, stellaris_adc_state),
+ VMSTATE_UINT32(emux, stellaris_adc_state),
+ VMSTATE_UINT32(ostat, stellaris_adc_state),
+ VMSTATE_UINT32(ustat, stellaris_adc_state),
+ VMSTATE_UINT32(sspri, stellaris_adc_state),
+ VMSTATE_UINT32(sac, stellaris_adc_state),
+ VMSTATE_UINT32(fifo[0].state, stellaris_adc_state),
+ VMSTATE_UINT32_ARRAY(fifo[0].data, stellaris_adc_state, 16),
+ VMSTATE_UINT32(ssmux[0], stellaris_adc_state),
+ VMSTATE_UINT32(ssctl[0], stellaris_adc_state),
+ VMSTATE_UINT32(fifo[1].state, stellaris_adc_state),
+ VMSTATE_UINT32_ARRAY(fifo[1].data, stellaris_adc_state, 16),
+ VMSTATE_UINT32(ssmux[1], stellaris_adc_state),
+ VMSTATE_UINT32(ssctl[1], stellaris_adc_state),
+ VMSTATE_UINT32(fifo[2].state, stellaris_adc_state),
+ VMSTATE_UINT32_ARRAY(fifo[2].data, stellaris_adc_state, 16),
+ VMSTATE_UINT32(ssmux[2], stellaris_adc_state),
+ VMSTATE_UINT32(ssctl[2], stellaris_adc_state),
+ VMSTATE_UINT32(fifo[3].state, stellaris_adc_state),
+ VMSTATE_UINT32_ARRAY(fifo[3].data, stellaris_adc_state, 16),
+ VMSTATE_UINT32(ssmux[3], stellaris_adc_state),
+ VMSTATE_UINT32(ssctl[3], stellaris_adc_state),
+ VMSTATE_UINT32(noise, stellaris_adc_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int stellaris_adc_init(SysBusDevice *dev)
+{
+ stellaris_adc_state *s = FROM_SYSBUS(stellaris_adc_state, dev);
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ sysbus_init_irq(dev, &s->irq[n]);
+ }
+
+ memory_region_init_io(&s->iomem, &stellaris_adc_ops, s,
+ "adc", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ stellaris_adc_reset(s);
+ qdev_init_gpio_in(&dev->qdev, stellaris_adc_trigger, 1);
+ vmstate_register(&dev->qdev, -1, &vmstate_stellaris_adc, s);
+ return 0;
+}
+
+/* Board init. */
+static stellaris_board_info stellaris_boards[] = {
+ { "LM3S811EVB",
+ 0,
+ 0x0032000e,
+ 0x001f001f, /* dc0 */
+ 0x001132bf,
+ 0x01071013,
+ 0x3f0f01ff,
+ 0x0000001f,
+ BP_OLED_I2C
+ },
+ { "LM3S6965EVB",
+ 0x10010002,
+ 0x1073402e,
+ 0x00ff007f, /* dc0 */
+ 0x001133ff,
+ 0x030f5317,
+ 0x0f0f87ff,
+ 0x5000007f,
+ BP_OLED_SSI | BP_GAMEPAD
+ }
+};
+
+static void stellaris_init(const char *kernel_filename, const char *cpu_model,
+ stellaris_board_info *board)
+{
+ static const int uart_irq[] = {5, 6, 33, 34};
+ static const int timer_irq[] = {19, 21, 23, 35};
+ static const uint32_t gpio_addr[7] =
+ { 0x40004000, 0x40005000, 0x40006000, 0x40007000,
+ 0x40024000, 0x40025000, 0x40026000};
+ static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
+
+ MemoryRegion *address_space_mem = get_system_memory();
+ qemu_irq *pic;
+ DeviceState *gpio_dev[7];
+ qemu_irq gpio_in[7][8];
+ qemu_irq gpio_out[7][8];
+ qemu_irq adc;
+ int sram_size;
+ int flash_size;
+ i2c_bus *i2c;
+ DeviceState *dev;
+ int i;
+ int j;
+
+ flash_size = ((board->dc0 & 0xffff) + 1) << 1;
+ sram_size = (board->dc0 >> 18) + 1;
+ pic = armv7m_init(address_space_mem,
+ flash_size, sram_size, kernel_filename, cpu_model);
+
+ if (board->dc1 & (1 << 16)) {
+ dev = sysbus_create_varargs("stellaris-adc", 0x40038000,
+ pic[14], pic[15], pic[16], pic[17], NULL);
+ adc = qdev_get_gpio_in(dev, 0);
+ } else {
+ adc = NULL;
+ }
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (0x10000 << i)) {
+ dev = sysbus_create_simple("stellaris-gptm",
+ 0x40030000 + i * 0x1000,
+ pic[timer_irq[i]]);
+ /* TODO: This is incorrect, but we get away with it because
+ the ADC output is only ever pulsed. */
+ qdev_connect_gpio_out(dev, 0, adc);
+ }
+ }
+
+ stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a);
+
+ for (i = 0; i < 7; i++) {
+ if (board->dc4 & (1 << i)) {
+ gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i],
+ pic[gpio_irq[i]]);
+ for (j = 0; j < 8; j++) {
+ gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j);
+ gpio_out[i][j] = NULL;
+ }
+ }
+ }
+
+ if (board->dc2 & (1 << 12)) {
+ dev = sysbus_create_simple("stellaris-i2c", 0x40020000, pic[8]);
+ i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ if (board->peripherals & BP_OLED_I2C) {
+ i2c_create_slave(i2c, "ssd0303", 0x3d);
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (1 << i)) {
+ sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
+ pic[uart_irq[i]]);
+ }
+ }
+ if (board->dc2 & (1 << 4)) {
+ dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
+ if (board->peripherals & BP_OLED_SSI) {
+ void *bus;
+ DeviceState *sddev;
+ DeviceState *ssddev;
+
+ /* Some boards have both an OLED controller and SD card connected to
+ * the same SSI port, with the SD card chip select connected to a
+ * GPIO pin. Technically the OLED chip select is connected to the
+ * SSI Fss pin. We do not bother emulating that as both devices
+ * should never be selected simultaneously, and our OLED controller
+ * ignores stray 0xff commands that occur when deselecting the SD
+ * card.
+ */
+ bus = qdev_get_child_bus(dev, "ssi");
+
+ sddev = ssi_create_slave(bus, "ssi-sd");
+ ssddev = ssi_create_slave(bus, "ssd0323");
+ gpio_out[GPIO_D][0] = qemu_irq_split(qdev_get_gpio_in(sddev, 0),
+ qdev_get_gpio_in(ssddev, 0));
+ gpio_out[GPIO_C][7] = qdev_get_gpio_in(ssddev, 1);
+
+ /* Make sure the select pin is high. */
+ qemu_irq_raise(gpio_out[GPIO_D][0]);
+ }
+ }
+ if (board->dc4 & (1 << 28)) {
+ DeviceState *enet;
+
+ qemu_check_nic_model(&nd_table[0], "stellaris");
+
+ enet = qdev_create(NULL, "stellaris_enet");
+ qdev_set_nic_properties(enet, &nd_table[0]);
+ qdev_init_nofail(enet);
+ sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, pic[42]);
+ }
+ if (board->peripherals & BP_GAMEPAD) {
+ qemu_irq gpad_irq[5];
+ static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d };
+
+ gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */
+ gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */
+ gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */
+ gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */
+ gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */
+
+ stellaris_gamepad_init(5, gpad_irq, gpad_keycode);
+ }
+ for (i = 0; i < 7; i++) {
+ if (board->dc4 & (1 << i)) {
+ for (j = 0; j < 8; j++) {
+ if (gpio_out[i][j]) {
+ qdev_connect_gpio_out(gpio_dev[i], j, gpio_out[i][j]);
+ }
+ }
+ }
+ }
+}
+
+/* FIXME: Figure out how to generate these from stellaris_boards. */
+static void lm3s811evb_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]);
+}
+
+static void lm3s6965evb_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
+}
+
+static QEMUMachine lm3s811evb_machine = {
+ .name = "lm3s811evb",
+ .desc = "Stellaris LM3S811EVB",
+ .init = lm3s811evb_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine lm3s6965evb_machine = {
+ .name = "lm3s6965evb",
+ .desc = "Stellaris LM3S6965EVB",
+ .init = lm3s6965evb_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void stellaris_machine_init(void)
+{
+ qemu_register_machine(&lm3s811evb_machine);
+ qemu_register_machine(&lm3s6965evb_machine);
+}
+
+machine_init(stellaris_machine_init);
+
+static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = stellaris_i2c_init;
+}
+
+static const TypeInfo stellaris_i2c_info = {
+ .name = "stellaris-i2c",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(stellaris_i2c_state),
+ .class_init = stellaris_i2c_class_init,
+};
+
+static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = stellaris_gptm_init;
+}
+
+static const TypeInfo stellaris_gptm_info = {
+ .name = "stellaris-gptm",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(gptm_state),
+ .class_init = stellaris_gptm_class_init,
+};
+
+static void stellaris_adc_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = stellaris_adc_init;
+}
+
+static const TypeInfo stellaris_adc_info = {
+ .name = "stellaris-adc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(stellaris_adc_state),
+ .class_init = stellaris_adc_class_init,
+};
+
+static void stellaris_register_types(void)
+{
+ type_register_static(&stellaris_i2c_info);
+ type_register_static(&stellaris_gptm_info);
+ type_register_static(&stellaris_adc_info);
+}
+
+type_init(stellaris_register_types)
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
new file mode 100644
index 0000000..747888c
--- /dev/null
+++ b/hw/arm/tosa.c
@@ -0,0 +1,302 @@
+/* vim:set shiftwidth=4 ts=4 et: */
+/*
+ * PXA255 Sharp Zaurus SL-6000 PDA platform
+ *
+ * Copyright (c) 2008 Dmitry Baryshkov
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/sharpsl.h"
+#include "hw/pcmcia.h"
+#include "block/block.h"
+#include "hw/boards.h"
+#include "hw/i2c.h"
+#include "hw/ssi.h"
+#include "sysemu/blockdev.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+#define TOSA_RAM 0x04000000
+#define TOSA_ROM 0x00800000
+
+#define TOSA_GPIO_USB_IN (5)
+#define TOSA_GPIO_nSD_DETECT (9)
+#define TOSA_GPIO_ON_RESET (19)
+#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */
+#define TOSA_GPIO_CF_CD (13)
+#define TOSA_GPIO_TC6393XB_INT (15)
+#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */
+
+#define TOSA_SCOOP_GPIO_BASE 1
+#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2)
+#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3)
+#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4)
+
+#define TOSA_SCOOP_JC_GPIO_BASE 1
+#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0)
+#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1)
+#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2)
+#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5)
+#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7)
+
+#define DAC_BASE 0x4e
+#define DAC_CH1 0
+#define DAC_CH2 1
+
+static void tosa_microdrive_attach(PXA2xxState *cpu)
+{
+ PCMCIACardState *md;
+ DriveInfo *dinfo;
+
+ dinfo = drive_get(IF_IDE, 0, 0);
+ if (!dinfo || dinfo->media_cd)
+ return;
+ md = dscm1xxxx_init(dinfo);
+ pxa2xx_pcmcia_attach(cpu->pcmcia[0], md);
+}
+
+static void tosa_out_switch(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ fprintf(stderr, "blue LED %s.\n", level ? "on" : "off");
+ break;
+ case 1:
+ fprintf(stderr, "green LED %s.\n", level ? "on" : "off");
+ break;
+ case 2:
+ fprintf(stderr, "amber LED %s.\n", level ? "on" : "off");
+ break;
+ case 3:
+ fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off");
+ break;
+ default:
+ fprintf(stderr, "Uhandled out event: %d = %d\n", line, level);
+ break;
+ }
+}
+
+
+static void tosa_gpio_setup(PXA2xxState *cpu,
+ DeviceState *scp0,
+ DeviceState *scp1,
+ TC6393xbState *tmio)
+{
+ qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4);
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc,
+ qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP),
+ qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT)));
+
+ /* Handle reset */
+ qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset);
+
+ /* PCMCIA signals: card's IRQ and Card-Detect */
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0],
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ),
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD));
+
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1],
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ),
+ NULL);
+
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]);
+
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio));
+
+ /* UDC Vbus */
+ qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_USB_IN));
+}
+
+static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value)
+{
+ fprintf(stderr, "TG: %d %02x\n", value >> 5, value & 0x1f);
+ return 0;
+}
+
+static int tosa_ssp_init(SSISlave *dev)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+typedef struct {
+ I2CSlave i2c;
+ int len;
+ char buf[3];
+} TosaDACState;
+
+static int tosa_dac_send(I2CSlave *i2c, uint8_t data)
+{
+ TosaDACState *s = FROM_I2C_SLAVE(TosaDACState, i2c);
+ s->buf[s->len] = data;
+ if (s->len ++ > 2) {
+#ifdef VERBOSE
+ fprintf(stderr, "%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+ return 1;
+ }
+
+ if (s->len == 2) {
+ fprintf(stderr, "dac: channel %d value 0x%02x\n",
+ s->buf[0], s->buf[1]);
+ }
+
+ return 0;
+}
+
+static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
+{
+ TosaDACState *s = FROM_I2C_SLAVE(TosaDACState, i2c);
+ s->len = 0;
+ switch (event) {
+ case I2C_START_SEND:
+ break;
+ case I2C_START_RECV:
+ printf("%s: recv not supported!!!\n", __FUNCTION__);
+ break;
+ case I2C_FINISH:
+#ifdef VERBOSE
+ if (s->len < 2)
+ printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
+ if (s->len > 2)
+ printf("%s: message too long\n", __FUNCTION__);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+static int tosa_dac_recv(I2CSlave *s)
+{
+ printf("%s: recv not supported!!!\n", __FUNCTION__);
+ return -1;
+}
+
+static int tosa_dac_init(I2CSlave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static void tosa_tg_init(PXA2xxState *cpu)
+{
+ i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
+ i2c_create_slave(bus, "tosa_dac", DAC_BASE);
+ ssi_create_slave(cpu->ssp[1], "tosa-ssp");
+}
+
+
+static struct arm_boot_info tosa_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void tosa_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *rom = g_new(MemoryRegion, 1);
+ PXA2xxState *mpu;
+ TC6393xbState *tmio;
+ DeviceState *scp0, *scp1;
+
+ if (!cpu_model)
+ cpu_model = "pxa255";
+
+ mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size);
+
+ memory_region_init_ram(rom, "tosa.rom", TOSA_ROM);
+ vmstate_register_ram_global(rom);
+ memory_region_set_readonly(rom, true);
+ memory_region_add_subregion(address_space_mem, 0, rom);
+
+ tmio = tc6393xb_init(address_space_mem, 0x10000000,
+ qdev_get_gpio_in(mpu->gpio, TOSA_GPIO_TC6393XB_INT));
+
+ scp0 = sysbus_create_simple("scoop", 0x08800000, NULL);
+ scp1 = sysbus_create_simple("scoop", 0x14800040, NULL);
+
+ tosa_gpio_setup(mpu, scp0, scp1, tmio);
+
+ tosa_microdrive_attach(mpu);
+
+ tosa_tg_init(mpu);
+
+ tosa_binfo.kernel_filename = kernel_filename;
+ tosa_binfo.kernel_cmdline = kernel_cmdline;
+ tosa_binfo.initrd_filename = initrd_filename;
+ tosa_binfo.board_id = 0x208;
+ arm_load_kernel(mpu->cpu, &tosa_binfo);
+ sl_bootparam_write(SL_PXA_PARAM_BASE);
+}
+
+static QEMUMachine tosapda_machine = {
+ .name = "tosa",
+ .desc = "Tosa PDA (PXA255)",
+ .init = tosa_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void tosapda_machine_init(void)
+{
+ qemu_register_machine(&tosapda_machine);
+}
+
+machine_init(tosapda_machine_init);
+
+static void tosa_dac_class_init(ObjectClass *klass, void *data)
+{
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->init = tosa_dac_init;
+ k->event = tosa_dac_event;
+ k->recv = tosa_dac_recv;
+ k->send = tosa_dac_send;
+}
+
+static const TypeInfo tosa_dac_info = {
+ .name = "tosa_dac",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(TosaDACState),
+ .class_init = tosa_dac_class_init,
+};
+
+static void tosa_ssp_class_init(ObjectClass *klass, void *data)
+{
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = tosa_ssp_init;
+ k->transfer = tosa_ssp_tansfer;
+}
+
+static const TypeInfo tosa_ssp_info = {
+ .name = "tosa-ssp",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(SSISlave),
+ .class_init = tosa_ssp_class_init,
+};
+
+static void tosa_register_types(void)
+{
+ type_register_static(&tosa_dac_info);
+ type_register_static(&tosa_ssp_info);
+}
+
+type_init(tosa_register_types)
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
new file mode 100644
index 0000000..baaa265
--- /dev/null
+++ b/hw/arm/versatilepb.c
@@ -0,0 +1,403 @@
+/*
+ * ARM Versatile Platform/Application Baseboard System emulation.
+ *
+ * Copyright (c) 2005-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/pci/pci.h"
+#include "hw/i2c.h"
+#include "hw/boards.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+#include "hw/flash.h"
+
+#define VERSATILE_FLASH_ADDR 0x34000000
+#define VERSATILE_FLASH_SIZE (64 * 1024 * 1024)
+#define VERSATILE_FLASH_SECT_SIZE (256 * 1024)
+
+/* Primary interrupt controller. */
+
+typedef struct vpb_sic_state
+{
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t level;
+ uint32_t mask;
+ uint32_t pic_enable;
+ qemu_irq parent[32];
+ int irq;
+} vpb_sic_state;
+
+static const VMStateDescription vmstate_vpb_sic = {
+ .name = "versatilepb_sic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, vpb_sic_state),
+ VMSTATE_UINT32(mask, vpb_sic_state),
+ VMSTATE_UINT32(pic_enable, vpb_sic_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vpb_sic_update(vpb_sic_state *s)
+{
+ uint32_t flags;
+
+ flags = s->level & s->mask;
+ qemu_set_irq(s->parent[s->irq], flags != 0);
+}
+
+static void vpb_sic_update_pic(vpb_sic_state *s)
+{
+ int i;
+ uint32_t mask;
+
+ for (i = 21; i <= 30; i++) {
+ mask = 1u << i;
+ if (!(s->pic_enable & mask))
+ continue;
+ qemu_set_irq(s->parent[i], (s->level & mask) != 0);
+ }
+}
+
+static void vpb_sic_set_irq(void *opaque, int irq, int level)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ if (s->pic_enable & (1u << irq))
+ qemu_set_irq(s->parent[irq], level);
+ vpb_sic_update(s);
+}
+
+static uint64_t vpb_sic_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* STATUS */
+ return s->level & s->mask;
+ case 1: /* RAWSTAT */
+ return s->level;
+ case 2: /* ENABLE */
+ return s->mask;
+ case 4: /* SOFTINT */
+ return s->level & 1;
+ case 8: /* PICENABLE */
+ return s->pic_enable;
+ default:
+ printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void vpb_sic_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 2: /* ENSET */
+ s->mask |= value;
+ break;
+ case 3: /* ENCLR */
+ s->mask &= ~value;
+ break;
+ case 4: /* SOFTINTSET */
+ if (value)
+ s->mask |= 1;
+ break;
+ case 5: /* SOFTINTCLR */
+ if (value)
+ s->mask &= ~1u;
+ break;
+ case 8: /* PICENSET */
+ s->pic_enable |= (value & 0x7fe00000);
+ vpb_sic_update_pic(s);
+ break;
+ case 9: /* PICENCLR */
+ s->pic_enable &= ~value;
+ vpb_sic_update_pic(s);
+ break;
+ default:
+ printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset);
+ return;
+ }
+ vpb_sic_update(s);
+}
+
+static const MemoryRegionOps vpb_sic_ops = {
+ .read = vpb_sic_read,
+ .write = vpb_sic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int vpb_sic_init(SysBusDevice *dev)
+{
+ vpb_sic_state *s = FROM_SYSBUS(vpb_sic_state, dev);
+ int i;
+
+ qdev_init_gpio_in(&dev->qdev, vpb_sic_set_irq, 32);
+ for (i = 0; i < 32; i++) {
+ sysbus_init_irq(dev, &s->parent[i]);
+ }
+ s->irq = 31;
+ memory_region_init_io(&s->iomem, &vpb_sic_ops, s, "vpb-sic", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+/* Board init. */
+
+/* The AB and PB boards both use the same core, just with different
+ peripherals and expansion busses. For now we emulate a subset of the
+ PB peripherals and just change the board ID. */
+
+static struct arm_boot_info versatile_binfo;
+
+static void versatile_init(QEMUMachineInitArgs *args, int board_id)
+{
+ ARMCPU *cpu;
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ qemu_irq *cpu_pic;
+ qemu_irq pic[32];
+ qemu_irq sic[32];
+ DeviceState *dev, *sysctl;
+ SysBusDevice *busdev;
+ DeviceState *pl041;
+ PCIBus *pci_bus;
+ NICInfo *nd;
+ i2c_bus *i2c;
+ int n;
+ int done_smc = 0;
+ DriveInfo *dinfo;
+
+ if (!args->cpu_model) {
+ args->cpu_model = "arm926";
+ }
+ cpu = cpu_arm_init(args->cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ memory_region_init_ram(ram, "versatile.ram", args->ram_size);
+ vmstate_register_ram_global(ram);
+ /* ??? RAM should repeat to fill physical memory space. */
+ /* SDRAM at address zero. */
+ memory_region_add_subregion(sysmem, 0, ram);
+
+ sysctl = qdev_create(NULL, "realview_sysctl");
+ qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004);
+ qdev_prop_set_uint32(sysctl, "proc_id", 0x02000000);
+ qdev_init_nofail(sysctl);
+ sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000);
+
+ cpu_pic = arm_pic_init_cpu(cpu);
+ dev = sysbus_create_varargs("pl190", 0x10140000,
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+ for (n = 0; n < 32; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+ dev = sysbus_create_simple("versatilepb_sic", 0x10003000, NULL);
+ for (n = 0; n < 32; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), n, pic[n]);
+ sic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ sysbus_create_simple("pl050_keyboard", 0x10006000, sic[3]);
+ sysbus_create_simple("pl050_mouse", 0x10007000, sic[4]);
+
+ dev = qdev_create(NULL, "versatile_pci");
+ busdev = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(busdev, 0, 0x41000000); /* PCI self-config */
+ sysbus_mmio_map(busdev, 1, 0x42000000); /* PCI config */
+ sysbus_connect_irq(busdev, 0, sic[27]);
+ sysbus_connect_irq(busdev, 1, sic[28]);
+ sysbus_connect_irq(busdev, 2, sic[29]);
+ sysbus_connect_irq(busdev, 3, sic[30]);
+ pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
+
+ /* The Versatile PCI bridge does not provide access to PCI IO space,
+ so many of the qemu PCI devices are not useable. */
+ for(n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+
+ if (!done_smc && (!nd->model || strcmp(nd->model, "smc91c111") == 0)) {
+ smc91c111_init(nd, 0x10010000, sic[25]);
+ done_smc = 1;
+ } else {
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
+ }
+ }
+ if (usb_enabled(false)) {
+ pci_create_simple(pci_bus, -1, "pci-ohci");
+ }
+ n = drive_get_max_bus(IF_SCSI);
+ while (n >= 0) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ n--;
+ }
+
+ sysbus_create_simple("pl011", 0x101f1000, pic[12]);
+ sysbus_create_simple("pl011", 0x101f2000, pic[13]);
+ sysbus_create_simple("pl011", 0x101f3000, pic[14]);
+ sysbus_create_simple("pl011", 0x10009000, sic[6]);
+
+ sysbus_create_simple("pl080", 0x10130000, pic[17]);
+ sysbus_create_simple("sp804", 0x101e2000, pic[4]);
+ sysbus_create_simple("sp804", 0x101e3000, pic[5]);
+
+ sysbus_create_simple("pl061", 0x101e4000, pic[6]);
+ sysbus_create_simple("pl061", 0x101e5000, pic[7]);
+ sysbus_create_simple("pl061", 0x101e6000, pic[8]);
+ sysbus_create_simple("pl061", 0x101e7000, pic[9]);
+
+ /* The versatile/PB actually has a modified Color LCD controller
+ that includes hardware cursor support from the PL111. */
+ dev = sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]);
+ /* Wire up the mux control signals from the SYS_CLCD register */
+ qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0));
+
+ sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL);
+ sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL);
+
+ /* Add PL031 Real Time Clock. */
+ sysbus_create_simple("pl031", 0x101e8000, pic[10]);
+
+ dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL);
+ i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ i2c_create_slave(i2c, "ds1338", 0x68);
+
+ /* Add PL041 AACI Interface to the LM4549 codec */
+ pl041 = qdev_create(NULL, "pl041");
+ qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
+ qdev_init_nofail(pl041);
+ sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, sic[24]);
+
+ /* Memory map for Versatile/PB: */
+ /* 0x10000000 System registers. */
+ /* 0x10001000 PCI controller config registers. */
+ /* 0x10002000 Serial bus interface. */
+ /* 0x10003000 Secondary interrupt controller. */
+ /* 0x10004000 AACI (audio). */
+ /* 0x10005000 MMCI0. */
+ /* 0x10006000 KMI0 (keyboard). */
+ /* 0x10007000 KMI1 (mouse). */
+ /* 0x10008000 Character LCD Interface. */
+ /* 0x10009000 UART3. */
+ /* 0x1000a000 Smart card 1. */
+ /* 0x1000b000 MMCI1. */
+ /* 0x10010000 Ethernet. */
+ /* 0x10020000 USB. */
+ /* 0x10100000 SSMC. */
+ /* 0x10110000 MPMC. */
+ /* 0x10120000 CLCD Controller. */
+ /* 0x10130000 DMA Controller. */
+ /* 0x10140000 Vectored interrupt controller. */
+ /* 0x101d0000 AHB Monitor Interface. */
+ /* 0x101e0000 System Controller. */
+ /* 0x101e1000 Watchdog Interface. */
+ /* 0x101e2000 Timer 0/1. */
+ /* 0x101e3000 Timer 2/3. */
+ /* 0x101e4000 GPIO port 0. */
+ /* 0x101e5000 GPIO port 1. */
+ /* 0x101e6000 GPIO port 2. */
+ /* 0x101e7000 GPIO port 3. */
+ /* 0x101e8000 RTC. */
+ /* 0x101f0000 Smart card 0. */
+ /* 0x101f1000 UART0. */
+ /* 0x101f2000 UART1. */
+ /* 0x101f3000 UART2. */
+ /* 0x101f4000 SSPI. */
+ /* 0x34000000 NOR Flash */
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash",
+ VERSATILE_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
+ VERSATILE_FLASH_SECT_SIZE,
+ VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE,
+ 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ }
+
+ versatile_binfo.ram_size = args->ram_size;
+ versatile_binfo.kernel_filename = args->kernel_filename;
+ versatile_binfo.kernel_cmdline = args->kernel_cmdline;
+ versatile_binfo.initrd_filename = args->initrd_filename;
+ versatile_binfo.board_id = board_id;
+ arm_load_kernel(cpu, &versatile_binfo);
+}
+
+static void vpb_init(QEMUMachineInitArgs *args)
+{
+ versatile_init(args, 0x183);
+}
+
+static void vab_init(QEMUMachineInitArgs *args)
+{
+ versatile_init(args, 0x25e);
+}
+
+static QEMUMachine versatilepb_machine = {
+ .name = "versatilepb",
+ .desc = "ARM Versatile/PB (ARM926EJ-S)",
+ .init = vpb_init,
+ .block_default_type = IF_SCSI,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine versatileab_machine = {
+ .name = "versatileab",
+ .desc = "ARM Versatile/AB (ARM926EJ-S)",
+ .init = vab_init,
+ .block_default_type = IF_SCSI,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void versatile_machine_init(void)
+{
+ qemu_register_machine(&versatilepb_machine);
+ qemu_register_machine(&versatileab_machine);
+}
+
+machine_init(versatile_machine_init);
+
+static void vpb_sic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = vpb_sic_init;
+ dc->no_user = 1;
+ dc->vmsd = &vmstate_vpb_sic;
+}
+
+static const TypeInfo vpb_sic_info = {
+ .name = "versatilepb_sic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(vpb_sic_state),
+ .class_init = vpb_sic_class_init,
+};
+
+static void versatilepb_register_types(void)
+{
+ type_register_static(&vpb_sic_info);
+}
+
+type_init(versatilepb_register_types)
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
new file mode 100644
index 0000000..02922c3
--- /dev/null
+++ b/hw/arm/vexpress.c
@@ -0,0 +1,500 @@
+/*
+ * ARM Versatile Express emulation.
+ *
+ * Copyright (c) 2010 - 2011 B Labs Ltd.
+ * Copyright (c) 2011 Linaro Limited
+ * Written by Bahadir Balban, Amit Mahajan, Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/primecell.h"
+#include "hw/devices.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "sysemu/blockdev.h"
+#include "hw/flash.h"
+
+#define VEXPRESS_BOARD_ID 0x8e0
+#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
+#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
+
+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_NORFLASH1,
+ VE_SRAM,
+ VE_VIDEORAM,
+ VE_ETHERNET,
+ VE_USB,
+ VE_DAPROM,
+};
+
+static hwaddr 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 hwaddr motherboard_aseries_map[] = {
+ /* CS0: 0x08000000 .. 0x0c000000 */
+ [VE_NORFLASH0] = 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 hwaddr *motherboard_map;
+ hwaddr loader_start;
+ const hwaddr 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)
+{
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *lowram = g_new(MemoryRegion, 1);
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ qemu_irq *irqp;
+ int n;
+ qemu_irq cpu_irq[4];
+ ram_addr_t low_ram_size;
+
+ if (!cpu_model) {
+ cpu_model = "cortex-a9";
+ }
+
+ *proc_id = 0x0c000191;
+
+ for (n = 0; n < smp_cpus; n++) {
+ ARMCPU *cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(cpu);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ if (ram_size > 0x40000000) {
+ /* 1GB is the maximum the address space permits */
+ fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n");
+ exit(1);
+ }
+
+ memory_region_init_ram(ram, "vexpress.highmem", ram_size);
+ vmstate_register_ram_global(ram);
+ low_ram_size = ram_size;
+ if (low_ram_size > 0x4000000) {
+ low_ram_size = 0x4000000;
+ }
+ /* RAM is from 0x60000000 upwards. The bottom 64MB of the
+ * address space should in theory be remappable to various
+ * things including ROM or RAM; we always map the RAM there.
+ */
+ memory_region_init_alias(lowram, "vexpress.lowmem", ram, 0, low_ram_size);
+ memory_region_add_subregion(sysmem, 0x0, lowram);
+ memory_region_add_subregion(sysmem, 0x60000000, ram);
+
+ /* 0x1e000000 A9MPCore (SCU) private memory region */
+ dev = qdev_create(NULL, "a9mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0x1e000000);
+ 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 the
+ * A9MP has internal interrupts 0..31).
+ */
+ for (n = 0; n < 64; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
+
+ /* 0x10020000 PL111 CLCD (daughterboard) */
+ sysbus_create_simple("pl111", 0x10020000, pic[44]);
+
+ /* 0x10060000 AXI RAM */
+ /* 0x100e0000 PL341 Dynamic Memory Controller */
+ /* 0x100e1000 PL354 Static Memory Controller */
+ /* 0x100e2000 System Configuration Controller */
+
+ sysbus_create_simple("sp804", 0x100e4000, pic[48]);
+ /* 0x100e5000 SP805 Watchdog module */
+ /* 0x100e6000 BP147 TrustZone Protection Controller */
+ /* 0x100e9000 PL301 'Fast' AXI matrix */
+ /* 0x100ea000 PL301 'Slow' AXI matrix */
+ /* 0x100ec000 TrustZone Address Space Controller */
+ /* 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;
+ 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 = 0x14000237;
+
+ for (n = 0; n < smp_cpus; n++) {
+ ARMCPU *cpu;
+ qemu_irq *irqp;
+
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(cpu);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ {
+ /* We have to use a separate 64 bit variable here to avoid the gcc
+ * "comparison is always false due to limited range of data type"
+ * warning if we are on a host where ram_addr_t is 32 bits.
+ */
+ uint64_t rsz = ram_size;
+ if (rsz > (30ULL * 1024 * 1024 * 1024)) {
+ fprintf(stderr, "vexpress-a15: cannot model more than 30GB 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 = SYS_BUS_DEVICE(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,
+ QEMUMachineInitArgs *args)
+{
+ DeviceState *dev, *sysctl, *pl041;
+ qemu_irq pic[64];
+ uint32_t proc_id;
+ uint32_t sys_id;
+ DriveInfo *dinfo;
+ 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 hwaddr *map = daughterboard->motherboard_map;
+
+ daughterboard->init(daughterboard, args->ram_size, args->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(SYS_BUS_DEVICE(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(SYS_BUS_DEVICE(pl041), 0, map[VE_PL041]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(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]);
+
+ dinfo = drive_get_next(IF_PFLASH);
+ if (!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)) {
+ fprintf(stderr, "vexpress: error registering flash 0.\n");
+ exit(1);
+ }
+
+ 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)) {
+ fprintf(stderr, "vexpress: error registering flash 1.\n");
+ exit(1);
+ }
+
+ sram_size = 0x2000000;
+ memory_region_init_ram(sram, "vexpress.sram", sram_size);
+ vmstate_register_ram_global(sram);
+ memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
+
+ vram_size = 0x800000;
+ memory_region_init_ram(vram, "vexpress.vram", vram_size);
+ vmstate_register_ram_global(vram);
+ memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
+
+ /* 0x4e000000 LAN9118 Ethernet */
+ if (nd_table[0].used) {
+ lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]);
+ }
+
+ /* VE_USB: not modelled */
+
+ /* VE_DAPROM: not modelled */
+
+ vexpress_binfo.ram_size = args->ram_size;
+ vexpress_binfo.kernel_filename = args->kernel_filename;
+ vexpress_binfo.kernel_cmdline = args->kernel_cmdline;
+ vexpress_binfo.initrd_filename = args->initrd_filename;
+ vexpress_binfo.nb_cpus = smp_cpus;
+ vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
+ 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(arm_env_get_cpu(first_cpu), &vexpress_binfo);
+}
+
+static void vexpress_a9_init(QEMUMachineInitArgs *args)
+{
+ vexpress_common_init(&a9_daughterboard, args);
+}
+
+static void vexpress_a15_init(QEMUMachineInitArgs *args)
+{
+ vexpress_common_init(&a15_daughterboard, args);
+}
+
+static QEMUMachine vexpress_a9_machine = {
+ .name = "vexpress-a9",
+ .desc = "ARM Versatile Express for Cortex-A9",
+ .init = vexpress_a9_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 4,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static QEMUMachine vexpress_a15_machine = {
+ .name = "vexpress-a15",
+ .desc = "ARM Versatile Express for Cortex-A15",
+ .init = vexpress_a15_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 4,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void vexpress_machine_init(void)
+{
+ qemu_register_machine(&vexpress_a9_machine);
+ qemu_register_machine(&vexpress_a15_machine);
+}
+
+machine_init(vexpress_machine_init);
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
new file mode 100644
index 0000000..f78c47e
--- /dev/null
+++ b/hw/arm/xilinx_zynq.c
@@ -0,0 +1,224 @@
+/*
+ * Xilinx Zynq Baseboard System emulation.
+ *
+ * Copyright (c) 2010 Xilinx.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com)
+ * Copyright (c) 2012 Petalogix Pty Ltd.
+ * Written by Haibing Ma
+ *
+ * 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.
+ *
+ * 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 "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "net/net.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+#include "hw/loader.h"
+#include "hw/ssi.h"
+
+#define NUM_SPI_FLASHES 4
+#define NUM_QSPI_FLASHES 2
+#define NUM_QSPI_BUSSES 2
+
+#define FLASH_SIZE (64 * 1024 * 1024)
+#define FLASH_SECTOR_SIZE (128 * 1024)
+
+#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
+
+static struct arm_boot_info zynq_binfo = {};
+
+static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "cadence_gem");
+ dev = qdev_create(NULL, "cadence_gem");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+}
+
+static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
+ bool is_qspi)
+{
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ SSIBus *spi;
+ DeviceState *flash_dev;
+ int i, j;
+ int num_busses = is_qspi ? NUM_QSPI_BUSSES : 1;
+ int num_ss = is_qspi ? NUM_QSPI_FLASHES : NUM_SPI_FLASHES;
+
+ dev = qdev_create(NULL, "xilinx,spips");
+ qdev_prop_set_uint8(dev, "num-txrx-bytes", is_qspi ? 4 : 1);
+ qdev_prop_set_uint8(dev, "num-ss-bits", num_ss);
+ qdev_prop_set_uint8(dev, "num-busses", num_busses);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, base_addr);
+ if (is_qspi) {
+ sysbus_mmio_map(busdev, 1, 0xFC000000);
+ }
+ sysbus_connect_irq(busdev, 0, irq);
+
+ for (i = 0; i < num_busses; ++i) {
+ char bus_name[16];
+ qemu_irq cs_line;
+
+ snprintf(bus_name, 16, "spi%d", i);
+ spi = (SSIBus *)qdev_get_child_bus(dev, bus_name);
+
+ for (j = 0; j < num_ss; ++j) {
+ flash_dev = ssi_create_slave_no_init(spi, "n25q128");
+ qdev_init_nofail(flash_dev);
+
+ cs_line = qdev_get_gpio_in(flash_dev, 0);
+ sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line);
+ }
+ }
+
+}
+
+static void zynq_init(QEMUMachineInitArgs *args)
+{
+ ram_addr_t ram_size = args->ram_size;
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ ARMCPU *cpu;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
+ MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ qemu_irq *irqp;
+ qemu_irq pic[64];
+ NICInfo *nd;
+ int n;
+ qemu_irq cpu_irq;
+
+ if (!cpu_model) {
+ cpu_model = "cortex-a9";
+ }
+
+ cpu = cpu_arm_init(cpu_model);
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(cpu);
+ cpu_irq = irqp[ARM_PIC_CPU_IRQ];
+
+ /* max 2GB ram */
+ if (ram_size > 0x80000000) {
+ ram_size = 0x80000000;
+ }
+
+ /* DDR remapped to address zero. */
+ memory_region_init_ram(ext_ram, "zynq.ext_ram", ram_size);
+ vmstate_register_ram_global(ext_ram);
+ memory_region_add_subregion(address_space_mem, 0, ext_ram);
+
+ /* 256K of on-chip memory */
+ memory_region_init_ram(ocm_ram, "zynq.ocm_ram", 256 << 10);
+ vmstate_register_ram_global(ocm_ram);
+ memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram);
+
+ DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
+
+ /* AMD */
+ pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE,
+ dinfo ? dinfo->bdrv : NULL, FLASH_SECTOR_SIZE,
+ FLASH_SIZE/FLASH_SECTOR_SIZE, 1,
+ 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa,
+ 0);
+
+ dev = qdev_create(NULL, "xilinx,zynq_slcr");
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8000000);
+
+ dev = qdev_create(NULL, "a9mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", 1);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0xF8F00000);
+ sysbus_connect_irq(busdev, 0, cpu_irq);
+
+ for (n = 0; n < 64; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ zynq_init_spi_flashes(0xE0006000, pic[58-IRQ_OFFSET], false);
+ zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
+ zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
+
+ sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
+ sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]);
+
+ sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
+ sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
+
+ sysbus_create_varargs("cadence_ttc", 0xF8001000,
+ pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
+ sysbus_create_varargs("cadence_ttc", 0xF8002000,
+ pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL);
+
+ for (n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+ if (n == 0) {
+ gem_init(nd, 0xE000B000, pic[54-IRQ_OFFSET]);
+ } else if (n == 1) {
+ gem_init(nd, 0xE000C000, pic[77-IRQ_OFFSET]);
+ }
+ }
+
+ dev = qdev_create(NULL, "generic-sdhci");
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]);
+
+ dev = qdev_create(NULL, "generic-sdhci");
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
+
+ zynq_binfo.ram_size = ram_size;
+ zynq_binfo.kernel_filename = kernel_filename;
+ zynq_binfo.kernel_cmdline = kernel_cmdline;
+ zynq_binfo.initrd_filename = initrd_filename;
+ zynq_binfo.nb_cpus = 1;
+ zynq_binfo.board_id = 0xd32;
+ zynq_binfo.loader_start = 0;
+ arm_load_kernel(arm_env_get_cpu(first_cpu), &zynq_binfo);
+}
+
+static QEMUMachine zynq_machine = {
+ .name = "xilinx-zynq-a9",
+ .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
+ .init = zynq_init,
+ .block_default_type = IF_SCSI,
+ .max_cpus = 1,
+ .no_sdcard = 1,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void zynq_machine_init(void)
+{
+ qemu_register_machine(&zynq_machine);
+}
+
+machine_init(zynq_machine_init);
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
new file mode 100644
index 0000000..cbb6d80
--- /dev/null
+++ b/hw/arm/z2.c
@@ -0,0 +1,384 @@
+/*
+ * PXA270-based Zipit Z2 device
+ *
+ * Copyright (c) 2011 by Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * Code is based on mainstone platform.
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "hw/arm-misc.h"
+#include "hw/devices.h"
+#include "hw/i2c.h"
+#include "hw/ssi.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "hw/flash.h"
+#include "sysemu/blockdev.h"
+#include "ui/console.h"
+#include "audio/audio.h"
+#include "exec/address-spaces.h"
+
+#ifdef DEBUG_Z2
+#define DPRINTF(fmt, ...) \
+ printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static struct keymap map[0x100] = {
+ [0 ... 0xff] = { -1, -1 },
+ [0x3b] = {0, 0}, /* Option = F1 */
+ [0xc8] = {0, 1}, /* Up */
+ [0xd0] = {0, 2}, /* Down */
+ [0xcb] = {0, 3}, /* Left */
+ [0xcd] = {0, 4}, /* Right */
+ [0xcf] = {0, 5}, /* End */
+ [0x0d] = {0, 6}, /* KPPLUS */
+ [0xc7] = {1, 0}, /* Home */
+ [0x10] = {1, 1}, /* Q */
+ [0x17] = {1, 2}, /* I */
+ [0x22] = {1, 3}, /* G */
+ [0x2d] = {1, 4}, /* X */
+ [0x1c] = {1, 5}, /* Enter */
+ [0x0c] = {1, 6}, /* KPMINUS */
+ [0xc9] = {2, 0}, /* PageUp */
+ [0x11] = {2, 1}, /* W */
+ [0x18] = {2, 2}, /* O */
+ [0x23] = {2, 3}, /* H */
+ [0x2e] = {2, 4}, /* C */
+ [0x38] = {2, 5}, /* LeftAlt */
+ [0xd1] = {3, 0}, /* PageDown */
+ [0x12] = {3, 1}, /* E */
+ [0x19] = {3, 2}, /* P */
+ [0x24] = {3, 3}, /* J */
+ [0x2f] = {3, 4}, /* V */
+ [0x2a] = {3, 5}, /* LeftShift */
+ [0x01] = {4, 0}, /* Esc */
+ [0x13] = {4, 1}, /* R */
+ [0x1e] = {4, 2}, /* A */
+ [0x25] = {4, 3}, /* K */
+ [0x30] = {4, 4}, /* B */
+ [0x1d] = {4, 5}, /* LeftCtrl */
+ [0x0f] = {5, 0}, /* Tab */
+ [0x14] = {5, 1}, /* T */
+ [0x1f] = {5, 2}, /* S */
+ [0x26] = {5, 3}, /* L */
+ [0x31] = {5, 4}, /* N */
+ [0x39] = {5, 5}, /* Space */
+ [0x3c] = {6, 0}, /* Stop = F2 */
+ [0x15] = {6, 1}, /* Y */
+ [0x20] = {6, 2}, /* D */
+ [0x0e] = {6, 3}, /* Backspace */
+ [0x32] = {6, 4}, /* M */
+ [0x33] = {6, 5}, /* Comma */
+ [0x3d] = {7, 0}, /* Play = F3 */
+ [0x16] = {7, 1}, /* U */
+ [0x21] = {7, 2}, /* F */
+ [0x2c] = {7, 3}, /* Z */
+ [0x27] = {7, 4}, /* Semicolon */
+ [0x34] = {7, 5}, /* Dot */
+};
+
+#define Z2_RAM_SIZE 0x02000000
+#define Z2_FLASH_BASE 0x00000000
+#define Z2_FLASH_SIZE 0x00800000
+
+static struct arm_boot_info z2_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = Z2_RAM_SIZE,
+};
+
+#define Z2_GPIO_SD_DETECT 96
+#define Z2_GPIO_AC_IN 0
+#define Z2_GPIO_KEY_ON 1
+#define Z2_GPIO_LCD_CS 88
+
+typedef struct {
+ SSISlave ssidev;
+ int32_t selected;
+ int32_t enabled;
+ uint8_t buf[3];
+ uint32_t cur_reg;
+ int pos;
+} ZipitLCD;
+
+static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value)
+{
+ ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
+ uint16_t val;
+ if (z->selected) {
+ z->buf[z->pos] = value & 0xff;
+ z->pos++;
+ }
+ if (z->pos == 3) {
+ switch (z->buf[0]) {
+ case 0x74:
+ DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]);
+ z->cur_reg = z->buf[2];
+ break;
+ case 0x76:
+ val = z->buf[1] << 8 | z->buf[2];
+ DPRINTF("%s: value: 0x%.4x\n", __func__, val);
+ if (z->cur_reg == 0x22 && val == 0x0000) {
+ z->enabled = 1;
+ printf("%s: LCD enabled\n", __func__);
+ } else if (z->cur_reg == 0x10 && val == 0x0000) {
+ z->enabled = 0;
+ printf("%s: LCD disabled\n", __func__);
+ }
+ break;
+ default:
+ DPRINTF("%s: unknown command!\n", __func__);
+ break;
+ }
+ z->pos = 0;
+ }
+ return 0;
+}
+
+static void z2_lcd_cs(void *opaque, int line, int level)
+{
+ ZipitLCD *z2_lcd = opaque;
+ z2_lcd->selected = !level;
+}
+
+static int zipit_lcd_init(SSISlave *dev)
+{
+ ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
+ z->selected = 0;
+ z->enabled = 0;
+ z->pos = 0;
+
+ return 0;
+}
+
+static VMStateDescription vmstate_zipit_lcd_state = {
+ .name = "zipit-lcd",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ZipitLCD),
+ VMSTATE_INT32(selected, ZipitLCD),
+ VMSTATE_INT32(enabled, ZipitLCD),
+ VMSTATE_BUFFER(buf, ZipitLCD),
+ VMSTATE_UINT32(cur_reg, ZipitLCD),
+ VMSTATE_INT32(pos, ZipitLCD),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void zipit_lcd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = zipit_lcd_init;
+ k->transfer = zipit_lcd_transfer;
+ dc->vmsd = &vmstate_zipit_lcd_state;
+}
+
+static const TypeInfo zipit_lcd_info = {
+ .name = "zipit-lcd",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(ZipitLCD),
+ .class_init = zipit_lcd_class_init,
+};
+
+typedef struct {
+ I2CSlave i2c;
+ int len;
+ uint8_t buf[3];
+} AER915State;
+
+static int aer915_send(I2CSlave *i2c, uint8_t data)
+{
+ AER915State *s = FROM_I2C_SLAVE(AER915State, i2c);
+ s->buf[s->len] = data;
+ if (s->len++ > 2) {
+ DPRINTF("%s: message too long (%i bytes)\n",
+ __func__, s->len);
+ return 1;
+ }
+
+ if (s->len == 2) {
+ DPRINTF("%s: reg %d value 0x%02x\n", __func__,
+ s->buf[0], s->buf[1]);
+ }
+
+ return 0;
+}
+
+static void aer915_event(I2CSlave *i2c, enum i2c_event event)
+{
+ AER915State *s = FROM_I2C_SLAVE(AER915State, i2c);
+ switch (event) {
+ case I2C_START_SEND:
+ s->len = 0;
+ break;
+ case I2C_START_RECV:
+ if (s->len != 1) {
+ DPRINTF("%s: short message!?\n", __func__);
+ }
+ break;
+ case I2C_FINISH:
+ break;
+ default:
+ break;
+ }
+}
+
+static int aer915_recv(I2CSlave *slave)
+{
+ int retval = 0x00;
+ AER915State *s = FROM_I2C_SLAVE(AER915State, slave);
+
+ switch (s->buf[0]) {
+ /* Return hardcoded battery voltage,
+ * 0xf0 means ~4.1V
+ */
+ case 0x02:
+ retval = 0xf0;
+ break;
+ /* Return 0x00 for other regs,
+ * we don't know what they are for,
+ * anyway they return 0x00 on real hardware.
+ */
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static int aer915_init(I2CSlave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static VMStateDescription vmstate_aer915_state = {
+ .name = "aer915",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(len, AER915State),
+ VMSTATE_BUFFER(buf, AER915State),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void aer915_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->init = aer915_init;
+ k->event = aer915_event;
+ k->recv = aer915_recv;
+ k->send = aer915_send;
+ dc->vmsd = &vmstate_aer915_state;
+}
+
+static const TypeInfo aer915_info = {
+ .name = "aer915",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(AER915State),
+ .class_init = aer915_class_init,
+};
+
+static void z2_init(QEMUMachineInitArgs *args)
+{
+ const char *cpu_model = args->cpu_model;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ uint32_t sector_len = 0x10000;
+ PXA2xxState *mpu;
+ DriveInfo *dinfo;
+ int be;
+ void *z2_lcd;
+ i2c_bus *bus;
+ DeviceState *wm;
+
+ if (!cpu_model) {
+ cpu_model = "pxa270-c5";
+ }
+
+ /* Setup CPU & memory */
+ mpu = pxa270_init(address_space_mem, z2_binfo.ram_size, cpu_model);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "Flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+ if (!pflash_cfi01_register(Z2_FLASH_BASE,
+ NULL, "z2.flash0", Z2_FLASH_SIZE,
+ dinfo->bdrv, sector_len,
+ Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0,
+ be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* setup keypad */
+ pxa27x_register_keypad(mpu->kp, map, 0x100);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(mpu->mmc,
+ NULL,
+ qdev_get_gpio_in(mpu->gpio, Z2_GPIO_SD_DETECT));
+
+ type_register_static(&zipit_lcd_info);
+ type_register_static(&aer915_info);
+ z2_lcd = ssi_create_slave(mpu->ssp[1], "zipit-lcd");
+ bus = pxa2xx_i2c_bus(mpu->i2c[0]);
+ i2c_create_slave(bus, "aer915", 0x55);
+ wm = i2c_create_slave(bus, "wm8750", 0x1b);
+ mpu->i2s->opaque = wm;
+ mpu->i2s->codec_out = wm8750_dac_dat;
+ mpu->i2s->codec_in = wm8750_adc_dat;
+ wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s);
+
+ qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS,
+ qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]);
+
+ if (kernel_filename) {
+ z2_binfo.kernel_filename = kernel_filename;
+ z2_binfo.kernel_cmdline = kernel_cmdline;
+ z2_binfo.initrd_filename = initrd_filename;
+ z2_binfo.board_id = 0x6dd;
+ arm_load_kernel(mpu->cpu, &z2_binfo);
+ }
+}
+
+static QEMUMachine z2_machine = {
+ .name = "z2",
+ .desc = "Zipit Z2 (PXA27x)",
+ .init = z2_init,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void z2_machine_init(void)
+{
+ qemu_register_machine(&z2_machine);
+}
+
+machine_init(z2_machine_init);