summaryrefslogtreecommitdiffstats
path: root/hw/arm
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm')
-rw-r--r--hw/arm/Makefile.objs42
-rw-r--r--hw/arm/armv7m.c284
-rw-r--r--hw/arm/boot.c480
-rw-r--r--hw/arm/collie.c73
-rw-r--r--hw/arm/exynos4210.c349
-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/omap1.c4056
-rw-r--r--hw/arm/omap2.c2684
-rw-r--r--hw/arm/omap_sx1.c238
-rw-r--r--hw/arm/palm.c291
-rw-r--r--hw/arm/pic_cpu.c66
-rw-r--r--hw/arm/pxa2xx.c2291
-rw-r--r--hw/arm/pxa2xx_gpio.c350
-rw-r--r--hw/arm/pxa2xx_pic.c334
-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
29 files changed, 20966 insertions, 21 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 0e7df60..2d9c69d 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,37 +1,37 @@
-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 += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
-obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
+obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o
+obj-y += exynos4210_gic.o exynos4210_combiner.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 += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
+obj-y += armv7m_nvic.o stellaris_enet.o
+obj-y += 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 += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
+obj-y += zaurus.o ide/microdrive.o tc6393xb.o
+obj-y += 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 \
+obj-y += 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-$(CONFIG_FDT) += ../device_tree.o
obj-$(CONFIG_KVM) += kvm/arm_gic.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
+
+obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
+obj-y += omap1.o omap2.o
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
new file mode 100644
index 0000000..1d5bb59
--- /dev/null
+++ b/hw/arm/armv7m.c
@@ -0,0 +1,284 @@
+/*
+ * ARMV7M 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/loader.h"
+#include "elf.h"
+
+/* Bitbanded IO. Each word corresponds to a single bit. */
+
+/* Get the byte address of the real memory for a bitband access. */
+static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
+{
+ uint32_t res;
+
+ res = *(uint32_t *)opaque;
+ res |= (addr & 0x1ffffff) >> 5;
+ return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, hwaddr offset)
+{
+ uint8_t v;
+ cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
+ return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint8_t mask;
+ uint8_t v;
+ addr = bitband_addr(opaque, offset);
+ mask = (1 << ((offset >> 2) & 7));
+ cpu_physical_memory_read(addr, &v, 1);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, hwaddr offset)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(opaque, offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(opaque, offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, hwaddr offset)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(opaque, offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(opaque, offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static const MemoryRegionOps bitband_ops = {
+ .old_mmio = {
+ .read = { bitband_readb, bitband_readw, bitband_readl, },
+ .write = { bitband_writeb, bitband_writew, bitband_writel, },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t base;
+} BitBandState;
+
+static int bitband_init(SysBusDevice *dev)
+{
+ BitBandState *s = FROM_SYSBUS(BitBandState, dev);
+
+ memory_region_init_io(&s->iomem, &bitband_ops, &s->base, "bitband",
+ 0x02000000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void armv7m_bitband_init(void)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "ARM,bitband-memory");
+ qdev_prop_set_uint32(dev, "base", 0x20000000);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000);
+
+ dev = qdev_create(NULL, "ARM,bitband-memory");
+ qdev_prop_set_uint32(dev, "base", 0x40000000);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000);
+}
+
+/* Board init. */
+
+static void armv7m_reset(void *opaque)
+{
+ ARMCPU *cpu = opaque;
+
+ cpu_reset(CPU(cpu));
+}
+
+/* Init CPU and memory for a v7-M based board.
+ flash_size and sram_size are in kb.
+ Returns the NVIC array. */
+
+qemu_irq *armv7m_init(MemoryRegion *address_space_mem,
+ int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model)
+{
+ ARMCPU *cpu;
+ CPUARMState *env;
+ DeviceState *nvic;
+ /* FIXME: make this local state. */
+ static qemu_irq pic[64];
+ qemu_irq *cpu_pic;
+ int image_size;
+ uint64_t entry;
+ uint64_t lowaddr;
+ int i;
+ int big_endian;
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ MemoryRegion *flash = g_new(MemoryRegion, 1);
+ MemoryRegion *hack = g_new(MemoryRegion, 1);
+
+ flash_size *= 1024;
+ sram_size *= 1024;
+
+ if (cpu_model == NULL) {
+ cpu_model = "cortex-m3";
+ }
+ cpu = cpu_arm_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+#if 0
+ /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+ We don't have proper commandline options, so allocate half of memory
+ as SRAM, up to a maximum of 32Mb, and the rest as code. */
+ if (ram_size > (512 + 32) * 1024 * 1024)
+ ram_size = (512 + 32) * 1024 * 1024;
+ sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+ if (sram_size > 32 * 1024 * 1024)
+ sram_size = 32 * 1024 * 1024;
+ code_size = ram_size - sram_size;
+#endif
+
+ /* Flash programming is done via the SCU, so pretend it is ROM. */
+ memory_region_init_ram(flash, "armv7m.flash", flash_size);
+ vmstate_register_ram_global(flash);
+ memory_region_set_readonly(flash, true);
+ memory_region_add_subregion(address_space_mem, 0, flash);
+ memory_region_init_ram(sram, "armv7m.sram", sram_size);
+ vmstate_register_ram_global(sram);
+ memory_region_add_subregion(address_space_mem, 0x20000000, sram);
+ armv7m_bitband_init();
+
+ nvic = qdev_create(NULL, "armv7m_nvic");
+ env->nvic = nvic;
+ qdev_init_nofail(nvic);
+ cpu_pic = arm_pic_init_cpu(cpu);
+ sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]);
+ for (i = 0; i < 64; i++) {
+ pic[i] = qdev_get_gpio_in(nvic, i);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ if (!kernel_filename) {
+ fprintf(stderr, "Guest image must be specified (using -kernel)\n");
+ exit(1);
+ }
+
+ image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
+ NULL, big_endian, ELF_MACHINE, 1);
+ if (image_size < 0) {
+ image_size = load_image_targphys(kernel_filename, 0, flash_size);
+ lowaddr = 0;
+ }
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* Hack to map an additional page of ram at the top of the address
+ space. This stops qemu complaining about executing code outside RAM
+ when returning from an exception. */
+ memory_region_init_ram(hack, "armv7m.hack", 0x1000);
+ vmstate_register_ram_global(hack);
+ memory_region_add_subregion(address_space_mem, 0xfffff000, hack);
+
+ qemu_register_reset(armv7m_reset, cpu);
+ return pic;
+}
+
+static Property bitband_properties[] = {
+ DEFINE_PROP_UINT32("base", BitBandState, base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bitband_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = bitband_init;
+ dc->props = bitband_properties;
+}
+
+static const TypeInfo bitband_info = {
+ .name = "ARM,bitband-memory",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BitBandState),
+ .class_init = bitband_class_init,
+};
+
+static void armv7m_register_types(void)
+{
+ type_register_static(&bitband_info);
+}
+
+type_init(armv7m_register_types)
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/exynos4210.c b/hw/arm/exynos4210.c
new file mode 100644
index 0000000..4592514
--- /dev/null
+++ b/hw/arm/exynos4210.c
@@ -0,0 +1,349 @@
+/*
+ * Samsung exynos4210 SoC emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/arm-misc.h"
+#include "hw/loader.h"
+#include "hw/exynos4210.h"
+#include "hw/usb/hcd-ehci.h"
+
+#define EXYNOS4210_CHIPID_ADDR 0x10000000
+
+/* PWM */
+#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000
+
+/* RTC */
+#define EXYNOS4210_RTC_BASE_ADDR 0x10070000
+
+/* MCT */
+#define EXYNOS4210_MCT_BASE_ADDR 0x10050000
+
+/* I2C */
+#define EXYNOS4210_I2C_SHIFT 0x00010000
+#define EXYNOS4210_I2C_BASE_ADDR 0x13860000
+/* Interrupt Group of External Interrupt Combiner for I2C */
+#define EXYNOS4210_I2C_INTG 27
+#define EXYNOS4210_HDMI_INTG 16
+
+/* UART's definitions */
+#define EXYNOS4210_UART0_BASE_ADDR 0x13800000
+#define EXYNOS4210_UART1_BASE_ADDR 0x13810000
+#define EXYNOS4210_UART2_BASE_ADDR 0x13820000
+#define EXYNOS4210_UART3_BASE_ADDR 0x13830000
+#define EXYNOS4210_UART0_FIFO_SIZE 256
+#define EXYNOS4210_UART1_FIFO_SIZE 64
+#define EXYNOS4210_UART2_FIFO_SIZE 16
+#define EXYNOS4210_UART3_FIFO_SIZE 16
+/* Interrupt Group of External Interrupt Combiner for UART */
+#define EXYNOS4210_UART_INT_GRP 26
+
+/* External GIC */
+#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR 0x10480000
+#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR 0x10490000
+
+/* Combiner */
+#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000
+#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000
+
+/* PMU SFR base address */
+#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
+
+/* Display controllers (FIMD) */
+#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
+
+/* EHCI */
+#define EXYNOS4210_EHCI_BASE_ADDR 0x12580000
+
+static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
+ 0x09, 0x00, 0x00, 0x00 };
+
+void exynos4210_write_secondary(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ int n;
+ uint32_t smpboot[] = {
+ 0xe59f3034, /* ldr r3, External gic_cpu_if */
+ 0xe59f2034, /* ldr r2, Internal gic_cpu_if */
+ 0xe59f0034, /* ldr r0, startaddr */
+ 0xe3a01001, /* mov r1, #1 */
+ 0xe5821000, /* str r1, [r2] */
+ 0xe5831000, /* str r1, [r3] */
+ 0xe3a010ff, /* mov r1, #0xff */
+ 0xe5821004, /* str r1, [r2, #4] */
+ 0xe5831004, /* str r1, [r3, #4] */
+ 0xf57ff04f, /* dsb */
+ 0xe320f003, /* wfi */
+ 0xe5901000, /* ldr r1, [r0] */
+ 0xe1110001, /* tst r1, r1 */
+ 0x0afffffb, /* beq <wfi> */
+ 0xe12fff11, /* bx r1 */
+ EXYNOS4210_EXT_GIC_CPU_BASE_ADDR,
+ 0, /* gic_cpu_if: base address of Internal GIC CPU interface */
+ 0 /* bootreg: Boot register address is held here */
+ };
+ 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++) {
+ smpboot[n] = tswap32(smpboot[n]);
+ }
+ rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+}
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+ unsigned long ram_size)
+{
+ qemu_irq cpu_irq[EXYNOS4210_NCPUS];
+ int i, n;
+ Exynos4210State *s = g_new(Exynos4210State, 1);
+ qemu_irq *irqp;
+ qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS];
+ unsigned long mem_size;
+ DeviceState *dev;
+ SysBusDevice *busdev;
+
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ s->cpu[n] = cpu_arm_init("cortex-a9");
+ if (!s->cpu[n]) {
+ fprintf(stderr, "Unable to find CPU %d definition\n", n);
+ exit(1);
+ }
+
+ /* Create PIC controller for each processor instance */
+ irqp = arm_pic_init_cpu(s->cpu[n]);
+
+ /*
+ * Get GICs gpio_in cpu_irq to connect a combiner to them later.
+ * Use only IRQ for a while.
+ */
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ /*** IRQs ***/
+
+ s->irq_table = exynos4210_init_irq(&s->irqs);
+
+ /* IRQ Gate */
+ for (i = 0; i < EXYNOS4210_NCPUS; i++) {
+ dev = qdev_create(NULL, "exynos4210.irq_gate");
+ qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS);
+ qdev_init_nofail(dev);
+ /* Get IRQ Gate input in gate_irq */
+ for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) {
+ gate_irq[i][n] = qdev_get_gpio_in(dev, n);
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+
+ /* Connect IRQ Gate output to cpu_irq */
+ sysbus_connect_irq(busdev, 0, cpu_irq[i]);
+ }
+
+ /* Private memory region and Internal GIC */
+ dev = qdev_create(NULL, "a9mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n][0]);
+ }
+ for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
+ s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* Cache controller */
+ sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL);
+
+ /* External GIC */
+ dev = qdev_create(NULL, "exynos4210.gic");
+ qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ /* Map CPU interface */
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR);
+ /* Map Distributer interface */
+ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR);
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n][1]);
+ }
+ for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
+ s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* Internal Interrupt Combiner */
+ dev = qdev_create(NULL, "exynos4210.combiner");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+ sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(&s->irqs, dev, 0);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
+
+ /* External Interrupt Combiner */
+ dev = qdev_create(NULL, "exynos4210.combiner");
+ qdev_prop_set_uint32(dev, "external", 1);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+ sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(&s->irqs, dev, 1);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
+
+ /* Initialize board IRQs. */
+ exynos4210_init_board_irqs(&s->irqs);
+
+ /*** Memory ***/
+
+ /* Chip-ID and OMR */
+ memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid",
+ sizeof(chipid_and_omr), chipid_and_omr);
+ memory_region_set_readonly(&s->chipid_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR,
+ &s->chipid_mem);
+
+ /* Internal ROM */
+ memory_region_init_ram(&s->irom_mem, "exynos4210.irom",
+ EXYNOS4210_IROM_SIZE);
+ memory_region_set_readonly(&s->irom_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
+ &s->irom_mem);
+ /* mirror of iROM */
+ memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
+ &s->irom_mem,
+ 0,
+ EXYNOS4210_IROM_SIZE);
+ memory_region_set_readonly(&s->irom_alias_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
+ &s->irom_alias_mem);
+
+ /* Internal RAM */
+ memory_region_init_ram(&s->iram_mem, "exynos4210.iram",
+ EXYNOS4210_IRAM_SIZE);
+ vmstate_register_ram_global(&s->iram_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
+ &s->iram_mem);
+
+ /* DRAM */
+ mem_size = ram_size;
+ if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
+ memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1",
+ mem_size - EXYNOS4210_DRAM_MAX_SIZE);
+ vmstate_register_ram_global(&s->dram1_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
+ &s->dram1_mem);
+ mem_size = EXYNOS4210_DRAM_MAX_SIZE;
+ }
+ memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size);
+ vmstate_register_ram_global(&s->dram0_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
+ &s->dram0_mem);
+
+ /* PMU.
+ * The only reason of existence at the moment is that secondary CPU boot
+ * loader uses PMU INFORM5 register as a holding pen.
+ */
+ sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
+
+ /* PWM */
+ sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(22, 0)],
+ s->irq_table[exynos4210_get_irq(22, 1)],
+ s->irq_table[exynos4210_get_irq(22, 2)],
+ s->irq_table[exynos4210_get_irq(22, 3)],
+ s->irq_table[exynos4210_get_irq(22, 4)],
+ NULL);
+ /* RTC */
+ sysbus_create_varargs("exynos4210.rtc", EXYNOS4210_RTC_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(23, 0)],
+ s->irq_table[exynos4210_get_irq(23, 1)],
+ NULL);
+
+ /* Multi Core Timer */
+ dev = qdev_create(NULL, "exynos4210.mct");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ for (n = 0; n < 4; n++) {
+ /* Connect global timer interrupts to Combiner gpio_in */
+ sysbus_connect_irq(busdev, n,
+ s->irq_table[exynos4210_get_irq(1, 4 + n)]);
+ }
+ /* Connect local timer interrupts to Combiner gpio_in */
+ sysbus_connect_irq(busdev, 4,
+ s->irq_table[exynos4210_get_irq(51, 0)]);
+ sysbus_connect_irq(busdev, 5,
+ s->irq_table[exynos4210_get_irq(35, 3)]);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR);
+
+ /*** I2C ***/
+ for (n = 0; n < EXYNOS4210_I2C_NUMBER; n++) {
+ uint32_t addr = EXYNOS4210_I2C_BASE_ADDR + EXYNOS4210_I2C_SHIFT * n;
+ qemu_irq i2c_irq;
+
+ if (n < 8) {
+ i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_I2C_INTG, n)];
+ } else {
+ i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_HDMI_INTG, 1)];
+ }
+
+ dev = qdev_create(NULL, "exynos4210.i2c");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_connect_irq(busdev, 0, i2c_irq);
+ sysbus_mmio_map(busdev, 0, addr);
+ s->i2c_if[n] = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ }
+
+
+ /*** UARTs ***/
+ exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
+ EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR,
+ EXYNOS4210_UART1_FIFO_SIZE, 1, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR,
+ EXYNOS4210_UART2_FIFO_SIZE, 2, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR,
+ EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
+
+ /*** Display controller (FIMD) ***/
+ sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(11, 0)],
+ s->irq_table[exynos4210_get_irq(11, 1)],
+ s->irq_table[exynos4210_get_irq(11, 2)],
+ NULL);
+
+ sysbus_create_simple(TYPE_EXYNOS4210_EHCI, EXYNOS4210_EHCI_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(28, 3)]);
+
+ return s;
+}
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/omap1.c b/hw/arm/omap1.c
new file mode 100644
index 0000000..6f0a8ca
--- /dev/null
+++ b/hw/arm/omap1.c
@@ -0,0 +1,4056 @@
+/*
+ * TI OMAP processors emulation.
+ *
+ * Copyright (C) 2006-2008 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 "hw/arm-misc.h"
+#include "hw/omap.h"
+#include "sysemu/sysemu.h"
+#include "hw/soc_dma.h"
+#include "sysemu/blockdev.h"
+#include "qemu/range.h"
+#include "hw/sysbus.h"
+
+/* Should signal the TCMI/GPMC */
+uint32_t omap_badwidth_read8(void *opaque, hwaddr addr)
+{
+ uint8_t ret;
+
+ OMAP_8B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 1);
+ return ret;
+}
+
+void omap_badwidth_write8(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ uint8_t val8 = value;
+
+ OMAP_8B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &val8, 1);
+}
+
+uint32_t omap_badwidth_read16(void *opaque, hwaddr addr)
+{
+ uint16_t ret;
+
+ OMAP_16B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 2);
+ return ret;
+}
+
+void omap_badwidth_write16(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ uint16_t val16 = value;
+
+ OMAP_16B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &val16, 2);
+}
+
+uint32_t omap_badwidth_read32(void *opaque, hwaddr addr)
+{
+ uint32_t ret;
+
+ OMAP_32B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 4);
+ return ret;
+}
+
+void omap_badwidth_write32(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ OMAP_32B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &value, 4);
+}
+
+/* MPU OS timers */
+struct omap_mpu_timer_s {
+ MemoryRegion iomem;
+ qemu_irq irq;
+ omap_clk clk;
+ uint32_t val;
+ int64_t time;
+ QEMUTimer *timer;
+ QEMUBH *tick;
+ int64_t rate;
+ int it_ena;
+
+ int enable;
+ int ptv;
+ int ar;
+ int st;
+ uint32_t reset_val;
+};
+
+static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer)
+{
+ uint64_t distance = qemu_get_clock_ns(vm_clock) - timer->time;
+
+ if (timer->st && timer->enable && timer->rate)
+ return timer->val - muldiv64(distance >> (timer->ptv + 1),
+ timer->rate, get_ticks_per_sec());
+ else
+ return timer->val;
+}
+
+static inline void omap_timer_sync(struct omap_mpu_timer_s *timer)
+{
+ timer->val = omap_timer_read(timer);
+ timer->time = qemu_get_clock_ns(vm_clock);
+}
+
+static inline void omap_timer_update(struct omap_mpu_timer_s *timer)
+{
+ int64_t expires;
+
+ if (timer->enable && timer->st && timer->rate) {
+ timer->val = timer->reset_val; /* Should skip this on clk enable */
+ expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1),
+ get_ticks_per_sec(), timer->rate);
+
+ /* If timer expiry would be sooner than in about 1 ms and
+ * auto-reload isn't set, then fire immediately. This is a hack
+ * to make systems like PalmOS run in acceptable time. PalmOS
+ * sets the interval to a very low value and polls the status bit
+ * in a busy loop when it wants to sleep just a couple of CPU
+ * ticks. */
+ if (expires > (get_ticks_per_sec() >> 10) || timer->ar)
+ qemu_mod_timer(timer->timer, timer->time + expires);
+ else
+ qemu_bh_schedule(timer->tick);
+ } else
+ qemu_del_timer(timer->timer);
+}
+
+static void omap_timer_fire(void *opaque)
+{
+ struct omap_mpu_timer_s *timer = opaque;
+
+ if (!timer->ar) {
+ timer->val = 0;
+ timer->st = 0;
+ }
+
+ if (timer->it_ena)
+ /* Edge-triggered irq */
+ qemu_irq_pulse(timer->irq);
+}
+
+static void omap_timer_tick(void *opaque)
+{
+ struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+
+ omap_timer_sync(timer);
+ omap_timer_fire(timer);
+ omap_timer_update(timer);
+}
+
+static void omap_timer_clk_update(void *opaque, int line, int on)
+{
+ struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+
+ omap_timer_sync(timer);
+ timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
+ omap_timer_update(timer);
+}
+
+static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer)
+{
+ omap_clk_adduser(timer->clk,
+ qemu_allocate_irqs(omap_timer_clk_update, timer, 1)[0]);
+ timer->rate = omap_clk_getrate(timer->clk);
+}
+
+static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st;
+
+ case 0x04: /* LOAD_TIM */
+ break;
+
+ case 0x08: /* READ_TIM */
+ return omap_timer_read(s);
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpu_timer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ omap_timer_sync(s);
+ s->enable = (value >> 5) & 1;
+ s->ptv = (value >> 2) & 7;
+ s->ar = (value >> 1) & 1;
+ s->st = value & 1;
+ omap_timer_update(s);
+ return;
+
+ case 0x04: /* LOAD_TIM */
+ s->reset_val = value;
+ return;
+
+ case 0x08: /* READ_TIM */
+ OMAP_RO_REG(addr);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_mpu_timer_ops = {
+ .read = omap_mpu_timer_read,
+ .write = omap_mpu_timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void omap_mpu_timer_reset(struct omap_mpu_timer_s *s)
+{
+ qemu_del_timer(s->timer);
+ s->enable = 0;
+ s->reset_val = 31337;
+ s->val = 0;
+ s->ptv = 0;
+ s->ar = 0;
+ s->st = 0;
+ s->it_ena = 1;
+}
+
+static struct omap_mpu_timer_s *omap_mpu_timer_init(MemoryRegion *system_memory,
+ hwaddr base,
+ qemu_irq irq, omap_clk clk)
+{
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *)
+ g_malloc0(sizeof(struct omap_mpu_timer_s));
+
+ s->irq = irq;
+ s->clk = clk;
+ s->timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, s);
+ s->tick = qemu_bh_new(omap_timer_fire, s);
+ omap_mpu_timer_reset(s);
+ omap_timer_clk_setup(s);
+
+ memory_region_init_io(&s->iomem, &omap_mpu_timer_ops, s,
+ "omap-mpu-timer", 0x100);
+
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ return s;
+}
+
+/* Watchdog timer */
+struct omap_watchdog_timer_s {
+ struct omap_mpu_timer_s timer;
+ MemoryRegion iomem;
+ uint8_t last_wr;
+ int mode;
+ int free;
+ int reset;
+};
+
+static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ return (s->timer.ptv << 9) | (s->timer.ar << 8) |
+ (s->timer.st << 7) | (s->free << 1);
+
+ case 0x04: /* READ_TIMER */
+ return omap_timer_read(&s->timer);
+
+ case 0x08: /* TIMER_MODE */
+ return s->mode << 15;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_wd_timer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ omap_timer_sync(&s->timer);
+ s->timer.ptv = (value >> 9) & 7;
+ s->timer.ar = (value >> 8) & 1;
+ s->timer.st = (value >> 7) & 1;
+ s->free = (value >> 1) & 1;
+ omap_timer_update(&s->timer);
+ break;
+
+ case 0x04: /* LOAD_TIMER */
+ s->timer.reset_val = value & 0xffff;
+ break;
+
+ case 0x08: /* TIMER_MODE */
+ if (!s->mode && ((value >> 15) & 1))
+ omap_clk_get(s->timer.clk);
+ s->mode |= (value >> 15) & 1;
+ if (s->last_wr == 0xf5) {
+ if ((value & 0xff) == 0xa0) {
+ if (s->mode) {
+ s->mode = 0;
+ omap_clk_put(s->timer.clk);
+ }
+ } else {
+ /* XXX: on T|E hardware somehow this has no effect,
+ * on Zire 71 it works as specified. */
+ s->reset = 1;
+ qemu_system_reset_request();
+ }
+ }
+ s->last_wr = value & 0xff;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_wd_timer_ops = {
+ .read = omap_wd_timer_read,
+ .write = omap_wd_timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_wd_timer_reset(struct omap_watchdog_timer_s *s)
+{
+ qemu_del_timer(s->timer.timer);
+ if (!s->mode)
+ omap_clk_get(s->timer.clk);
+ s->mode = 1;
+ s->free = 1;
+ s->reset = 0;
+ s->timer.enable = 1;
+ s->timer.it_ena = 1;
+ s->timer.reset_val = 0xffff;
+ s->timer.val = 0;
+ s->timer.st = 0;
+ s->timer.ptv = 0;
+ s->timer.ar = 0;
+ omap_timer_update(&s->timer);
+}
+
+static struct omap_watchdog_timer_s *omap_wd_timer_init(MemoryRegion *memory,
+ hwaddr base,
+ qemu_irq irq, omap_clk clk)
+{
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *)
+ g_malloc0(sizeof(struct omap_watchdog_timer_s));
+
+ s->timer.irq = irq;
+ s->timer.clk = clk;
+ s->timer.timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, &s->timer);
+ omap_wd_timer_reset(s);
+ omap_timer_clk_setup(&s->timer);
+
+ memory_region_init_io(&s->iomem, &omap_wd_timer_ops, s,
+ "omap-wd-timer", 0x100);
+ memory_region_add_subregion(memory, base, &s->iomem);
+
+ return s;
+}
+
+/* 32-kHz timer */
+struct omap_32khz_timer_s {
+ struct omap_mpu_timer_s timer;
+ MemoryRegion iomem;
+};
+
+static uint64_t omap_os_timer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* TVR */
+ return s->timer.reset_val;
+
+ case 0x04: /* TCR */
+ return omap_timer_read(&s->timer);
+
+ case 0x08: /* CR */
+ return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_os_timer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* TVR */
+ s->timer.reset_val = value & 0x00ffffff;
+ break;
+
+ case 0x04: /* TCR */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x08: /* CR */
+ s->timer.ar = (value >> 3) & 1;
+ s->timer.it_ena = (value >> 2) & 1;
+ if (s->timer.st != (value & 1) || (value & 2)) {
+ omap_timer_sync(&s->timer);
+ s->timer.enable = value & 1;
+ s->timer.st = value & 1;
+ omap_timer_update(&s->timer);
+ }
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_os_timer_ops = {
+ .read = omap_os_timer_read,
+ .write = omap_os_timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_os_timer_reset(struct omap_32khz_timer_s *s)
+{
+ qemu_del_timer(s->timer.timer);
+ s->timer.enable = 0;
+ s->timer.it_ena = 0;
+ s->timer.reset_val = 0x00ffffff;
+ s->timer.val = 0;
+ s->timer.st = 0;
+ s->timer.ptv = 0;
+ s->timer.ar = 1;
+}
+
+static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory,
+ hwaddr base,
+ qemu_irq irq, omap_clk clk)
+{
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *)
+ g_malloc0(sizeof(struct omap_32khz_timer_s));
+
+ s->timer.irq = irq;
+ s->timer.clk = clk;
+ s->timer.timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, &s->timer);
+ omap_os_timer_reset(s);
+ omap_timer_clk_setup(&s->timer);
+
+ memory_region_init_io(&s->iomem, &omap_os_timer_ops, s,
+ "omap-os-timer", 0x800);
+ memory_region_add_subregion(memory, base, &s->iomem);
+
+ return s;
+}
+
+/* Ultra Low-Power Device Module */
+static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t ret;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x14: /* IT_STATUS */
+ ret = s->ulpd_pm_regs[addr >> 2];
+ s->ulpd_pm_regs[addr >> 2] = 0;
+ qemu_irq_lower(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K));
+ return ret;
+
+ case 0x18: /* Reserved */
+ case 0x1c: /* Reserved */
+ case 0x20: /* Reserved */
+ case 0x28: /* Reserved */
+ case 0x2c: /* Reserved */
+ OMAP_BAD_REG(addr);
+ /* fall through */
+ case 0x00: /* COUNTER_32_LSB */
+ case 0x04: /* COUNTER_32_MSB */
+ case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+ case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+ case 0x10: /* GAUGING_CTRL */
+ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+ case 0x30: /* CLOCK_CTRL */
+ case 0x34: /* SOFT_REQ */
+ case 0x38: /* COUNTER_32_FIQ */
+ case 0x3c: /* DPLL_CTRL */
+ case 0x40: /* STATUS_REQ */
+ /* XXX: check clk::usecount state for every clock */
+ case 0x48: /* LOCL_TIME */
+ case 0x4c: /* APLL_CTRL */
+ case 0x50: /* POWER_CTRL */
+ return s->ulpd_pm_regs[addr >> 2];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ if (diff & (1 << 4)) /* USB_MCLK_EN */
+ omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1);
+ if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */
+ omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1);
+}
+
+static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ if (diff & (1 << 0)) /* SOFT_DPLL_REQ */
+ omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1);
+ if (diff & (1 << 1)) /* SOFT_COM_REQ */
+ omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1);
+ if (diff & (1 << 2)) /* SOFT_SDW_REQ */
+ omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1);
+ if (diff & (1 << 3)) /* SOFT_USB_REQ */
+ omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1);
+}
+
+static void omap_ulpd_pm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int64_t now, ticks;
+ int div, mult;
+ static const int bypass_div[4] = { 1, 2, 4, 4 };
+ uint16_t diff;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* COUNTER_32_LSB */
+ case 0x04: /* COUNTER_32_MSB */
+ case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+ case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+ case 0x14: /* IT_STATUS */
+ case 0x40: /* STATUS_REQ */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* GAUGING_CTRL */
+ /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */
+ if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) {
+ now = qemu_get_clock_ns(vm_clock);
+
+ if (value & 1)
+ s->ulpd_gauge_start = now;
+ else {
+ now -= s->ulpd_gauge_start;
+
+ /* 32-kHz ticks */
+ ticks = muldiv64(now, 32768, get_ticks_per_sec());
+ s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff;
+ s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff;
+ if (ticks >> 32) /* OVERFLOW_32K */
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2;
+
+ /* High frequency ticks */
+ ticks = muldiv64(now, 12000000, get_ticks_per_sec());
+ s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff;
+ s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff;
+ if (ticks >> 32) /* OVERFLOW_HI_FREQ */
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1;
+
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */
+ qemu_irq_raise(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K));
+ }
+ }
+ s->ulpd_pm_regs[addr >> 2] = value;
+ break;
+
+ case 0x18: /* Reserved */
+ case 0x1c: /* Reserved */
+ case 0x20: /* Reserved */
+ case 0x28: /* Reserved */
+ case 0x2c: /* Reserved */
+ OMAP_BAD_REG(addr);
+ /* fall through */
+ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+ case 0x38: /* COUNTER_32_FIQ */
+ case 0x48: /* LOCL_TIME */
+ case 0x50: /* POWER_CTRL */
+ s->ulpd_pm_regs[addr >> 2] = value;
+ break;
+
+ case 0x30: /* CLOCK_CTRL */
+ diff = s->ulpd_pm_regs[addr >> 2] ^ value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x3f;
+ omap_ulpd_clk_update(s, diff, value);
+ break;
+
+ case 0x34: /* SOFT_REQ */
+ diff = s->ulpd_pm_regs[addr >> 2] ^ value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x1f;
+ omap_ulpd_req_update(s, diff, value);
+ break;
+
+ case 0x3c: /* DPLL_CTRL */
+ /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is
+ * omitted altogether, probably a typo. */
+ /* This register has identical semantics with DPLL(1:3) control
+ * registers, see omap_dpll_write() */
+ diff = s->ulpd_pm_regs[addr >> 2] & value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x2fff;
+ if (diff & (0x3ff << 2)) {
+ if (value & (1 << 4)) { /* PLL_ENABLE */
+ div = ((value >> 5) & 3) + 1; /* PLL_DIV */
+ mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */
+ } else {
+ div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */
+ mult = 1;
+ }
+ omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult);
+ }
+
+ /* Enter the desired mode. */
+ s->ulpd_pm_regs[addr >> 2] =
+ (s->ulpd_pm_regs[addr >> 2] & 0xfffe) |
+ ((s->ulpd_pm_regs[addr >> 2] >> 4) & 1);
+
+ /* Act as if the lock is restored. */
+ s->ulpd_pm_regs[addr >> 2] |= 2;
+ break;
+
+ case 0x4c: /* APLL_CTRL */
+ diff = s->ulpd_pm_regs[addr >> 2] & value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0xf;
+ if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */
+ omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s,
+ (value & (1 << 0)) ? "apll" : "dpll4"));
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_ulpd_pm_ops = {
+ .read = omap_ulpd_pm_read,
+ .write = omap_ulpd_pm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_ulpd_pm_reset(struct omap_mpu_state_s *mpu)
+{
+ mpu->ulpd_pm_regs[0x00 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x04 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x08 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x0c >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x10 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x18 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x1c >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x20 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x24 >> 2] = 0x03ff;
+ mpu->ulpd_pm_regs[0x28 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x2c >> 2] = 0x01;
+ omap_ulpd_clk_update(mpu, mpu->ulpd_pm_regs[0x30 >> 2], 0x0000);
+ mpu->ulpd_pm_regs[0x30 >> 2] = 0x0000;
+ omap_ulpd_req_update(mpu, mpu->ulpd_pm_regs[0x34 >> 2], 0x0000);
+ mpu->ulpd_pm_regs[0x34 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x38 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x3c >> 2] = 0x2211;
+ mpu->ulpd_pm_regs[0x40 >> 2] = 0x0000; /* FIXME: dump a real STATUS_REQ */
+ mpu->ulpd_pm_regs[0x48 >> 2] = 0x960;
+ mpu->ulpd_pm_regs[0x4c >> 2] = 0x08;
+ mpu->ulpd_pm_regs[0x50 >> 2] = 0x08;
+ omap_clk_setrate(omap_findclk(mpu, "dpll4"), 1, 4);
+ omap_clk_reparent(omap_findclk(mpu, "ck_48m"), omap_findclk(mpu, "dpll4"));
+}
+
+static void omap_ulpd_pm_init(MemoryRegion *system_memory,
+ hwaddr base,
+ struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->ulpd_pm_iomem, &omap_ulpd_pm_ops, mpu,
+ "omap-ulpd-pm", 0x800);
+ memory_region_add_subregion(system_memory, base, &mpu->ulpd_pm_iomem);
+ omap_ulpd_pm_reset(mpu);
+}
+
+/* OMAP Pin Configuration */
+static uint64_t omap_pin_cfg_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* FUNC_MUX_CTRL_0 */
+ case 0x04: /* FUNC_MUX_CTRL_1 */
+ case 0x08: /* FUNC_MUX_CTRL_2 */
+ return s->func_mux_ctrl[addr >> 2];
+
+ case 0x0c: /* COMP_MODE_CTRL_0 */
+ return s->comp_mode_ctrl[0];
+
+ case 0x10: /* FUNC_MUX_CTRL_3 */
+ case 0x14: /* FUNC_MUX_CTRL_4 */
+ case 0x18: /* FUNC_MUX_CTRL_5 */
+ case 0x1c: /* FUNC_MUX_CTRL_6 */
+ case 0x20: /* FUNC_MUX_CTRL_7 */
+ case 0x24: /* FUNC_MUX_CTRL_8 */
+ case 0x28: /* FUNC_MUX_CTRL_9 */
+ case 0x2c: /* FUNC_MUX_CTRL_A */
+ case 0x30: /* FUNC_MUX_CTRL_B */
+ case 0x34: /* FUNC_MUX_CTRL_C */
+ case 0x38: /* FUNC_MUX_CTRL_D */
+ return s->func_mux_ctrl[(addr >> 2) - 1];
+
+ case 0x40: /* PULL_DWN_CTRL_0 */
+ case 0x44: /* PULL_DWN_CTRL_1 */
+ case 0x48: /* PULL_DWN_CTRL_2 */
+ case 0x4c: /* PULL_DWN_CTRL_3 */
+ return s->pull_dwn_ctrl[(addr & 0xf) >> 2];
+
+ case 0x50: /* GATE_INH_CTRL_0 */
+ return s->gate_inh_ctrl[0];
+
+ case 0x60: /* VOLTAGE_CTRL_0 */
+ return s->voltage_ctrl[0];
+
+ case 0x70: /* TEST_DBG_CTRL_0 */
+ return s->test_dbg_ctrl[0];
+
+ case 0x80: /* MOD_CONF_CTRL_0 */
+ return s->mod_conf_ctrl[0];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (s->compat1509) {
+ if (diff & (1 << 9)) /* BLUETOOTH */
+ omap_clk_onoff(omap_findclk(s, "bt_mclk_out"),
+ (~value >> 9) & 1);
+ if (diff & (1 << 7)) /* USB.CLKO */
+ omap_clk_onoff(omap_findclk(s, "usb.clko"),
+ (value >> 7) & 1);
+ }
+}
+
+static inline void omap_pin_funcmux1_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (s->compat1509) {
+ if (diff & (1 << 31)) /* MCBSP3_CLK_HIZ_DI */
+ omap_clk_onoff(omap_findclk(s, "mcbsp3.clkx"),
+ (value >> 31) & 1);
+ if (diff & (1 << 1)) /* CLK32K */
+ omap_clk_onoff(omap_findclk(s, "clk32k_out"),
+ (~value >> 1) & 1);
+ }
+}
+
+static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (diff & (1 << 31)) /* CONF_MOD_UART3_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart3_ck"),
+ omap_findclk(s, ((value >> 31) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart2_ck"),
+ omap_findclk(s, ((value >> 30) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart1_ck"),
+ omap_findclk(s, ((value >> 29) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */
+ omap_clk_reparent(omap_findclk(s, "mmc_ck"),
+ omap_findclk(s, ((value >> 23) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */
+ omap_clk_reparent(omap_findclk(s, "com_mclk_out"),
+ omap_findclk(s, ((value >> 12) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */
+ omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1);
+}
+
+static void omap_pin_cfg_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint32_t diff;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* FUNC_MUX_CTRL_0 */
+ diff = s->func_mux_ctrl[addr >> 2] ^ value;
+ s->func_mux_ctrl[addr >> 2] = value;
+ omap_pin_funcmux0_update(s, diff, value);
+ return;
+
+ case 0x04: /* FUNC_MUX_CTRL_1 */
+ diff = s->func_mux_ctrl[addr >> 2] ^ value;
+ s->func_mux_ctrl[addr >> 2] = value;
+ omap_pin_funcmux1_update(s, diff, value);
+ return;
+
+ case 0x08: /* FUNC_MUX_CTRL_2 */
+ s->func_mux_ctrl[addr >> 2] = value;
+ return;
+
+ case 0x0c: /* COMP_MODE_CTRL_0 */
+ s->comp_mode_ctrl[0] = value;
+ s->compat1509 = (value != 0x0000eaef);
+ omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]);
+ omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]);
+ return;
+
+ case 0x10: /* FUNC_MUX_CTRL_3 */
+ case 0x14: /* FUNC_MUX_CTRL_4 */
+ case 0x18: /* FUNC_MUX_CTRL_5 */
+ case 0x1c: /* FUNC_MUX_CTRL_6 */
+ case 0x20: /* FUNC_MUX_CTRL_7 */
+ case 0x24: /* FUNC_MUX_CTRL_8 */
+ case 0x28: /* FUNC_MUX_CTRL_9 */
+ case 0x2c: /* FUNC_MUX_CTRL_A */
+ case 0x30: /* FUNC_MUX_CTRL_B */
+ case 0x34: /* FUNC_MUX_CTRL_C */
+ case 0x38: /* FUNC_MUX_CTRL_D */
+ s->func_mux_ctrl[(addr >> 2) - 1] = value;
+ return;
+
+ case 0x40: /* PULL_DWN_CTRL_0 */
+ case 0x44: /* PULL_DWN_CTRL_1 */
+ case 0x48: /* PULL_DWN_CTRL_2 */
+ case 0x4c: /* PULL_DWN_CTRL_3 */
+ s->pull_dwn_ctrl[(addr & 0xf) >> 2] = value;
+ return;
+
+ case 0x50: /* GATE_INH_CTRL_0 */
+ s->gate_inh_ctrl[0] = value;
+ return;
+
+ case 0x60: /* VOLTAGE_CTRL_0 */
+ s->voltage_ctrl[0] = value;
+ return;
+
+ case 0x70: /* TEST_DBG_CTRL_0 */
+ s->test_dbg_ctrl[0] = value;
+ return;
+
+ case 0x80: /* MOD_CONF_CTRL_0 */
+ diff = s->mod_conf_ctrl[0] ^ value;
+ s->mod_conf_ctrl[0] = value;
+ omap_pin_modconf1_update(s, diff, value);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_pin_cfg_ops = {
+ .read = omap_pin_cfg_read,
+ .write = omap_pin_cfg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_pin_cfg_reset(struct omap_mpu_state_s *mpu)
+{
+ /* Start in Compatibility Mode. */
+ mpu->compat1509 = 1;
+ omap_pin_funcmux0_update(mpu, mpu->func_mux_ctrl[0], 0);
+ omap_pin_funcmux1_update(mpu, mpu->func_mux_ctrl[1], 0);
+ omap_pin_modconf1_update(mpu, mpu->mod_conf_ctrl[0], 0);
+ memset(mpu->func_mux_ctrl, 0, sizeof(mpu->func_mux_ctrl));
+ memset(mpu->comp_mode_ctrl, 0, sizeof(mpu->comp_mode_ctrl));
+ memset(mpu->pull_dwn_ctrl, 0, sizeof(mpu->pull_dwn_ctrl));
+ memset(mpu->gate_inh_ctrl, 0, sizeof(mpu->gate_inh_ctrl));
+ memset(mpu->voltage_ctrl, 0, sizeof(mpu->voltage_ctrl));
+ memset(mpu->test_dbg_ctrl, 0, sizeof(mpu->test_dbg_ctrl));
+ memset(mpu->mod_conf_ctrl, 0, sizeof(mpu->mod_conf_ctrl));
+}
+
+static void omap_pin_cfg_init(MemoryRegion *system_memory,
+ hwaddr base,
+ struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->pin_cfg_iomem, &omap_pin_cfg_ops, mpu,
+ "omap-pin-cfg", 0x800);
+ memory_region_add_subregion(system_memory, base, &mpu->pin_cfg_iomem);
+ omap_pin_cfg_reset(mpu);
+}
+
+/* Device Identification, Die Identification */
+static uint64_t omap_id_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0xfffe1800: /* DIE_ID_LSB */
+ return 0xc9581f0e;
+ case 0xfffe1804: /* DIE_ID_MSB */
+ return 0xa8858bfa;
+
+ case 0xfffe2000: /* PRODUCT_ID_LSB */
+ return 0x00aaaafc;
+ case 0xfffe2004: /* PRODUCT_ID_MSB */
+ return 0xcafeb574;
+
+ case 0xfffed400: /* JTAG_ID_LSB */
+ switch (s->mpu_model) {
+ case omap310:
+ return 0x03310315;
+ case omap1510:
+ return 0x03310115;
+ default:
+ hw_error("%s: bad mpu model\n", __FUNCTION__);
+ }
+ break;
+
+ case 0xfffed404: /* JTAG_ID_MSB */
+ switch (s->mpu_model) {
+ case omap310:
+ return 0xfb57402f;
+ case omap1510:
+ return 0xfb47002f;
+ default:
+ hw_error("%s: bad mpu model\n", __FUNCTION__);
+ }
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_id_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ OMAP_BAD_REG(addr);
+}
+
+static const MemoryRegionOps omap_id_ops = {
+ .read = omap_id_read,
+ .write = omap_id_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_id_init(MemoryRegion *memory, struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->id_iomem, &omap_id_ops, mpu,
+ "omap-id", 0x100000000ULL);
+ memory_region_init_alias(&mpu->id_iomem_e18, "omap-id-e18", &mpu->id_iomem,
+ 0xfffe1800, 0x800);
+ memory_region_add_subregion(memory, 0xfffe1800, &mpu->id_iomem_e18);
+ memory_region_init_alias(&mpu->id_iomem_ed4, "omap-id-ed4", &mpu->id_iomem,
+ 0xfffed400, 0x100);
+ memory_region_add_subregion(memory, 0xfffed400, &mpu->id_iomem_ed4);
+ if (!cpu_is_omap15xx(mpu)) {
+ memory_region_init_alias(&mpu->id_iomem_ed4, "omap-id-e20",
+ &mpu->id_iomem, 0xfffe2000, 0x800);
+ memory_region_add_subregion(memory, 0xfffe2000, &mpu->id_iomem_e20);
+ }
+}
+
+/* MPUI Control (Dummy) */
+static uint64_t omap_mpui_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* CTRL */
+ return s->mpui_ctrl;
+ case 0x04: /* DEBUG_ADDR */
+ return 0x01ffffff;
+ case 0x08: /* DEBUG_DATA */
+ return 0xffffffff;
+ case 0x0c: /* DEBUG_FLAG */
+ return 0x00000800;
+ case 0x10: /* STATUS */
+ return 0x00000000;
+
+ /* Not in OMAP310 */
+ case 0x14: /* DSP_STATUS */
+ case 0x18: /* DSP_BOOT_CONFIG */
+ return 0x00000000;
+ case 0x1c: /* DSP_MPUI_CONFIG */
+ return 0x0000ffff;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpui_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* CTRL */
+ s->mpui_ctrl = value & 0x007fffff;
+ break;
+
+ case 0x04: /* DEBUG_ADDR */
+ case 0x08: /* DEBUG_DATA */
+ case 0x0c: /* DEBUG_FLAG */
+ case 0x10: /* STATUS */
+ /* Not in OMAP310 */
+ case 0x14: /* DSP_STATUS */
+ OMAP_RO_REG(addr);
+ break;
+ case 0x18: /* DSP_BOOT_CONFIG */
+ case 0x1c: /* DSP_MPUI_CONFIG */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_mpui_ops = {
+ .read = omap_mpui_read,
+ .write = omap_mpui_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_mpui_reset(struct omap_mpu_state_s *s)
+{
+ s->mpui_ctrl = 0x0003ff1b;
+}
+
+static void omap_mpui_init(MemoryRegion *memory, hwaddr base,
+ struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->mpui_iomem, &omap_mpui_ops, mpu,
+ "omap-mpui", 0x100);
+ memory_region_add_subregion(memory, base, &mpu->mpui_iomem);
+
+ omap_mpui_reset(mpu);
+}
+
+/* TIPB Bridges */
+struct omap_tipb_bridge_s {
+ qemu_irq abort;
+ MemoryRegion iomem;
+
+ int width_intr;
+ uint16_t control;
+ uint16_t alloc;
+ uint16_t buffer;
+ uint16_t enh_control;
+};
+
+static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+
+ if (size < 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* TIPB_CNTL */
+ return s->control;
+ case 0x04: /* TIPB_BUS_ALLOC */
+ return s->alloc;
+ case 0x08: /* MPU_TIPB_CNTL */
+ return s->buffer;
+ case 0x0c: /* ENHANCED_TIPB_CNTL */
+ return s->enh_control;
+ case 0x10: /* ADDRESS_DBG */
+ case 0x14: /* DATA_DEBUG_LOW */
+ case 0x18: /* DATA_DEBUG_HIGH */
+ return 0xffff;
+ case 0x1c: /* DEBUG_CNTR_SIG */
+ return 0x00f8;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_tipb_bridge_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+
+ if (size < 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* TIPB_CNTL */
+ s->control = value & 0xffff;
+ break;
+
+ case 0x04: /* TIPB_BUS_ALLOC */
+ s->alloc = value & 0x003f;
+ break;
+
+ case 0x08: /* MPU_TIPB_CNTL */
+ s->buffer = value & 0x0003;
+ break;
+
+ case 0x0c: /* ENHANCED_TIPB_CNTL */
+ s->width_intr = !(value & 2);
+ s->enh_control = value & 0x000f;
+ break;
+
+ case 0x10: /* ADDRESS_DBG */
+ case 0x14: /* DATA_DEBUG_LOW */
+ case 0x18: /* DATA_DEBUG_HIGH */
+ case 0x1c: /* DEBUG_CNTR_SIG */
+ OMAP_RO_REG(addr);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_tipb_bridge_ops = {
+ .read = omap_tipb_bridge_read,
+ .write = omap_tipb_bridge_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_tipb_bridge_reset(struct omap_tipb_bridge_s *s)
+{
+ s->control = 0xffff;
+ s->alloc = 0x0009;
+ s->buffer = 0x0000;
+ s->enh_control = 0x000f;
+}
+
+static struct omap_tipb_bridge_s *omap_tipb_bridge_init(
+ MemoryRegion *memory, hwaddr base,
+ qemu_irq abort_irq, omap_clk clk)
+{
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *)
+ g_malloc0(sizeof(struct omap_tipb_bridge_s));
+
+ s->abort = abort_irq;
+ omap_tipb_bridge_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_tipb_bridge_ops, s,
+ "omap-tipb-bridge", 0x100);
+ memory_region_add_subregion(memory, base, &s->iomem);
+
+ return s;
+}
+
+/* Dummy Traffic Controller's Memory Interface */
+static uint64_t omap_tcmi_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint32_t ret;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* IMIF_PRIO */
+ case 0x04: /* EMIFS_PRIO */
+ case 0x08: /* EMIFF_PRIO */
+ case 0x0c: /* EMIFS_CONFIG */
+ case 0x10: /* EMIFS_CS0_CONFIG */
+ case 0x14: /* EMIFS_CS1_CONFIG */
+ case 0x18: /* EMIFS_CS2_CONFIG */
+ case 0x1c: /* EMIFS_CS3_CONFIG */
+ case 0x24: /* EMIFF_MRS */
+ case 0x28: /* TIMEOUT1 */
+ case 0x2c: /* TIMEOUT2 */
+ case 0x30: /* TIMEOUT3 */
+ case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */
+ case 0x40: /* EMIFS_CFG_DYN_WAIT */
+ return s->tcmi_regs[addr >> 2];
+
+ case 0x20: /* EMIFF_SDRAM_CONFIG */
+ ret = s->tcmi_regs[addr >> 2];
+ s->tcmi_regs[addr >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */
+ /* XXX: We can try using the VGA_DIRTY flag for this */
+ return ret;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_tcmi_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* IMIF_PRIO */
+ case 0x04: /* EMIFS_PRIO */
+ case 0x08: /* EMIFF_PRIO */
+ case 0x10: /* EMIFS_CS0_CONFIG */
+ case 0x14: /* EMIFS_CS1_CONFIG */
+ case 0x18: /* EMIFS_CS2_CONFIG */
+ case 0x1c: /* EMIFS_CS3_CONFIG */
+ case 0x20: /* EMIFF_SDRAM_CONFIG */
+ case 0x24: /* EMIFF_MRS */
+ case 0x28: /* TIMEOUT1 */
+ case 0x2c: /* TIMEOUT2 */
+ case 0x30: /* TIMEOUT3 */
+ case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */
+ case 0x40: /* EMIFS_CFG_DYN_WAIT */
+ s->tcmi_regs[addr >> 2] = value;
+ break;
+ case 0x0c: /* EMIFS_CONFIG */
+ s->tcmi_regs[addr >> 2] = (value & 0xf) | (1 << 4);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_tcmi_ops = {
+ .read = omap_tcmi_read,
+ .write = omap_tcmi_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_tcmi_reset(struct omap_mpu_state_s *mpu)
+{
+ mpu->tcmi_regs[0x00 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x04 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x08 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x0c >> 2] = 0x00000010;
+ mpu->tcmi_regs[0x10 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x14 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x18 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x1c >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x20 >> 2] = 0x00618800;
+ mpu->tcmi_regs[0x24 >> 2] = 0x00000037;
+ mpu->tcmi_regs[0x28 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x2c >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x30 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x3c >> 2] = 0x00000003;
+ mpu->tcmi_regs[0x40 >> 2] = 0x00000000;
+}
+
+static void omap_tcmi_init(MemoryRegion *memory, hwaddr base,
+ struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->tcmi_iomem, &omap_tcmi_ops, mpu,
+ "omap-tcmi", 0x100);
+ memory_region_add_subregion(memory, base, &mpu->tcmi_iomem);
+ omap_tcmi_reset(mpu);
+}
+
+/* Digital phase-locked loops control */
+struct dpll_ctl_s {
+ MemoryRegion iomem;
+ uint16_t mode;
+ omap_clk dpll;
+};
+
+static uint64_t omap_dpll_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ if (addr == 0x00) /* CTL_REG */
+ return s->mode;
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_dpll_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+ uint16_t diff;
+ static const int bypass_div[4] = { 1, 2, 4, 4 };
+ int div, mult;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ if (addr == 0x00) { /* CTL_REG */
+ /* See omap_ulpd_pm_write() too */
+ diff = s->mode & value;
+ s->mode = value & 0x2fff;
+ if (diff & (0x3ff << 2)) {
+ if (value & (1 << 4)) { /* PLL_ENABLE */
+ div = ((value >> 5) & 3) + 1; /* PLL_DIV */
+ mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */
+ } else {
+ div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */
+ mult = 1;
+ }
+ omap_clk_setrate(s->dpll, div, mult);
+ }
+
+ /* Enter the desired mode. */
+ s->mode = (s->mode & 0xfffe) | ((s->mode >> 4) & 1);
+
+ /* Act as if the lock is restored. */
+ s->mode |= 2;
+ } else {
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_dpll_ops = {
+ .read = omap_dpll_read,
+ .write = omap_dpll_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_dpll_reset(struct dpll_ctl_s *s)
+{
+ s->mode = 0x2002;
+ omap_clk_setrate(s->dpll, 1, 1);
+}
+
+static struct dpll_ctl_s *omap_dpll_init(MemoryRegion *memory,
+ hwaddr base, omap_clk clk)
+{
+ struct dpll_ctl_s *s = g_malloc0(sizeof(*s));
+ memory_region_init_io(&s->iomem, &omap_dpll_ops, s, "omap-dpll", 0x100);
+
+ s->dpll = clk;
+ omap_dpll_reset(s);
+
+ memory_region_add_subregion(memory, base, &s->iomem);
+ return s;
+}
+
+/* MPU Clock/Reset/Power Mode Control */
+static uint64_t omap_clkm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* ARM_CKCTL */
+ return s->clkm.arm_ckctl;
+
+ case 0x04: /* ARM_IDLECT1 */
+ return s->clkm.arm_idlect1;
+
+ case 0x08: /* ARM_IDLECT2 */
+ return s->clkm.arm_idlect2;
+
+ case 0x0c: /* ARM_EWUPCT */
+ return s->clkm.arm_ewupct;
+
+ case 0x10: /* ARM_RSTCT1 */
+ return s->clkm.arm_rstct1;
+
+ case 0x14: /* ARM_RSTCT2 */
+ return s->clkm.arm_rstct2;
+
+ case 0x18: /* ARM_SYSST */
+ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start;
+
+ case 0x1c: /* ARM_CKOUT1 */
+ return s->clkm.arm_ckout1;
+
+ case 0x20: /* ARM_CKOUT2 */
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */
+ if (value & (1 << 14))
+ /* Reserved */;
+ else {
+ clk = omap_findclk(s, "arminth_ck");
+ omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+ }
+ }
+ if (diff & (1 << 12)) { /* ARM_TIMXO */
+ clk = omap_findclk(s, "armtim_ck");
+ if (value & (1 << 12))
+ omap_clk_reparent(clk, omap_findclk(s, "clkin"));
+ else
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+ }
+ /* XXX: en_dspck */
+ if (diff & (3 << 10)) { /* DSPMMUDIV */
+ clk = omap_findclk(s, "dspmmu_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1);
+ }
+ if (diff & (3 << 8)) { /* TCDIV */
+ clk = omap_findclk(s, "tc_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1);
+ }
+ if (diff & (3 << 6)) { /* DSPDIV */
+ clk = omap_findclk(s, "dsp_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1);
+ }
+ if (diff & (3 << 4)) { /* ARMDIV */
+ clk = omap_findclk(s, "arm_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1);
+ }
+ if (diff & (3 << 2)) { /* LCDDIV */
+ clk = omap_findclk(s, "lcd_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1);
+ }
+ if (diff & (3 << 0)) { /* PERDIV */
+ clk = omap_findclk(s, "armper_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1);
+ }
+}
+
+static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (value & (1 << 11)) { /* SETARM_IDLE */
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT);
+ }
+ if (!(value & (1 << 10))) /* WKUP_MODE */
+ qemu_system_shutdown_request(); /* XXX: disable wakeup from IRQ */
+
+#define SET_CANIDLE(clock, bit) \
+ if (diff & (1 << bit)) { \
+ clk = omap_findclk(s, clock); \
+ omap_clk_canidle(clk, (value >> bit) & 1); \
+ }
+ SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */
+ SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */
+ SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */
+ SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */
+ SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */
+ SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */
+ SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */
+ SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */
+}
+
+static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+#define SET_ONOFF(clock, bit) \
+ if (diff & (1 << bit)) { \
+ clk = omap_findclk(s, clock); \
+ omap_clk_onoff(clk, (value >> bit) & 1); \
+ }
+ SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */
+ SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */
+ SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */
+ SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */
+ SET_ONOFF("lb_ck", 4) /* EN_LBCK */
+ SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */
+ SET_ONOFF("mpui_ck", 6) /* EN_APICK */
+ SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */
+ SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */
+ SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */
+ SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */
+}
+
+static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (diff & (3 << 4)) { /* TCLKOUT */
+ clk = omap_findclk(s, "tclk_out");
+ switch ((value >> 4) & 3) {
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen3"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+ omap_clk_onoff(clk, 1);
+ break;
+ default:
+ omap_clk_onoff(clk, 0);
+ }
+ }
+ if (diff & (3 << 2)) { /* DCLKOUT */
+ clk = omap_findclk(s, "dclk_out");
+ switch ((value >> 2) & 3) {
+ case 0:
+ omap_clk_reparent(clk, omap_findclk(s, "dspmmu_ck"));
+ break;
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen2"));
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "dsp_ck"));
+ break;
+ case 3:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+ break;
+ }
+ }
+ if (diff & (3 << 0)) { /* ACLKOUT */
+ clk = omap_findclk(s, "aclk_out");
+ switch ((value >> 0) & 3) {
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "arm_ck"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 3:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+ omap_clk_onoff(clk, 1);
+ break;
+ default:
+ omap_clk_onoff(clk, 0);
+ }
+ }
+}
+
+static void omap_clkm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t diff;
+ omap_clk clk;
+ static const char *clkschemename[8] = {
+ "fully synchronous", "fully asynchronous", "synchronous scalable",
+ "mix mode 1", "mix mode 2", "bypass mode", "mix mode 3", "mix mode 4",
+ };
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* ARM_CKCTL */
+ diff = s->clkm.arm_ckctl ^ value;
+ s->clkm.arm_ckctl = value & 0x7fff;
+ omap_clkm_ckctl_update(s, diff, value);
+ return;
+
+ case 0x04: /* ARM_IDLECT1 */
+ diff = s->clkm.arm_idlect1 ^ value;
+ s->clkm.arm_idlect1 = value & 0x0fff;
+ omap_clkm_idlect1_update(s, diff, value);
+ return;
+
+ case 0x08: /* ARM_IDLECT2 */
+ diff = s->clkm.arm_idlect2 ^ value;
+ s->clkm.arm_idlect2 = value & 0x07ff;
+ omap_clkm_idlect2_update(s, diff, value);
+ return;
+
+ case 0x0c: /* ARM_EWUPCT */
+ s->clkm.arm_ewupct = value & 0x003f;
+ return;
+
+ case 0x10: /* ARM_RSTCT1 */
+ diff = s->clkm.arm_rstct1 ^ value;
+ s->clkm.arm_rstct1 = value & 0x0007;
+ if (value & 9) {
+ qemu_system_reset_request();
+ s->clkm.cold_start = 0xa;
+ }
+ if (diff & ~value & 4) { /* DSP_RST */
+ omap_mpui_reset(s);
+ omap_tipb_bridge_reset(s->private_tipb);
+ omap_tipb_bridge_reset(s->public_tipb);
+ }
+ if (diff & 2) { /* DSP_EN */
+ clk = omap_findclk(s, "dsp_ck");
+ omap_clk_canidle(clk, (~value >> 1) & 1);
+ }
+ return;
+
+ case 0x14: /* ARM_RSTCT2 */
+ s->clkm.arm_rstct2 = value & 0x0001;
+ return;
+
+ case 0x18: /* ARM_SYSST */
+ if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) {
+ s->clkm.clocking_scheme = (value >> 11) & 7;
+ printf("%s: clocking scheme set to %s\n", __FUNCTION__,
+ clkschemename[s->clkm.clocking_scheme]);
+ }
+ s->clkm.cold_start &= value & 0x3f;
+ return;
+
+ case 0x1c: /* ARM_CKOUT1 */
+ diff = s->clkm.arm_ckout1 ^ value;
+ s->clkm.arm_ckout1 = value & 0x003f;
+ omap_clkm_ckout1_update(s, diff, value);
+ return;
+
+ case 0x20: /* ARM_CKOUT2 */
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_clkm_ops = {
+ .read = omap_clkm_read,
+ .write = omap_clkm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x04: /* DSP_IDLECT1 */
+ return s->clkm.dsp_idlect1;
+
+ case 0x08: /* DSP_IDLECT2 */
+ return s->clkm.dsp_idlect2;
+
+ case 0x14: /* DSP_RSTCT2 */
+ return s->clkm.dsp_rstct2;
+
+ case 0x18: /* DSP_SYSST */
+ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start |
+ (s->cpu->env.halted << 6); /* Quite useless... */
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */
+}
+
+static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */
+}
+
+static void omap_clkdsp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t diff;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x04: /* DSP_IDLECT1 */
+ diff = s->clkm.dsp_idlect1 ^ value;
+ s->clkm.dsp_idlect1 = value & 0x01f7;
+ omap_clkdsp_idlect1_update(s, diff, value);
+ break;
+
+ case 0x08: /* DSP_IDLECT2 */
+ s->clkm.dsp_idlect2 = value & 0x0037;
+ diff = s->clkm.dsp_idlect1 ^ value;
+ omap_clkdsp_idlect2_update(s, diff, value);
+ break;
+
+ case 0x14: /* DSP_RSTCT2 */
+ s->clkm.dsp_rstct2 = value & 0x0001;
+ break;
+
+ case 0x18: /* DSP_SYSST */
+ s->clkm.cold_start &= value & 0x3f;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static const MemoryRegionOps omap_clkdsp_ops = {
+ .read = omap_clkdsp_read,
+ .write = omap_clkdsp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_clkm_reset(struct omap_mpu_state_s *s)
+{
+ if (s->wdt && s->wdt->reset)
+ s->clkm.cold_start = 0x6;
+ s->clkm.clocking_scheme = 0;
+ omap_clkm_ckctl_update(s, ~0, 0x3000);
+ s->clkm.arm_ckctl = 0x3000;
+ omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 ^ 0x0400, 0x0400);
+ s->clkm.arm_idlect1 = 0x0400;
+ omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 ^ 0x0100, 0x0100);
+ s->clkm.arm_idlect2 = 0x0100;
+ s->clkm.arm_ewupct = 0x003f;
+ s->clkm.arm_rstct1 = 0x0000;
+ s->clkm.arm_rstct2 = 0x0000;
+ s->clkm.arm_ckout1 = 0x0015;
+ s->clkm.dpll1_mode = 0x2002;
+ omap_clkdsp_idlect1_update(s, s->clkm.dsp_idlect1 ^ 0x0040, 0x0040);
+ s->clkm.dsp_idlect1 = 0x0040;
+ omap_clkdsp_idlect2_update(s, ~0, 0x0000);
+ s->clkm.dsp_idlect2 = 0x0000;
+ s->clkm.dsp_rstct2 = 0x0000;
+}
+
+static void omap_clkm_init(MemoryRegion *memory, hwaddr mpu_base,
+ hwaddr dsp_base, struct omap_mpu_state_s *s)
+{
+ memory_region_init_io(&s->clkm_iomem, &omap_clkm_ops, s,
+ "omap-clkm", 0x100);
+ memory_region_init_io(&s->clkdsp_iomem, &omap_clkdsp_ops, s,
+ "omap-clkdsp", 0x1000);
+
+ s->clkm.arm_idlect1 = 0x03ff;
+ s->clkm.arm_idlect2 = 0x0100;
+ s->clkm.dsp_idlect1 = 0x0002;
+ omap_clkm_reset(s);
+ s->clkm.cold_start = 0x3a;
+
+ memory_region_add_subregion(memory, mpu_base, &s->clkm_iomem);
+ memory_region_add_subregion(memory, dsp_base, &s->clkdsp_iomem);
+}
+
+/* MPU I/O */
+struct omap_mpuio_s {
+ qemu_irq irq;
+ qemu_irq kbd_irq;
+ qemu_irq *in;
+ qemu_irq handler[16];
+ qemu_irq wakeup;
+ MemoryRegion iomem;
+
+ uint16_t inputs;
+ uint16_t outputs;
+ uint16_t dir;
+ uint16_t edge;
+ uint16_t mask;
+ uint16_t ints;
+
+ uint16_t debounce;
+ uint16_t latch;
+ uint8_t event;
+
+ uint8_t buttons[5];
+ uint8_t row_latch;
+ uint8_t cols;
+ int kbd_mask;
+ int clk;
+};
+
+static void omap_mpuio_set(void *opaque, int line, int level)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ uint16_t prev = s->inputs;
+
+ if (level)
+ s->inputs |= 1 << line;
+ else
+ s->inputs &= ~(1 << line);
+
+ if (((1 << line) & s->dir & ~s->mask) && s->clk) {
+ if ((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) {
+ s->ints |= 1 << line;
+ qemu_irq_raise(s->irq);
+ /* TODO: wakeup */
+ }
+ if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */
+ (s->event >> 1) == line) /* PIN_SELECT */
+ s->latch = s->inputs;
+ }
+}
+
+static void omap_mpuio_kbd_update(struct omap_mpuio_s *s)
+{
+ int i;
+ uint8_t *row, rows = 0, cols = ~s->cols;
+
+ for (row = s->buttons + 4, i = 1 << 4; i; row --, i >>= 1)
+ if (*row & cols)
+ rows |= i;
+
+ qemu_set_irq(s->kbd_irq, rows && !s->kbd_mask && s->clk);
+ s->row_latch = ~rows;
+}
+
+static uint64_t omap_mpuio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t ret;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* INPUT_LATCH */
+ return s->inputs;
+
+ case 0x04: /* OUTPUT_REG */
+ return s->outputs;
+
+ case 0x08: /* IO_CNTL */
+ return s->dir;
+
+ case 0x10: /* KBR_LATCH */
+ return s->row_latch;
+
+ case 0x14: /* KBC_REG */
+ return s->cols;
+
+ case 0x18: /* GPIO_EVENT_MODE_REG */
+ return s->event;
+
+ case 0x1c: /* GPIO_INT_EDGE_REG */
+ return s->edge;
+
+ case 0x20: /* KBD_INT */
+ return (~s->row_latch & 0x1f) && !s->kbd_mask;
+
+ case 0x24: /* GPIO_INT */
+ ret = s->ints;
+ s->ints &= s->mask;
+ if (ret)
+ qemu_irq_lower(s->irq);
+ return ret;
+
+ case 0x28: /* KBD_MASKIT */
+ return s->kbd_mask;
+
+ case 0x2c: /* GPIO_MASKIT */
+ return s->mask;
+
+ case 0x30: /* GPIO_DEBOUNCING_REG */
+ return s->debounce;
+
+ case 0x34: /* GPIO_LATCH_REG */
+ return s->latch;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpuio_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t diff;
+ int ln;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x04: /* OUTPUT_REG */
+ diff = (s->outputs ^ value) & ~s->dir;
+ s->outputs = value;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x08: /* IO_CNTL */
+ diff = s->outputs & (s->dir ^ value);
+ s->dir = value;
+
+ value = s->outputs & ~s->dir;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x14: /* KBC_REG */
+ s->cols = value;
+ omap_mpuio_kbd_update(s);
+ break;
+
+ case 0x18: /* GPIO_EVENT_MODE_REG */
+ s->event = value & 0x1f;
+ break;
+
+ case 0x1c: /* GPIO_INT_EDGE_REG */
+ s->edge = value;
+ break;
+
+ case 0x28: /* KBD_MASKIT */
+ s->kbd_mask = value & 1;
+ omap_mpuio_kbd_update(s);
+ break;
+
+ case 0x2c: /* GPIO_MASKIT */
+ s->mask = value;
+ break;
+
+ case 0x30: /* GPIO_DEBOUNCING_REG */
+ s->debounce = value & 0x1ff;
+ break;
+
+ case 0x00: /* INPUT_LATCH */
+ case 0x10: /* KBR_LATCH */
+ case 0x20: /* KBD_INT */
+ case 0x24: /* GPIO_INT */
+ case 0x34: /* GPIO_LATCH_REG */
+ OMAP_RO_REG(addr);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_mpuio_ops = {
+ .read = omap_mpuio_read,
+ .write = omap_mpuio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_mpuio_reset(struct omap_mpuio_s *s)
+{
+ s->inputs = 0;
+ s->outputs = 0;
+ s->dir = ~0;
+ s->event = 0;
+ s->edge = 0;
+ s->kbd_mask = 0;
+ s->mask = 0;
+ s->debounce = 0;
+ s->latch = 0;
+ s->ints = 0;
+ s->row_latch = 0x1f;
+ s->clk = 1;
+}
+
+static void omap_mpuio_onoff(void *opaque, int line, int on)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+
+ s->clk = on;
+ if (on)
+ omap_mpuio_kbd_update(s);
+}
+
+static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory,
+ hwaddr base,
+ qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup,
+ omap_clk clk)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *)
+ g_malloc0(sizeof(struct omap_mpuio_s));
+
+ s->irq = gpio_int;
+ s->kbd_irq = kbd_int;
+ s->wakeup = wakeup;
+ s->in = qemu_allocate_irqs(omap_mpuio_set, s, 16);
+ omap_mpuio_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_mpuio_ops, s,
+ "omap-mpuio", 0x800);
+ memory_region_add_subregion(memory, base, &s->iomem);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_mpuio_onoff, s, 1)[0]);
+
+ return s;
+}
+
+qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s)
+{
+ return s->in;
+}
+
+void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler)
+{
+ if (line >= 16 || line < 0)
+ hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
+ s->handler[line] = handler;
+}
+
+void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down)
+{
+ if (row >= 5 || row < 0)
+ hw_error("%s: No key %i-%i\n", __FUNCTION__, col, row);
+
+ if (down)
+ s->buttons[row] |= 1 << col;
+ else
+ s->buttons[row] &= ~(1 << col);
+
+ omap_mpuio_kbd_update(s);
+}
+
+/* MicroWire Interface */
+struct omap_uwire_s {
+ MemoryRegion iomem;
+ qemu_irq txirq;
+ qemu_irq rxirq;
+ qemu_irq txdrq;
+
+ uint16_t txbuf;
+ uint16_t rxbuf;
+ uint16_t control;
+ uint16_t setup[5];
+
+ uWireSlave *chip[4];
+};
+
+static void omap_uwire_transfer_start(struct omap_uwire_s *s)
+{
+ int chipselect = (s->control >> 10) & 3; /* INDEX */
+ uWireSlave *slave = s->chip[chipselect];
+
+ if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */
+ if (s->control & (1 << 12)) /* CS_CMD */
+ if (slave && slave->send)
+ slave->send(slave->opaque,
+ s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
+ s->control &= ~(1 << 14); /* CSRB */
+ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
+ * a DRQ. When is the level IRQ supposed to be reset? */
+ }
+
+ if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */
+ if (s->control & (1 << 12)) /* CS_CMD */
+ if (slave && slave->receive)
+ s->rxbuf = slave->receive(slave->opaque);
+ s->control |= 1 << 15; /* RDRB */
+ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
+ * a DRQ. When is the level IRQ supposed to be reset? */
+ }
+}
+
+static uint64_t omap_uwire_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_uwire_s *s = (struct omap_uwire_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* RDR */
+ s->control &= ~(1 << 15); /* RDRB */
+ return s->rxbuf;
+
+ case 0x04: /* CSR */
+ return s->control;
+
+ case 0x08: /* SR1 */
+ return s->setup[0];
+ case 0x0c: /* SR2 */
+ return s->setup[1];
+ case 0x10: /* SR3 */
+ return s->setup[2];
+ case 0x14: /* SR4 */
+ return s->setup[3];
+ case 0x18: /* SR5 */
+ return s->setup[4];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_uwire_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_uwire_s *s = (struct omap_uwire_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* TDR */
+ s->txbuf = value; /* TD */
+ if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */
+ ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */
+ (s->control & (1 << 12)))) { /* CS_CMD */
+ s->control |= 1 << 14; /* CSRB */
+ omap_uwire_transfer_start(s);
+ }
+ break;
+
+ case 0x04: /* CSR */
+ s->control = value & 0x1fff;
+ if (value & (1 << 13)) /* START */
+ omap_uwire_transfer_start(s);
+ break;
+
+ case 0x08: /* SR1 */
+ s->setup[0] = value & 0x003f;
+ break;
+
+ case 0x0c: /* SR2 */
+ s->setup[1] = value & 0x0fc0;
+ break;
+
+ case 0x10: /* SR3 */
+ s->setup[2] = value & 0x0003;
+ break;
+
+ case 0x14: /* SR4 */
+ s->setup[3] = value & 0x0001;
+ break;
+
+ case 0x18: /* SR5 */
+ s->setup[4] = value & 0x000f;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_uwire_ops = {
+ .read = omap_uwire_read,
+ .write = omap_uwire_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_uwire_reset(struct omap_uwire_s *s)
+{
+ s->control = 0;
+ s->setup[0] = 0;
+ s->setup[1] = 0;
+ s->setup[2] = 0;
+ s->setup[3] = 0;
+ s->setup[4] = 0;
+}
+
+static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory,
+ hwaddr base,
+ qemu_irq txirq, qemu_irq rxirq,
+ qemu_irq dma,
+ omap_clk clk)
+{
+ struct omap_uwire_s *s = (struct omap_uwire_s *)
+ g_malloc0(sizeof(struct omap_uwire_s));
+
+ s->txirq = txirq;
+ s->rxirq = rxirq;
+ s->txdrq = dma;
+ omap_uwire_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_uwire_ops, s, "omap-uwire", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ return s;
+}
+
+void omap_uwire_attach(struct omap_uwire_s *s,
+ uWireSlave *slave, int chipselect)
+{
+ if (chipselect < 0 || chipselect > 3) {
+ fprintf(stderr, "%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
+ exit(-1);
+ }
+
+ s->chip[chipselect] = slave;
+}
+
+/* Pseudonoise Pulse-Width Light Modulator */
+struct omap_pwl_s {
+ MemoryRegion iomem;
+ uint8_t output;
+ uint8_t level;
+ uint8_t enable;
+ int clk;
+};
+
+static void omap_pwl_update(struct omap_pwl_s *s)
+{
+ int output = (s->clk && s->enable) ? s->level : 0;
+
+ if (output != s->output) {
+ s->output = output;
+ printf("%s: Backlight now at %i/256\n", __FUNCTION__, output);
+ }
+}
+
+static uint64_t omap_pwl_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_pwl_s *s = (struct omap_pwl_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_read8(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* PWL_LEVEL */
+ return s->level;
+ case 0x04: /* PWL_CTRL */
+ return s->enable;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_pwl_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_pwl_s *s = (struct omap_pwl_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_write8(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* PWL_LEVEL */
+ s->level = value;
+ omap_pwl_update(s);
+ break;
+ case 0x04: /* PWL_CTRL */
+ s->enable = value & 1;
+ omap_pwl_update(s);
+ break;
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_pwl_ops = {
+ .read = omap_pwl_read,
+ .write = omap_pwl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_pwl_reset(struct omap_pwl_s *s)
+{
+ s->output = 0;
+ s->level = 0;
+ s->enable = 0;
+ s->clk = 1;
+ omap_pwl_update(s);
+}
+
+static void omap_pwl_clk_update(void *opaque, int line, int on)
+{
+ struct omap_pwl_s *s = (struct omap_pwl_s *) opaque;
+
+ s->clk = on;
+ omap_pwl_update(s);
+}
+
+static struct omap_pwl_s *omap_pwl_init(MemoryRegion *system_memory,
+ hwaddr base,
+ omap_clk clk)
+{
+ struct omap_pwl_s *s = g_malloc0(sizeof(*s));
+
+ omap_pwl_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_pwl_ops, s,
+ "omap-pwl", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_pwl_clk_update, s, 1)[0]);
+ return s;
+}
+
+/* Pulse-Width Tone module */
+struct omap_pwt_s {
+ MemoryRegion iomem;
+ uint8_t frc;
+ uint8_t vrc;
+ uint8_t gcr;
+ omap_clk clk;
+};
+
+static uint64_t omap_pwt_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_pwt_s *s = (struct omap_pwt_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_read8(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* FRC */
+ return s->frc;
+ case 0x04: /* VCR */
+ return s->vrc;
+ case 0x08: /* GCR */
+ return s->gcr;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_pwt_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_pwt_s *s = (struct omap_pwt_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_write8(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* FRC */
+ s->frc = value & 0x3f;
+ break;
+ case 0x04: /* VRC */
+ if ((value ^ s->vrc) & 1) {
+ if (value & 1)
+ printf("%s: %iHz buzz on\n", __FUNCTION__, (int)
+ /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */
+ ((omap_clk_getrate(s->clk) >> 3) /
+ /* Pre-multiplexer divider */
+ ((s->gcr & 2) ? 1 : 154) /
+ /* Octave multiplexer */
+ (2 << (value & 3)) *
+ /* 101/107 divider */
+ ((value & (1 << 2)) ? 101 : 107) *
+ /* 49/55 divider */
+ ((value & (1 << 3)) ? 49 : 55) *
+ /* 50/63 divider */
+ ((value & (1 << 4)) ? 50 : 63) *
+ /* 80/127 divider */
+ ((value & (1 << 5)) ? 80 : 127) /
+ (107 * 55 * 63 * 127)));
+ else
+ printf("%s: silence!\n", __FUNCTION__);
+ }
+ s->vrc = value & 0x7f;
+ break;
+ case 0x08: /* GCR */
+ s->gcr = value & 3;
+ break;
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_pwt_ops = {
+ .read =omap_pwt_read,
+ .write = omap_pwt_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_pwt_reset(struct omap_pwt_s *s)
+{
+ s->frc = 0;
+ s->vrc = 0;
+ s->gcr = 0;
+}
+
+static struct omap_pwt_s *omap_pwt_init(MemoryRegion *system_memory,
+ hwaddr base,
+ omap_clk clk)
+{
+ struct omap_pwt_s *s = g_malloc0(sizeof(*s));
+ s->clk = clk;
+ omap_pwt_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_pwt_ops, s,
+ "omap-pwt", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+ return s;
+}
+
+/* Real-time Clock module */
+struct omap_rtc_s {
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq alarm;
+ QEMUTimer *clk;
+
+ uint8_t interrupts;
+ uint8_t status;
+ int16_t comp_reg;
+ int running;
+ int pm_am;
+ int auto_comp;
+ int round;
+ struct tm alarm_tm;
+ time_t alarm_ti;
+
+ struct tm current_tm;
+ time_t ti;
+ uint64_t tick;
+};
+
+static void omap_rtc_interrupts_update(struct omap_rtc_s *s)
+{
+ /* s->alarm is level-triggered */
+ qemu_set_irq(s->alarm, (s->status >> 6) & 1);
+}
+
+static void omap_rtc_alarm_update(struct omap_rtc_s *s)
+{
+ s->alarm_ti = mktimegm(&s->alarm_tm);
+ if (s->alarm_ti == -1)
+ printf("%s: conversion failed\n", __FUNCTION__);
+}
+
+static uint64_t omap_rtc_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint8_t i;
+
+ if (size != 1) {
+ return omap_badwidth_read8(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* SECONDS_REG */
+ return to_bcd(s->current_tm.tm_sec);
+
+ case 0x04: /* MINUTES_REG */
+ return to_bcd(s->current_tm.tm_min);
+
+ case 0x08: /* HOURS_REG */
+ if (s->pm_am)
+ return ((s->current_tm.tm_hour > 11) << 7) |
+ to_bcd(((s->current_tm.tm_hour - 1) % 12) + 1);
+ else
+ return to_bcd(s->current_tm.tm_hour);
+
+ case 0x0c: /* DAYS_REG */
+ return to_bcd(s->current_tm.tm_mday);
+
+ case 0x10: /* MONTHS_REG */
+ return to_bcd(s->current_tm.tm_mon + 1);
+
+ case 0x14: /* YEARS_REG */
+ return to_bcd(s->current_tm.tm_year % 100);
+
+ case 0x18: /* WEEK_REG */
+ return s->current_tm.tm_wday;
+
+ case 0x20: /* ALARM_SECONDS_REG */
+ return to_bcd(s->alarm_tm.tm_sec);
+
+ case 0x24: /* ALARM_MINUTES_REG */
+ return to_bcd(s->alarm_tm.tm_min);
+
+ case 0x28: /* ALARM_HOURS_REG */
+ if (s->pm_am)
+ return ((s->alarm_tm.tm_hour > 11) << 7) |
+ to_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1);
+ else
+ return to_bcd(s->alarm_tm.tm_hour);
+
+ case 0x2c: /* ALARM_DAYS_REG */
+ return to_bcd(s->alarm_tm.tm_mday);
+
+ case 0x30: /* ALARM_MONTHS_REG */
+ return to_bcd(s->alarm_tm.tm_mon + 1);
+
+ case 0x34: /* ALARM_YEARS_REG */
+ return to_bcd(s->alarm_tm.tm_year % 100);
+
+ case 0x40: /* RTC_CTRL_REG */
+ return (s->pm_am << 3) | (s->auto_comp << 2) |
+ (s->round << 1) | s->running;
+
+ case 0x44: /* RTC_STATUS_REG */
+ i = s->status;
+ s->status &= ~0x3d;
+ return i;
+
+ case 0x48: /* RTC_INTERRUPTS_REG */
+ return s->interrupts;
+
+ case 0x4c: /* RTC_COMP_LSB_REG */
+ return ((uint16_t) s->comp_reg) & 0xff;
+
+ case 0x50: /* RTC_COMP_MSB_REG */
+ return ((uint16_t) s->comp_reg) >> 8;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_rtc_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ struct tm new_tm;
+ time_t ti[2];
+
+ if (size != 1) {
+ return omap_badwidth_write8(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* SECONDS_REG */
+#ifdef ALMDEBUG
+ printf("RTC SEC_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_sec;
+ s->ti += from_bcd(value);
+ return;
+
+ case 0x04: /* MINUTES_REG */
+#ifdef ALMDEBUG
+ printf("RTC MIN_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_min * 60;
+ s->ti += from_bcd(value) * 60;
+ return;
+
+ case 0x08: /* HOURS_REG */
+#ifdef ALMDEBUG
+ printf("RTC HRS_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_hour * 3600;
+ if (s->pm_am) {
+ s->ti += (from_bcd(value & 0x3f) & 12) * 3600;
+ s->ti += ((value >> 7) & 1) * 43200;
+ } else
+ s->ti += from_bcd(value & 0x3f) * 3600;
+ return;
+
+ case 0x0c: /* DAYS_REG */
+#ifdef ALMDEBUG
+ printf("RTC DAY_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_mday * 86400;
+ s->ti += from_bcd(value) * 86400;
+ return;
+
+ case 0x10: /* MONTHS_REG */
+#ifdef ALMDEBUG
+ printf("RTC MTH_REG <-- %02x\n", value);
+#endif
+ memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
+ new_tm.tm_mon = from_bcd(value);
+ ti[0] = mktimegm(&s->current_tm);
+ ti[1] = mktimegm(&new_tm);
+
+ if (ti[0] != -1 && ti[1] != -1) {
+ s->ti -= ti[0];
+ s->ti += ti[1];
+ } else {
+ /* A less accurate version */
+ s->ti -= s->current_tm.tm_mon * 2592000;
+ s->ti += from_bcd(value) * 2592000;
+ }
+ return;
+
+ case 0x14: /* YEARS_REG */
+#ifdef ALMDEBUG
+ printf("RTC YRS_REG <-- %02x\n", value);
+#endif
+ memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
+ new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100);
+ ti[0] = mktimegm(&s->current_tm);
+ ti[1] = mktimegm(&new_tm);
+
+ if (ti[0] != -1 && ti[1] != -1) {
+ s->ti -= ti[0];
+ s->ti += ti[1];
+ } else {
+ /* A less accurate version */
+ s->ti -= (s->current_tm.tm_year % 100) * 31536000;
+ s->ti += from_bcd(value) * 31536000;
+ }
+ return;
+
+ case 0x18: /* WEEK_REG */
+ return; /* Ignored */
+
+ case 0x20: /* ALARM_SECONDS_REG */
+#ifdef ALMDEBUG
+ printf("ALM SEC_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_sec = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x24: /* ALARM_MINUTES_REG */
+#ifdef ALMDEBUG
+ printf("ALM MIN_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_min = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x28: /* ALARM_HOURS_REG */
+#ifdef ALMDEBUG
+ printf("ALM HRS_REG <-- %02x\n", value);
+#endif
+ if (s->pm_am)
+ s->alarm_tm.tm_hour =
+ ((from_bcd(value & 0x3f)) % 12) +
+ ((value >> 7) & 1) * 12;
+ else
+ s->alarm_tm.tm_hour = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x2c: /* ALARM_DAYS_REG */
+#ifdef ALMDEBUG
+ printf("ALM DAY_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_mday = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x30: /* ALARM_MONTHS_REG */
+#ifdef ALMDEBUG
+ printf("ALM MON_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_mon = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x34: /* ALARM_YEARS_REG */
+#ifdef ALMDEBUG
+ printf("ALM YRS_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_year = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x40: /* RTC_CTRL_REG */
+#ifdef ALMDEBUG
+ printf("RTC CONTROL <-- %02x\n", value);
+#endif
+ s->pm_am = (value >> 3) & 1;
+ s->auto_comp = (value >> 2) & 1;
+ s->round = (value >> 1) & 1;
+ s->running = value & 1;
+ s->status &= 0xfd;
+ s->status |= s->running << 1;
+ return;
+
+ case 0x44: /* RTC_STATUS_REG */
+#ifdef ALMDEBUG
+ printf("RTC STATUSL <-- %02x\n", value);
+#endif
+ s->status &= ~((value & 0xc0) ^ 0x80);
+ omap_rtc_interrupts_update(s);
+ return;
+
+ case 0x48: /* RTC_INTERRUPTS_REG */
+#ifdef ALMDEBUG
+ printf("RTC INTRS <-- %02x\n", value);
+#endif
+ s->interrupts = value;
+ return;
+
+ case 0x4c: /* RTC_COMP_LSB_REG */
+#ifdef ALMDEBUG
+ printf("RTC COMPLSB <-- %02x\n", value);
+#endif
+ s->comp_reg &= 0xff00;
+ s->comp_reg |= 0x00ff & value;
+ return;
+
+ case 0x50: /* RTC_COMP_MSB_REG */
+#ifdef ALMDEBUG
+ printf("RTC COMPMSB <-- %02x\n", value);
+#endif
+ s->comp_reg &= 0x00ff;
+ s->comp_reg |= 0xff00 & (value << 8);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_rtc_ops = {
+ .read = omap_rtc_read,
+ .write = omap_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_rtc_tick(void *opaque)
+{
+ struct omap_rtc_s *s = opaque;
+
+ if (s->round) {
+ /* Round to nearest full minute. */
+ if (s->current_tm.tm_sec < 30)
+ s->ti -= s->current_tm.tm_sec;
+ else
+ s->ti += 60 - s->current_tm.tm_sec;
+
+ s->round = 0;
+ }
+
+ localtime_r(&s->ti, &s->current_tm);
+
+ if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) {
+ s->status |= 0x40;
+ omap_rtc_interrupts_update(s);
+ }
+
+ if (s->interrupts & 0x04)
+ switch (s->interrupts & 3) {
+ case 0:
+ s->status |= 0x04;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 1:
+ if (s->current_tm.tm_sec)
+ break;
+ s->status |= 0x08;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 2:
+ if (s->current_tm.tm_sec || s->current_tm.tm_min)
+ break;
+ s->status |= 0x10;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 3:
+ if (s->current_tm.tm_sec ||
+ s->current_tm.tm_min || s->current_tm.tm_hour)
+ break;
+ s->status |= 0x20;
+ qemu_irq_pulse(s->irq);
+ break;
+ }
+
+ /* Move on */
+ if (s->running)
+ s->ti ++;
+ s->tick += 1000;
+
+ /*
+ * Every full hour add a rough approximation of the compensation
+ * register to the 32kHz Timer (which drives the RTC) value.
+ */
+ if (s->auto_comp && !s->current_tm.tm_sec && !s->current_tm.tm_min)
+ s->tick += s->comp_reg * 1000 / 32768;
+
+ qemu_mod_timer(s->clk, s->tick);
+}
+
+static void omap_rtc_reset(struct omap_rtc_s *s)
+{
+ struct tm tm;
+
+ s->interrupts = 0;
+ s->comp_reg = 0;
+ s->running = 0;
+ s->pm_am = 0;
+ s->auto_comp = 0;
+ s->round = 0;
+ s->tick = qemu_get_clock_ms(rtc_clock);
+ memset(&s->alarm_tm, 0, sizeof(s->alarm_tm));
+ s->alarm_tm.tm_mday = 0x01;
+ s->status = 1 << 7;
+ qemu_get_timedate(&tm, 0);
+ s->ti = mktimegm(&tm);
+
+ omap_rtc_alarm_update(s);
+ omap_rtc_tick(s);
+}
+
+static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory,
+ hwaddr base,
+ qemu_irq timerirq, qemu_irq alarmirq,
+ omap_clk clk)
+{
+ struct omap_rtc_s *s = (struct omap_rtc_s *)
+ g_malloc0(sizeof(struct omap_rtc_s));
+
+ s->irq = timerirq;
+ s->alarm = alarmirq;
+ s->clk = qemu_new_timer_ms(rtc_clock, omap_rtc_tick, s);
+
+ omap_rtc_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_rtc_ops, s,
+ "omap-rtc", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ return s;
+}
+
+/* Multi-channel Buffered Serial Port interfaces */
+struct omap_mcbsp_s {
+ MemoryRegion iomem;
+ qemu_irq txirq;
+ qemu_irq rxirq;
+ qemu_irq txdrq;
+ qemu_irq rxdrq;
+
+ uint16_t spcr[2];
+ uint16_t rcr[2];
+ uint16_t xcr[2];
+ uint16_t srgr[2];
+ uint16_t mcr[2];
+ uint16_t pcr;
+ uint16_t rcer[8];
+ uint16_t xcer[8];
+ int tx_rate;
+ int rx_rate;
+ int tx_req;
+ int rx_req;
+
+ I2SCodec *codec;
+ QEMUTimer *source_timer;
+ QEMUTimer *sink_timer;
+};
+
+static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s)
+{
+ int irq;
+
+ switch ((s->spcr[0] >> 4) & 3) { /* RINTM */
+ case 0:
+ irq = (s->spcr[0] >> 1) & 1; /* RRDY */
+ break;
+ case 3:
+ irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */
+ break;
+ default:
+ irq = 0;
+ break;
+ }
+
+ if (irq)
+ qemu_irq_pulse(s->rxirq);
+
+ switch ((s->spcr[1] >> 4) & 3) { /* XINTM */
+ case 0:
+ irq = (s->spcr[1] >> 1) & 1; /* XRDY */
+ break;
+ case 3:
+ irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */
+ break;
+ default:
+ irq = 0;
+ break;
+ }
+
+ if (irq)
+ qemu_irq_pulse(s->txirq);
+}
+
+static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s)
+{
+ if ((s->spcr[0] >> 1) & 1) /* RRDY */
+ s->spcr[0] |= 1 << 2; /* RFULL */
+ s->spcr[0] |= 1 << 1; /* RRDY */
+ qemu_irq_raise(s->rxdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_source_tick(void *opaque)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 };
+
+ if (!s->rx_rate)
+ return;
+ if (s->rx_req)
+ printf("%s: Rx FIFO overrun\n", __FUNCTION__);
+
+ s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7];
+
+ omap_mcbsp_rx_newdata(s);
+ qemu_mod_timer(s->source_timer, qemu_get_clock_ns(vm_clock) +
+ get_ticks_per_sec());
+}
+
+static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s)
+{
+ if (!s->codec || !s->codec->rts)
+ omap_mcbsp_source_tick(s);
+ else if (s->codec->in.len) {
+ s->rx_req = s->codec->in.len;
+ omap_mcbsp_rx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s)
+{
+ qemu_del_timer(s->source_timer);
+}
+
+static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s)
+{
+ s->spcr[0] &= ~(1 << 1); /* RRDY */
+ qemu_irq_lower(s->rxdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s)
+{
+ s->spcr[1] |= 1 << 1; /* XRDY */
+ qemu_irq_raise(s->txdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_sink_tick(void *opaque)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 };
+
+ if (!s->tx_rate)
+ return;
+ if (s->tx_req)
+ printf("%s: Tx FIFO underrun\n", __FUNCTION__);
+
+ s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7];
+
+ omap_mcbsp_tx_newdata(s);
+ qemu_mod_timer(s->sink_timer, qemu_get_clock_ns(vm_clock) +
+ get_ticks_per_sec());
+}
+
+static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s)
+{
+ if (!s->codec || !s->codec->cts)
+ omap_mcbsp_sink_tick(s);
+ else if (s->codec->out.size) {
+ s->tx_req = s->codec->out.size;
+ omap_mcbsp_tx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s)
+{
+ s->spcr[1] &= ~(1 << 1); /* XRDY */
+ qemu_irq_lower(s->txdrq);
+ omap_mcbsp_intr_update(s);
+ if (s->codec && s->codec->cts)
+ s->codec->tx_swallow(s->codec->opaque);
+}
+
+static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s)
+{
+ s->tx_req = 0;
+ omap_mcbsp_tx_done(s);
+ qemu_del_timer(s->sink_timer);
+}
+
+static void omap_mcbsp_req_update(struct omap_mcbsp_s *s)
+{
+ int prev_rx_rate, prev_tx_rate;
+ int rx_rate = 0, tx_rate = 0;
+ int cpu_rate = 1500000; /* XXX */
+
+ /* TODO: check CLKSTP bit */
+ if (s->spcr[1] & (1 << 6)) { /* GRST */
+ if (s->spcr[0] & (1 << 0)) { /* RRST */
+ if ((s->srgr[1] & (1 << 13)) && /* CLKSM */
+ (s->pcr & (1 << 8))) { /* CLKRM */
+ if (~s->pcr & (1 << 7)) /* SCLKME */
+ rx_rate = cpu_rate /
+ ((s->srgr[0] & 0xff) + 1); /* CLKGDV */
+ } else
+ if (s->codec)
+ rx_rate = s->codec->rx_rate;
+ }
+
+ if (s->spcr[1] & (1 << 0)) { /* XRST */
+ if ((s->srgr[1] & (1 << 13)) && /* CLKSM */
+ (s->pcr & (1 << 9))) { /* CLKXM */
+ if (~s->pcr & (1 << 7)) /* SCLKME */
+ tx_rate = cpu_rate /
+ ((s->srgr[0] & 0xff) + 1); /* CLKGDV */
+ } else
+ if (s->codec)
+ tx_rate = s->codec->tx_rate;
+ }
+ }
+ prev_tx_rate = s->tx_rate;
+ prev_rx_rate = s->rx_rate;
+ s->tx_rate = tx_rate;
+ s->rx_rate = rx_rate;
+
+ if (s->codec)
+ s->codec->set_rate(s->codec->opaque, rx_rate, tx_rate);
+
+ if (!prev_tx_rate && tx_rate)
+ omap_mcbsp_tx_start(s);
+ else if (s->tx_rate && !tx_rate)
+ omap_mcbsp_tx_stop(s);
+
+ if (!prev_rx_rate && rx_rate)
+ omap_mcbsp_rx_start(s);
+ else if (prev_tx_rate && !tx_rate)
+ omap_mcbsp_rx_stop(s);
+}
+
+static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t ret;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* DRR2 */
+ if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */
+ return 0x0000;
+ /* Fall through. */
+ case 0x02: /* DRR1 */
+ if (s->rx_req < 2) {
+ printf("%s: Rx FIFO underrun\n", __FUNCTION__);
+ omap_mcbsp_rx_done(s);
+ } else {
+ s->tx_req -= 2;
+ if (s->codec && s->codec->in.len >= 2) {
+ ret = s->codec->in.fifo[s->codec->in.start ++] << 8;
+ ret |= s->codec->in.fifo[s->codec->in.start ++];
+ s->codec->in.len -= 2;
+ } else
+ ret = 0x0000;
+ if (!s->tx_req)
+ omap_mcbsp_rx_done(s);
+ return ret;
+ }
+ return 0x0000;
+
+ case 0x04: /* DXR2 */
+ case 0x06: /* DXR1 */
+ return 0x0000;
+
+ case 0x08: /* SPCR2 */
+ return s->spcr[1];
+ case 0x0a: /* SPCR1 */
+ return s->spcr[0];
+ case 0x0c: /* RCR2 */
+ return s->rcr[1];
+ case 0x0e: /* RCR1 */
+ return s->rcr[0];
+ case 0x10: /* XCR2 */
+ return s->xcr[1];
+ case 0x12: /* XCR1 */
+ return s->xcr[0];
+ case 0x14: /* SRGR2 */
+ return s->srgr[1];
+ case 0x16: /* SRGR1 */
+ return s->srgr[0];
+ case 0x18: /* MCR2 */
+ return s->mcr[1];
+ case 0x1a: /* MCR1 */
+ return s->mcr[0];
+ case 0x1c: /* RCERA */
+ return s->rcer[0];
+ case 0x1e: /* RCERB */
+ return s->rcer[1];
+ case 0x20: /* XCERA */
+ return s->xcer[0];
+ case 0x22: /* XCERB */
+ return s->xcer[1];
+ case 0x24: /* PCR0 */
+ return s->pcr;
+ case 0x26: /* RCERC */
+ return s->rcer[2];
+ case 0x28: /* RCERD */
+ return s->rcer[3];
+ case 0x2a: /* XCERC */
+ return s->xcer[2];
+ case 0x2c: /* XCERD */
+ return s->xcer[3];
+ case 0x2e: /* RCERE */
+ return s->rcer[4];
+ case 0x30: /* RCERF */
+ return s->rcer[5];
+ case 0x32: /* XCERE */
+ return s->xcer[4];
+ case 0x34: /* XCERF */
+ return s->xcer[5];
+ case 0x36: /* RCERG */
+ return s->rcer[6];
+ case 0x38: /* RCERH */
+ return s->rcer[7];
+ case 0x3a: /* XCERG */
+ return s->xcer[6];
+ case 0x3c: /* XCERH */
+ return s->xcer[7];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mcbsp_writeh(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* DRR2 */
+ case 0x02: /* DRR1 */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x04: /* DXR2 */
+ if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */
+ return;
+ /* Fall through. */
+ case 0x06: /* DXR1 */
+ if (s->tx_req > 1) {
+ s->tx_req -= 2;
+ if (s->codec && s->codec->cts) {
+ s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff;
+ }
+ if (s->tx_req < 2)
+ omap_mcbsp_tx_done(s);
+ } else
+ printf("%s: Tx FIFO overrun\n", __FUNCTION__);
+ return;
+
+ case 0x08: /* SPCR2 */
+ s->spcr[1] &= 0x0002;
+ s->spcr[1] |= 0x03f9 & value;
+ s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */
+ if (~value & 1) /* XRST */
+ s->spcr[1] &= ~6;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x0a: /* SPCR1 */
+ s->spcr[0] &= 0x0006;
+ s->spcr[0] |= 0xf8f9 & value;
+ if (value & (1 << 15)) /* DLB */
+ printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__);
+ if (~value & 1) { /* RRST */
+ s->spcr[0] &= ~6;
+ s->rx_req = 0;
+ omap_mcbsp_rx_done(s);
+ }
+ omap_mcbsp_req_update(s);
+ return;
+
+ case 0x0c: /* RCR2 */
+ s->rcr[1] = value & 0xffff;
+ return;
+ case 0x0e: /* RCR1 */
+ s->rcr[0] = value & 0x7fe0;
+ return;
+ case 0x10: /* XCR2 */
+ s->xcr[1] = value & 0xffff;
+ return;
+ case 0x12: /* XCR1 */
+ s->xcr[0] = value & 0x7fe0;
+ return;
+ case 0x14: /* SRGR2 */
+ s->srgr[1] = value & 0xffff;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x16: /* SRGR1 */
+ s->srgr[0] = value & 0xffff;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x18: /* MCR2 */
+ s->mcr[1] = value & 0x03e3;
+ if (value & 3) /* XMCM */
+ printf("%s: Tx channel selection mode enable attempt\n",
+ __FUNCTION__);
+ return;
+ case 0x1a: /* MCR1 */
+ s->mcr[0] = value & 0x03e1;
+ if (value & 1) /* RMCM */
+ printf("%s: Rx channel selection mode enable attempt\n",
+ __FUNCTION__);
+ return;
+ case 0x1c: /* RCERA */
+ s->rcer[0] = value & 0xffff;
+ return;
+ case 0x1e: /* RCERB */
+ s->rcer[1] = value & 0xffff;
+ return;
+ case 0x20: /* XCERA */
+ s->xcer[0] = value & 0xffff;
+ return;
+ case 0x22: /* XCERB */
+ s->xcer[1] = value & 0xffff;
+ return;
+ case 0x24: /* PCR0 */
+ s->pcr = value & 0x7faf;
+ return;
+ case 0x26: /* RCERC */
+ s->rcer[2] = value & 0xffff;
+ return;
+ case 0x28: /* RCERD */
+ s->rcer[3] = value & 0xffff;
+ return;
+ case 0x2a: /* XCERC */
+ s->xcer[2] = value & 0xffff;
+ return;
+ case 0x2c: /* XCERD */
+ s->xcer[3] = value & 0xffff;
+ return;
+ case 0x2e: /* RCERE */
+ s->rcer[4] = value & 0xffff;
+ return;
+ case 0x30: /* RCERF */
+ s->rcer[5] = value & 0xffff;
+ return;
+ case 0x32: /* XCERE */
+ s->xcer[4] = value & 0xffff;
+ return;
+ case 0x34: /* XCERF */
+ s->xcer[5] = value & 0xffff;
+ return;
+ case 0x36: /* RCERG */
+ s->rcer[6] = value & 0xffff;
+ return;
+ case 0x38: /* RCERH */
+ s->rcer[7] = value & 0xffff;
+ return;
+ case 0x3a: /* XCERG */
+ s->xcer[6] = value & 0xffff;
+ return;
+ case 0x3c: /* XCERH */
+ s->xcer[7] = value & 0xffff;
+ return;
+ }
+
+ OMAP_BAD_REG(addr);
+}
+
+static void omap_mcbsp_writew(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (offset == 0x04) { /* DXR */
+ if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */
+ return;
+ if (s->tx_req > 3) {
+ s->tx_req -= 4;
+ if (s->codec && s->codec->cts) {
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 24) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 16) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 8) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 0) & 0xff;
+ }
+ if (s->tx_req < 4)
+ omap_mcbsp_tx_done(s);
+ } else
+ printf("%s: Tx FIFO overrun\n", __FUNCTION__);
+ return;
+ }
+
+ omap_badwidth_write16(opaque, addr, value);
+}
+
+static void omap_mcbsp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 2: return omap_mcbsp_writeh(opaque, addr, value);
+ case 4: return omap_mcbsp_writew(opaque, addr, value);
+ default: return omap_badwidth_write16(opaque, addr, value);
+ }
+}
+
+static const MemoryRegionOps omap_mcbsp_ops = {
+ .read = omap_mcbsp_read,
+ .write = omap_mcbsp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_mcbsp_reset(struct omap_mcbsp_s *s)
+{
+ memset(&s->spcr, 0, sizeof(s->spcr));
+ memset(&s->rcr, 0, sizeof(s->rcr));
+ memset(&s->xcr, 0, sizeof(s->xcr));
+ s->srgr[0] = 0x0001;
+ s->srgr[1] = 0x2000;
+ memset(&s->mcr, 0, sizeof(s->mcr));
+ memset(&s->pcr, 0, sizeof(s->pcr));
+ memset(&s->rcer, 0, sizeof(s->rcer));
+ memset(&s->xcer, 0, sizeof(s->xcer));
+ s->tx_req = 0;
+ s->rx_req = 0;
+ s->tx_rate = 0;
+ s->rx_rate = 0;
+ qemu_del_timer(s->source_timer);
+ qemu_del_timer(s->sink_timer);
+}
+
+static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory,
+ hwaddr base,
+ qemu_irq txirq, qemu_irq rxirq,
+ qemu_irq *dma, omap_clk clk)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *)
+ g_malloc0(sizeof(struct omap_mcbsp_s));
+
+ s->txirq = txirq;
+ s->rxirq = rxirq;
+ s->txdrq = dma[0];
+ s->rxdrq = dma[1];
+ s->sink_timer = qemu_new_timer_ns(vm_clock, omap_mcbsp_sink_tick, s);
+ s->source_timer = qemu_new_timer_ns(vm_clock, omap_mcbsp_source_tick, s);
+ omap_mcbsp_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_mcbsp_ops, s, "omap-mcbsp", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ return s;
+}
+
+static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+
+ if (s->rx_rate) {
+ s->rx_req = s->codec->in.len;
+ omap_mcbsp_rx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_i2s_start(void *opaque, int line, int level)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+
+ if (s->tx_rate) {
+ s->tx_req = s->codec->out.size;
+ omap_mcbsp_tx_newdata(s);
+ }
+}
+
+void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave)
+{
+ s->codec = slave;
+ slave->rx_swallow = qemu_allocate_irqs(omap_mcbsp_i2s_swallow, s, 1)[0];
+ slave->tx_start = qemu_allocate_irqs(omap_mcbsp_i2s_start, s, 1)[0];
+}
+
+/* LED Pulse Generators */
+struct omap_lpg_s {
+ MemoryRegion iomem;
+ QEMUTimer *tm;
+
+ uint8_t control;
+ uint8_t power;
+ int64_t on;
+ int64_t period;
+ int clk;
+ int cycle;
+};
+
+static void omap_lpg_tick(void *opaque)
+{
+ struct omap_lpg_s *s = opaque;
+
+ if (s->cycle)
+ qemu_mod_timer(s->tm, qemu_get_clock_ms(vm_clock) + s->period - s->on);
+ else
+ qemu_mod_timer(s->tm, qemu_get_clock_ms(vm_clock) + s->on);
+
+ s->cycle = !s->cycle;
+ printf("%s: LED is %s\n", __FUNCTION__, s->cycle ? "on" : "off");
+}
+
+static void omap_lpg_update(struct omap_lpg_s *s)
+{
+ int64_t on, period = 1, ticks = 1000;
+ static const int per[8] = { 1, 2, 4, 8, 12, 16, 20, 24 };
+
+ if (~s->control & (1 << 6)) /* LPGRES */
+ on = 0;
+ else if (s->control & (1 << 7)) /* PERM_ON */
+ on = period;
+ else {
+ period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */
+ 256 / 32);
+ on = (s->clk && s->power) ? muldiv64(ticks,
+ per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */
+ }
+
+ qemu_del_timer(s->tm);
+ if (on == period && s->on < s->period)
+ printf("%s: LED is on\n", __FUNCTION__);
+ else if (on == 0 && s->on)
+ printf("%s: LED is off\n", __FUNCTION__);
+ else if (on && (on != s->on || period != s->period)) {
+ s->cycle = 0;
+ s->on = on;
+ s->period = period;
+ omap_lpg_tick(s);
+ return;
+ }
+
+ s->on = on;
+ s->period = period;
+}
+
+static void omap_lpg_reset(struct omap_lpg_s *s)
+{
+ s->control = 0x00;
+ s->power = 0x00;
+ s->clk = 1;
+ omap_lpg_update(s);
+}
+
+static uint64_t omap_lpg_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_read8(opaque, addr);
+ }
+
+ switch (offset) {
+ case 0x00: /* LCR */
+ return s->control;
+
+ case 0x04: /* PMR */
+ return s->power;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_lpg_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (size != 1) {
+ return omap_badwidth_write8(opaque, addr, value);
+ }
+
+ switch (offset) {
+ case 0x00: /* LCR */
+ if (~value & (1 << 6)) /* LPGRES */
+ omap_lpg_reset(s);
+ s->control = value & 0xff;
+ omap_lpg_update(s);
+ return;
+
+ case 0x04: /* PMR */
+ s->power = value & 0x01;
+ omap_lpg_update(s);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_lpg_ops = {
+ .read = omap_lpg_read,
+ .write = omap_lpg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_lpg_clk_update(void *opaque, int line, int on)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+
+ s->clk = on;
+ omap_lpg_update(s);
+}
+
+static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory,
+ hwaddr base, omap_clk clk)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *)
+ g_malloc0(sizeof(struct omap_lpg_s));
+
+ s->tm = qemu_new_timer_ms(vm_clock, omap_lpg_tick, s);
+
+ omap_lpg_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_lpg_ops, s, "omap-lpg", 0x800);
+ memory_region_add_subregion(system_memory, base, &s->iomem);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_lpg_clk_update, s, 1)[0]);
+
+ return s;
+}
+
+/* MPUI Peripheral Bridge configuration */
+static uint64_t omap_mpui_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ if (addr == OMAP_MPUI_BASE) /* CMR */
+ return 0xfe4d;
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpui_io_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ /* FIXME: infinite loop */
+ omap_badwidth_write16(opaque, addr, value);
+}
+
+static const MemoryRegionOps omap_mpui_io_ops = {
+ .read = omap_mpui_io_read,
+ .write = omap_mpui_io_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_setup_mpui_io(MemoryRegion *system_memory,
+ struct omap_mpu_state_s *mpu)
+{
+ memory_region_init_io(&mpu->mpui_io_iomem, &omap_mpui_io_ops, mpu,
+ "omap-mpui-io", 0x7fff);
+ memory_region_add_subregion(system_memory, OMAP_MPUI_BASE,
+ &mpu->mpui_io_iomem);
+}
+
+/* General chip reset */
+static void omap1_mpu_reset(void *opaque)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ omap_dma_reset(mpu->dma);
+ omap_mpu_timer_reset(mpu->timer[0]);
+ omap_mpu_timer_reset(mpu->timer[1]);
+ omap_mpu_timer_reset(mpu->timer[2]);
+ omap_wd_timer_reset(mpu->wdt);
+ omap_os_timer_reset(mpu->os_timer);
+ omap_lcdc_reset(mpu->lcd);
+ omap_ulpd_pm_reset(mpu);
+ omap_pin_cfg_reset(mpu);
+ omap_mpui_reset(mpu);
+ omap_tipb_bridge_reset(mpu->private_tipb);
+ omap_tipb_bridge_reset(mpu->public_tipb);
+ omap_dpll_reset(mpu->dpll[0]);
+ omap_dpll_reset(mpu->dpll[1]);
+ omap_dpll_reset(mpu->dpll[2]);
+ omap_uart_reset(mpu->uart[0]);
+ omap_uart_reset(mpu->uart[1]);
+ omap_uart_reset(mpu->uart[2]);
+ omap_mmc_reset(mpu->mmc);
+ omap_mpuio_reset(mpu->mpuio);
+ omap_uwire_reset(mpu->microwire);
+ omap_pwl_reset(mpu->pwl);
+ omap_pwt_reset(mpu->pwt);
+ omap_rtc_reset(mpu->rtc);
+ omap_mcbsp_reset(mpu->mcbsp1);
+ omap_mcbsp_reset(mpu->mcbsp2);
+ omap_mcbsp_reset(mpu->mcbsp3);
+ omap_lpg_reset(mpu->led[0]);
+ omap_lpg_reset(mpu->led[1]);
+ omap_clkm_reset(mpu);
+ cpu_reset(CPU(mpu->cpu));
+}
+
+static const struct omap_map_s {
+ hwaddr phys_dsp;
+ hwaddr phys_mpu;
+ uint32_t size;
+ const char *name;
+} omap15xx_dsp_mm[] = {
+ /* Strobe 0 */
+ { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */
+ { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */
+ { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */
+ { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */
+ { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */
+ { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */
+ { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */
+ { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */
+ { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */
+ { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */
+ { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */
+ { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */
+ { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */
+ { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */
+ { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */
+ { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */
+ { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */
+ /* Strobe 1 */
+ { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */
+
+ { 0 }
+};
+
+static void omap_setup_dsp_mapping(MemoryRegion *system_memory,
+ const struct omap_map_s *map)
+{
+ MemoryRegion *io;
+
+ for (; map->phys_dsp; map ++) {
+ io = g_new(MemoryRegion, 1);
+ memory_region_init_alias(io, map->name,
+ system_memory, map->phys_mpu, map->size);
+ memory_region_add_subregion(system_memory, map->phys_dsp, io);
+ }
+}
+
+void omap_mpu_wakeup(void *opaque, int irq, int req)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ if (mpu->cpu->env.halted) {
+ cpu_interrupt(&mpu->cpu->env, CPU_INTERRUPT_EXITTB);
+ }
+}
+
+static const struct dma_irq_map omap1_dma_irq_map[] = {
+ { 0, OMAP_INT_DMA_CH0_6 },
+ { 0, OMAP_INT_DMA_CH1_7 },
+ { 0, OMAP_INT_DMA_CH2_8 },
+ { 0, OMAP_INT_DMA_CH3 },
+ { 0, OMAP_INT_DMA_CH4 },
+ { 0, OMAP_INT_DMA_CH5 },
+ { 1, OMAP_INT_1610_DMA_CH6 },
+ { 1, OMAP_INT_1610_DMA_CH7 },
+ { 1, OMAP_INT_1610_DMA_CH8 },
+ { 1, OMAP_INT_1610_DMA_CH9 },
+ { 1, OMAP_INT_1610_DMA_CH10 },
+ { 1, OMAP_INT_1610_DMA_CH11 },
+ { 1, OMAP_INT_1610_DMA_CH12 },
+ { 1, OMAP_INT_1610_DMA_CH13 },
+ { 1, OMAP_INT_1610_DMA_CH14 },
+ { 1, OMAP_INT_1610_DMA_CH15 }
+};
+
+/* DMA ports for OMAP1 */
+static int omap_validate_emiff_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(OMAP_EMIFF_BASE, s->sdram_size, addr);
+}
+
+static int omap_validate_emifs_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(OMAP_EMIFS_BASE, OMAP_EMIFF_BASE - OMAP_EMIFS_BASE,
+ addr);
+}
+
+static int omap_validate_imif_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(OMAP_IMIF_BASE, s->sram_size, addr);
+}
+
+static int omap_validate_tipb_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(0xfffb0000, 0xffff0000 - 0xfffb0000, addr);
+}
+
+static int omap_validate_local_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(OMAP_LOCALBUS_BASE, 0x1000000, addr);
+}
+
+static int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return range_covers_byte(0xe1010000, 0xe1020004 - 0xe1010000, addr);
+}
+
+struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
+ unsigned long sdram_size,
+ const char *core)
+{
+ int i;
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
+ g_malloc0(sizeof(struct omap_mpu_state_s));
+ qemu_irq *cpu_irq;
+ qemu_irq dma_irqs[6];
+ DriveInfo *dinfo;
+ SysBusDevice *busdev;
+
+ if (!core)
+ core = "ti925t";
+
+ /* Core */
+ s->mpu_model = omap310;
+ s->cpu = cpu_arm_init(core);
+ if (s->cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->sdram_size = sdram_size;
+ s->sram_size = OMAP15XX_SRAM_SIZE;
+
+ s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
+
+ /* Clocks */
+ omap_clk_init(s);
+
+ /* Memory-mapped stuff */
+ memory_region_init_ram(&s->emiff_ram, "omap1.dram", s->sdram_size);
+ vmstate_register_ram_global(&s->emiff_ram);
+ memory_region_add_subregion(system_memory, OMAP_EMIFF_BASE, &s->emiff_ram);
+ memory_region_init_ram(&s->imif_ram, "omap1.sram", s->sram_size);
+ vmstate_register_ram_global(&s->imif_ram);
+ memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram);
+
+ omap_clkm_init(system_memory, 0xfffece00, 0xe1008000, s);
+
+ cpu_irq = arm_pic_init_cpu(s->cpu);
+ s->ih[0] = qdev_create(NULL, "omap-intc");
+ qdev_prop_set_uint32(s->ih[0], "size", 0x100);
+ qdev_prop_set_ptr(s->ih[0], "clk", omap_findclk(s, "arminth_ck"));
+ qdev_init_nofail(s->ih[0]);
+ busdev = SYS_BUS_DEVICE(s->ih[0]);
+ sysbus_connect_irq(busdev, 0, cpu_irq[ARM_PIC_CPU_IRQ]);
+ sysbus_connect_irq(busdev, 1, cpu_irq[ARM_PIC_CPU_FIQ]);
+ sysbus_mmio_map(busdev, 0, 0xfffecb00);
+ s->ih[1] = qdev_create(NULL, "omap-intc");
+ qdev_prop_set_uint32(s->ih[1], "size", 0x800);
+ qdev_prop_set_ptr(s->ih[1], "clk", omap_findclk(s, "arminth_ck"));
+ qdev_init_nofail(s->ih[1]);
+ busdev = SYS_BUS_DEVICE(s->ih[1]);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_15XX_IH2_IRQ));
+ /* The second interrupt controller's FIQ output is not wired up */
+ sysbus_mmio_map(busdev, 0, 0xfffe0000);
+
+ for (i = 0; i < 6; i++) {
+ dma_irqs[i] = qdev_get_gpio_in(s->ih[omap1_dma_irq_map[i].ih],
+ omap1_dma_irq_map[i].intr);
+ }
+ s->dma = omap_dma_init(0xfffed800, dma_irqs, system_memory,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_DMA_LCD),
+ s, omap_findclk(s, "dma_ck"), omap_dma_3_1);
+
+ s->port[emiff ].addr_valid = omap_validate_emiff_addr;
+ s->port[emifs ].addr_valid = omap_validate_emifs_addr;
+ s->port[imif ].addr_valid = omap_validate_imif_addr;
+ s->port[tipb ].addr_valid = omap_validate_tipb_addr;
+ s->port[local ].addr_valid = omap_validate_local_addr;
+ s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr;
+
+ /* Register SDRAM and SRAM DMA ports for fast transfers. */
+ soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->emiff_ram),
+ OMAP_EMIFF_BASE, s->sdram_size);
+ soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->imif_ram),
+ OMAP_IMIF_BASE, s->sram_size);
+
+ s->timer[0] = omap_mpu_timer_init(system_memory, 0xfffec500,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER1),
+ omap_findclk(s, "mputim_ck"));
+ s->timer[1] = omap_mpu_timer_init(system_memory, 0xfffec600,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER2),
+ omap_findclk(s, "mputim_ck"));
+ s->timer[2] = omap_mpu_timer_init(system_memory, 0xfffec700,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER3),
+ omap_findclk(s, "mputim_ck"));
+
+ s->wdt = omap_wd_timer_init(system_memory, 0xfffec800,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_WD_TIMER),
+ omap_findclk(s, "armwdt_ck"));
+
+ s->os_timer = omap_os_timer_init(system_memory, 0xfffb9000,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_OS_TIMER),
+ omap_findclk(s, "clk32-kHz"));
+
+ s->lcd = omap_lcdc_init(system_memory, 0xfffec000,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_LCD_CTRL),
+ omap_dma_get_lcdch(s->dma),
+ omap_findclk(s, "lcd_ck"));
+
+ omap_ulpd_pm_init(system_memory, 0xfffe0800, s);
+ omap_pin_cfg_init(system_memory, 0xfffe1000, s);
+ omap_id_init(system_memory, s);
+
+ omap_mpui_init(system_memory, 0xfffec900, s);
+
+ s->private_tipb = omap_tipb_bridge_init(system_memory, 0xfffeca00,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_BRIDGE_PRIV),
+ omap_findclk(s, "tipb_ck"));
+ s->public_tipb = omap_tipb_bridge_init(system_memory, 0xfffed300,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_BRIDGE_PUB),
+ omap_findclk(s, "tipb_ck"));
+
+ omap_tcmi_init(system_memory, 0xfffecc00, s);
+
+ s->uart[0] = omap_uart_init(0xfffb0000,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_UART1),
+ omap_findclk(s, "uart1_ck"),
+ omap_findclk(s, "uart1_ck"),
+ s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX],
+ "uart1",
+ serial_hds[0]);
+ s->uart[1] = omap_uart_init(0xfffb0800,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_UART2),
+ omap_findclk(s, "uart2_ck"),
+ omap_findclk(s, "uart2_ck"),
+ s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX],
+ "uart2",
+ serial_hds[0] ? serial_hds[1] : NULL);
+ s->uart[2] = omap_uart_init(0xfffb9800,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_UART3),
+ omap_findclk(s, "uart3_ck"),
+ omap_findclk(s, "uart3_ck"),
+ s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX],
+ "uart3",
+ serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
+
+ s->dpll[0] = omap_dpll_init(system_memory, 0xfffecf00,
+ omap_findclk(s, "dpll1"));
+ s->dpll[1] = omap_dpll_init(system_memory, 0xfffed000,
+ omap_findclk(s, "dpll2"));
+ s->dpll[2] = omap_dpll_init(system_memory, 0xfffed100,
+ omap_findclk(s, "dpll3"));
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = omap_mmc_init(0xfffb7800, system_memory, dinfo->bdrv,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN),
+ &s->drq[OMAP_DMA_MMC_TX],
+ omap_findclk(s, "mmc_ck"));
+
+ s->mpuio = omap_mpuio_init(system_memory, 0xfffb5000,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_KEYBOARD),
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_MPUIO),
+ s->wakeup, omap_findclk(s, "clk32-kHz"));
+
+ s->gpio = qdev_create(NULL, "omap-gpio");
+ qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model);
+ qdev_prop_set_ptr(s->gpio, "clk", omap_findclk(s, "arm_gpio_ck"));
+ qdev_init_nofail(s->gpio);
+ sysbus_connect_irq(SYS_BUS_DEVICE(s->gpio), 0,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_GPIO_BANK1));
+ sysbus_mmio_map(SYS_BUS_DEVICE(s->gpio), 0, 0xfffce000);
+
+ s->microwire = omap_uwire_init(system_memory, 0xfffb3000,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireTX),
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireRX),
+ s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck"));
+
+ s->pwl = omap_pwl_init(system_memory, 0xfffb5800,
+ omap_findclk(s, "armxor_ck"));
+ s->pwt = omap_pwt_init(system_memory, 0xfffb6000,
+ omap_findclk(s, "armxor_ck"));
+
+ s->i2c[0] = qdev_create(NULL, "omap_i2c");
+ qdev_prop_set_uint8(s->i2c[0], "revision", 0x11);
+ qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck"));
+ qdev_init_nofail(s->i2c[0]);
+ busdev = SYS_BUS_DEVICE(s->i2c[0]);
+ sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C));
+ sysbus_connect_irq(busdev, 1, s->drq[OMAP_DMA_I2C_TX]);
+ sysbus_connect_irq(busdev, 2, s->drq[OMAP_DMA_I2C_RX]);
+ sysbus_mmio_map(busdev, 0, 0xfffb3800);
+
+ s->rtc = omap_rtc_init(system_memory, 0xfffb4800,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_TIMER),
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_ALARM),
+ omap_findclk(s, "clk32-kHz"));
+
+ s->mcbsp1 = omap_mcbsp_init(system_memory, 0xfffb1800,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP1TX),
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP1RX),
+ &s->drq[OMAP_DMA_MCBSP1_TX], omap_findclk(s, "dspxor_ck"));
+ s->mcbsp2 = omap_mcbsp_init(system_memory, 0xfffb1000,
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_310_McBSP2_TX),
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_310_McBSP2_RX),
+ &s->drq[OMAP_DMA_MCBSP2_TX], omap_findclk(s, "mpuper_ck"));
+ s->mcbsp3 = omap_mcbsp_init(system_memory, 0xfffb7000,
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP3TX),
+ qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP3RX),
+ &s->drq[OMAP_DMA_MCBSP3_TX], omap_findclk(s, "dspxor_ck"));
+
+ s->led[0] = omap_lpg_init(system_memory,
+ 0xfffbd000, omap_findclk(s, "clk32-kHz"));
+ s->led[1] = omap_lpg_init(system_memory,
+ 0xfffbd800, omap_findclk(s, "clk32-kHz"));
+
+ /* Register mappings not currenlty implemented:
+ * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310)
+ * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310)
+ * USB W2FC fffb4000 - fffb47ff
+ * Camera Interface fffb6800 - fffb6fff
+ * USB Host fffba000 - fffba7ff
+ * FAC fffba800 - fffbafff
+ * HDQ/1-Wire fffbc000 - fffbc7ff
+ * TIPB switches fffbc800 - fffbcfff
+ * Mailbox fffcf000 - fffcf7ff
+ * Local bus IF fffec100 - fffec1ff
+ * Local bus MMU fffec200 - fffec2ff
+ * DSP MMU fffed200 - fffed2ff
+ */
+
+ omap_setup_dsp_mapping(system_memory, omap15xx_dsp_mm);
+ omap_setup_mpui_io(system_memory, s);
+
+ qemu_register_reset(omap1_mpu_reset, s);
+
+ return s;
+}
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
new file mode 100644
index 0000000..0a2cd7b
--- /dev/null
+++ b/hw/arm/omap2.c
@@ -0,0 +1,2684 @@
+/*
+ * TI OMAP processors emulation.
+ *
+ * Copyright (C) 2007-2008 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 "sysemu/blockdev.h"
+#include "hw/hw.h"
+#include "hw/arm-misc.h"
+#include "hw/omap.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "char/char.h"
+#include "hw/flash.h"
+#include "hw/soc_dma.h"
+#include "hw/sysbus.h"
+#include "audio/audio.h"
+
+/* Enhanced Audio Controller (CODEC only) */
+struct omap_eac_s {
+ qemu_irq irq;
+ MemoryRegion iomem;
+
+ uint16_t sysconfig;
+ uint8_t config[4];
+ uint8_t control;
+ uint8_t address;
+ uint16_t data;
+ uint8_t vtol;
+ uint8_t vtsl;
+ uint16_t mixer;
+ uint16_t gain[4];
+ uint8_t att;
+ uint16_t max[7];
+
+ struct {
+ qemu_irq txdrq;
+ qemu_irq rxdrq;
+ uint32_t (*txrx)(void *opaque, uint32_t, int);
+ void *opaque;
+
+#define EAC_BUF_LEN 1024
+ uint32_t rxbuf[EAC_BUF_LEN];
+ int rxoff;
+ int rxlen;
+ int rxavail;
+ uint32_t txbuf[EAC_BUF_LEN];
+ int txlen;
+ int txavail;
+
+ int enable;
+ int rate;
+
+ uint16_t config[4];
+
+ /* These need to be moved to the actual codec */
+ QEMUSoundCard card;
+ SWVoiceIn *in_voice;
+ SWVoiceOut *out_voice;
+ int hw_enable;
+ } codec;
+
+ struct {
+ uint8_t control;
+ uint16_t config;
+ } modem, bt;
+};
+
+static inline void omap_eac_interrupt_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->irq, (s->codec.config[1] >> 14) & 1); /* AURDI */
+}
+
+static inline void omap_eac_in_dmarequest_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->codec.rxdrq, (s->codec.rxavail || s->codec.rxlen) &&
+ ((s->codec.config[1] >> 12) & 1)); /* DMAREN */
+}
+
+static inline void omap_eac_out_dmarequest_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->codec.txdrq, s->codec.txlen < s->codec.txavail &&
+ ((s->codec.config[1] >> 11) & 1)); /* DMAWEN */
+}
+
+static inline void omap_eac_in_refill(struct omap_eac_s *s)
+{
+ int left = MIN(EAC_BUF_LEN - s->codec.rxlen, s->codec.rxavail) << 2;
+ int start = ((s->codec.rxoff + s->codec.rxlen) & (EAC_BUF_LEN - 1)) << 2;
+ int leftwrap = MIN(left, (EAC_BUF_LEN << 2) - start);
+ int recv = 1;
+ uint8_t *buf = (uint8_t *) s->codec.rxbuf + start;
+
+ left -= leftwrap;
+ start = 0;
+ while (leftwrap && (recv = AUD_read(s->codec.in_voice, buf + start,
+ leftwrap)) > 0) { /* Be defensive */
+ start += recv;
+ leftwrap -= recv;
+ }
+ if (recv <= 0)
+ s->codec.rxavail = 0;
+ else
+ s->codec.rxavail -= start >> 2;
+ s->codec.rxlen += start >> 2;
+
+ if (recv > 0 && left > 0) {
+ start = 0;
+ while (left && (recv = AUD_read(s->codec.in_voice,
+ (uint8_t *) s->codec.rxbuf + start,
+ left)) > 0) { /* Be defensive */
+ start += recv;
+ left -= recv;
+ }
+ if (recv <= 0)
+ s->codec.rxavail = 0;
+ else
+ s->codec.rxavail -= start >> 2;
+ s->codec.rxlen += start >> 2;
+ }
+}
+
+static inline void omap_eac_out_empty(struct omap_eac_s *s)
+{
+ int left = s->codec.txlen << 2;
+ int start = 0;
+ int sent = 1;
+
+ while (left && (sent = AUD_write(s->codec.out_voice,
+ (uint8_t *) s->codec.txbuf + start,
+ left)) > 0) { /* Be defensive */
+ start += sent;
+ left -= sent;
+ }
+
+ if (!sent) {
+ s->codec.txavail = 0;
+ omap_eac_out_dmarequest_update(s);
+ }
+
+ if (start)
+ s->codec.txlen = 0;
+}
+
+static void omap_eac_in_cb(void *opaque, int avail_b)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ s->codec.rxavail = avail_b >> 2;
+ omap_eac_in_refill(s);
+ /* TODO: possibly discard current buffer if overrun */
+ omap_eac_in_dmarequest_update(s);
+}
+
+static void omap_eac_out_cb(void *opaque, int free_b)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ s->codec.txavail = free_b >> 2;
+ if (s->codec.txlen)
+ omap_eac_out_empty(s);
+ else
+ omap_eac_out_dmarequest_update(s);
+}
+
+static void omap_eac_enable_update(struct omap_eac_s *s)
+{
+ s->codec.enable = !(s->codec.config[1] & 1) && /* EACPWD */
+ (s->codec.config[1] & 2) && /* AUDEN */
+ s->codec.hw_enable;
+}
+
+static const int omap_eac_fsint[4] = {
+ 8000,
+ 11025,
+ 22050,
+ 44100,
+};
+
+static const int omap_eac_fsint2[8] = {
+ 8000,
+ 11025,
+ 22050,
+ 44100,
+ 48000,
+ 0, 0, 0,
+};
+
+static const int omap_eac_fsint3[16] = {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void omap_eac_rate_update(struct omap_eac_s *s)
+{
+ int fsint[3];
+
+ fsint[2] = (s->codec.config[3] >> 9) & 0xf;
+ fsint[1] = (s->codec.config[2] >> 0) & 0x7;
+ fsint[0] = (s->codec.config[0] >> 6) & 0x3;
+ if (fsint[2] < 0xf)
+ s->codec.rate = omap_eac_fsint3[fsint[2]];
+ else if (fsint[1] < 0x7)
+ s->codec.rate = omap_eac_fsint2[fsint[1]];
+ else
+ s->codec.rate = omap_eac_fsint[fsint[0]];
+}
+
+static void omap_eac_volume_update(struct omap_eac_s *s)
+{
+ /* TODO */
+}
+
+static void omap_eac_format_update(struct omap_eac_s *s)
+{
+ struct audsettings fmt;
+
+ /* The hardware buffers at most one sample */
+ if (s->codec.rxlen)
+ s->codec.rxlen = 1;
+
+ if (s->codec.in_voice) {
+ AUD_set_active_in(s->codec.in_voice, 0);
+ AUD_close_in(&s->codec.card, s->codec.in_voice);
+ s->codec.in_voice = NULL;
+ }
+ if (s->codec.out_voice) {
+ omap_eac_out_empty(s);
+ AUD_set_active_out(s->codec.out_voice, 0);
+ AUD_close_out(&s->codec.card, s->codec.out_voice);
+ s->codec.out_voice = NULL;
+ s->codec.txavail = 0;
+ }
+ /* Discard what couldn't be written */
+ s->codec.txlen = 0;
+
+ omap_eac_enable_update(s);
+ if (!s->codec.enable)
+ return;
+
+ omap_eac_rate_update(s);
+ fmt.endianness = ((s->codec.config[0] >> 8) & 1); /* LI_BI */
+ fmt.nchannels = ((s->codec.config[0] >> 10) & 1) ? 2 : 1; /* MN_ST */
+ fmt.freq = s->codec.rate;
+ /* TODO: signedness possibly depends on the CODEC hardware - or
+ * does I2S specify it? */
+ /* All register writes are 16 bits so we we store 16-bit samples
+ * in the buffers regardless of AGCFR[B8_16] value. */
+ fmt.fmt = AUD_FMT_U16;
+
+ s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
+ "eac.codec.in", s, omap_eac_in_cb, &fmt);
+ s->codec.out_voice = AUD_open_out(&s->codec.card, s->codec.out_voice,
+ "eac.codec.out", s, omap_eac_out_cb, &fmt);
+
+ omap_eac_volume_update(s);
+
+ AUD_set_active_in(s->codec.in_voice, 1);
+ AUD_set_active_out(s->codec.out_voice, 1);
+}
+
+static void omap_eac_reset(struct omap_eac_s *s)
+{
+ s->sysconfig = 0;
+ s->config[0] = 0x0c;
+ s->config[1] = 0x09;
+ s->config[2] = 0xab;
+ s->config[3] = 0x03;
+ s->control = 0x00;
+ s->address = 0x00;
+ s->data = 0x0000;
+ s->vtol = 0x00;
+ s->vtsl = 0x00;
+ s->mixer = 0x0000;
+ s->gain[0] = 0xe7e7;
+ s->gain[1] = 0x6767;
+ s->gain[2] = 0x6767;
+ s->gain[3] = 0x6767;
+ s->att = 0xce;
+ s->max[0] = 0;
+ s->max[1] = 0;
+ s->max[2] = 0;
+ s->max[3] = 0;
+ s->max[4] = 0;
+ s->max[5] = 0;
+ s->max[6] = 0;
+
+ s->modem.control = 0x00;
+ s->modem.config = 0x0000;
+ s->bt.control = 0x00;
+ s->bt.config = 0x0000;
+ s->codec.config[0] = 0x0649;
+ s->codec.config[1] = 0x0000;
+ s->codec.config[2] = 0x0007;
+ s->codec.config[3] = 0x1ffc;
+ s->codec.rxoff = 0;
+ s->codec.rxlen = 0;
+ s->codec.txlen = 0;
+ s->codec.rxavail = 0;
+ s->codec.txavail = 0;
+
+ omap_eac_format_update(s);
+ omap_eac_interrupt_update(s);
+}
+
+static uint64_t omap_eac_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+ uint32_t ret;
+
+ if (size != 2) {
+ return omap_badwidth_read16(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x000: /* CPCFR1 */
+ return s->config[0];
+ case 0x004: /* CPCFR2 */
+ return s->config[1];
+ case 0x008: /* CPCFR3 */
+ return s->config[2];
+ case 0x00c: /* CPCFR4 */
+ return s->config[3];
+
+ case 0x010: /* CPTCTL */
+ return s->control | ((s->codec.rxavail + s->codec.rxlen > 0) << 7) |
+ ((s->codec.txlen < s->codec.txavail) << 5);
+
+ case 0x014: /* CPTTADR */
+ return s->address;
+ case 0x018: /* CPTDATL */
+ return s->data & 0xff;
+ case 0x01c: /* CPTDATH */
+ return s->data >> 8;
+ case 0x020: /* CPTVSLL */
+ return s->vtol;
+ case 0x024: /* CPTVSLH */
+ return s->vtsl | (3 << 5); /* CRDY1 | CRDY2 */
+ case 0x040: /* MPCTR */
+ return s->modem.control;
+ case 0x044: /* MPMCCFR */
+ return s->modem.config;
+ case 0x060: /* BPCTR */
+ return s->bt.control;
+ case 0x064: /* BPMCCFR */
+ return s->bt.config;
+ case 0x080: /* AMSCFR */
+ return s->mixer;
+ case 0x084: /* AMVCTR */
+ return s->gain[0];
+ case 0x088: /* AM1VCTR */
+ return s->gain[1];
+ case 0x08c: /* AM2VCTR */
+ return s->gain[2];
+ case 0x090: /* AM3VCTR */
+ return s->gain[3];
+ case 0x094: /* ASTCTR */
+ return s->att;
+ case 0x098: /* APD1LCR */
+ return s->max[0];
+ case 0x09c: /* APD1RCR */
+ return s->max[1];
+ case 0x0a0: /* APD2LCR */
+ return s->max[2];
+ case 0x0a4: /* APD2RCR */
+ return s->max[3];
+ case 0x0a8: /* APD3LCR */
+ return s->max[4];
+ case 0x0ac: /* APD3RCR */
+ return s->max[5];
+ case 0x0b0: /* APD4R */
+ return s->max[6];
+ case 0x0b4: /* ADWR */
+ /* This should be write-only? Docs list it as read-only. */
+ return 0x0000;
+ case 0x0b8: /* ADRDR */
+ if (likely(s->codec.rxlen > 1)) {
+ ret = s->codec.rxbuf[s->codec.rxoff ++];
+ s->codec.rxlen --;
+ s->codec.rxoff &= EAC_BUF_LEN - 1;
+ return ret;
+ } else if (s->codec.rxlen) {
+ ret = s->codec.rxbuf[s->codec.rxoff ++];
+ s->codec.rxlen --;
+ s->codec.rxoff &= EAC_BUF_LEN - 1;
+ if (s->codec.rxavail)
+ omap_eac_in_refill(s);
+ omap_eac_in_dmarequest_update(s);
+ return ret;
+ }
+ return 0x0000;
+ case 0x0bc: /* AGCFR */
+ return s->codec.config[0];
+ case 0x0c0: /* AGCTR */
+ return s->codec.config[1] | ((s->codec.config[1] & 2) << 14);
+ case 0x0c4: /* AGCFR2 */
+ return s->codec.config[2];
+ case 0x0c8: /* AGCFR3 */
+ return s->codec.config[3];
+ case 0x0cc: /* MBPDMACTR */
+ case 0x0d0: /* MPDDMARR */
+ case 0x0d8: /* MPUDMARR */
+ case 0x0e4: /* BPDDMARR */
+ case 0x0ec: /* BPUDMARR */
+ return 0x0000;
+
+ case 0x100: /* VERSION_NUMBER */
+ return 0x0010;
+
+ case 0x104: /* SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x108: /* SYSSTATUS */
+ return 1 | 0xe; /* RESETDONE | stuff */
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_eac_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ if (size != 2) {
+ return omap_badwidth_write16(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x098: /* APD1LCR */
+ case 0x09c: /* APD1RCR */
+ case 0x0a0: /* APD2LCR */
+ case 0x0a4: /* APD2RCR */
+ case 0x0a8: /* APD3LCR */
+ case 0x0ac: /* APD3RCR */
+ case 0x0b0: /* APD4R */
+ case 0x0b8: /* ADRDR */
+ case 0x0d0: /* MPDDMARR */
+ case 0x0d8: /* MPUDMARR */
+ case 0x0e4: /* BPDDMARR */
+ case 0x0ec: /* BPUDMARR */
+ case 0x100: /* VERSION_NUMBER */
+ case 0x108: /* SYSSTATUS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x000: /* CPCFR1 */
+ s->config[0] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x004: /* CPCFR2 */
+ s->config[1] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x008: /* CPCFR3 */
+ s->config[2] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x00c: /* CPCFR4 */
+ s->config[3] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+
+ case 0x010: /* CPTCTL */
+ /* Assuming TXF and TXE bits are read-only... */
+ s->control = value & 0x5f;
+ omap_eac_interrupt_update(s);
+ break;
+
+ case 0x014: /* CPTTADR */
+ s->address = value & 0xff;
+ break;
+ case 0x018: /* CPTDATL */
+ s->data &= 0xff00;
+ s->data |= value & 0xff;
+ break;
+ case 0x01c: /* CPTDATH */
+ s->data &= 0x00ff;
+ s->data |= value << 8;
+ break;
+ case 0x020: /* CPTVSLL */
+ s->vtol = value & 0xf8;
+ break;
+ case 0x024: /* CPTVSLH */
+ s->vtsl = value & 0x9f;
+ break;
+ case 0x040: /* MPCTR */
+ s->modem.control = value & 0x8f;
+ break;
+ case 0x044: /* MPMCCFR */
+ s->modem.config = value & 0x7fff;
+ break;
+ case 0x060: /* BPCTR */
+ s->bt.control = value & 0x8f;
+ break;
+ case 0x064: /* BPMCCFR */
+ s->bt.config = value & 0x7fff;
+ break;
+ case 0x080: /* AMSCFR */
+ s->mixer = value & 0x0fff;
+ break;
+ case 0x084: /* AMVCTR */
+ s->gain[0] = value & 0xffff;
+ break;
+ case 0x088: /* AM1VCTR */
+ s->gain[1] = value & 0xff7f;
+ break;
+ case 0x08c: /* AM2VCTR */
+ s->gain[2] = value & 0xff7f;
+ break;
+ case 0x090: /* AM3VCTR */
+ s->gain[3] = value & 0xff7f;
+ break;
+ case 0x094: /* ASTCTR */
+ s->att = value & 0xff;
+ break;
+
+ case 0x0b4: /* ADWR */
+ s->codec.txbuf[s->codec.txlen ++] = value;
+ if (unlikely(s->codec.txlen == EAC_BUF_LEN ||
+ s->codec.txlen == s->codec.txavail)) {
+ if (s->codec.txavail)
+ omap_eac_out_empty(s);
+ /* Discard what couldn't be written */
+ s->codec.txlen = 0;
+ }
+ break;
+
+ case 0x0bc: /* AGCFR */
+ s->codec.config[0] = value & 0x07ff;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c0: /* AGCTR */
+ s->codec.config[1] = value & 0x780f;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c4: /* AGCFR2 */
+ s->codec.config[2] = value & 0x003f;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c8: /* AGCFR3 */
+ s->codec.config[3] = value & 0xffff;
+ omap_eac_format_update(s);
+ break;
+ case 0x0cc: /* MBPDMACTR */
+ case 0x0d4: /* MPDDMAWR */
+ case 0x0e0: /* MPUDMAWR */
+ case 0x0e8: /* BPDDMAWR */
+ case 0x0f0: /* BPUDMAWR */
+ break;
+
+ case 0x104: /* SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_eac_reset(s);
+ s->sysconfig = value & 0x31d;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_eac_ops = {
+ .read = omap_eac_read,
+ .write = omap_eac_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *)
+ g_malloc0(sizeof(struct omap_eac_s));
+
+ s->irq = irq;
+ s->codec.rxdrq = *drq ++;
+ s->codec.txdrq = *drq;
+ omap_eac_reset(s);
+
+ AUD_register_card("OMAP EAC", &s->codec.card);
+
+ memory_region_init_io(&s->iomem, &omap_eac_ops, s, "omap.eac",
+ omap_l4_region_size(ta, 0));
+ omap_l4_attach(ta, 0, &s->iomem);
+
+ return s;
+}
+
+/* STI/XTI (emulation interface) console - reverse engineered only */
+struct omap_sti_s {
+ qemu_irq irq;
+ MemoryRegion iomem;
+ MemoryRegion iomem_fifo;
+ CharDriverState *chr;
+
+ uint32_t sysconfig;
+ uint32_t systest;
+ uint32_t irqst;
+ uint32_t irqen;
+ uint32_t clkcontrol;
+ uint32_t serial_config;
+};
+
+#define STI_TRACE_CONSOLE_CHANNEL 239
+#define STI_TRACE_CONTROL_CHANNEL 253
+
+static inline void omap_sti_interrupt_update(struct omap_sti_s *s)
+{
+ qemu_set_irq(s->irq, s->irqst & s->irqen);
+}
+
+static void omap_sti_reset(struct omap_sti_s *s)
+{
+ s->sysconfig = 0;
+ s->irqst = 0;
+ s->irqen = 0;
+ s->clkcontrol = 0;
+ s->serial_config = 0;
+
+ omap_sti_interrupt_update(s);
+}
+
+static uint64_t omap_sti_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x00: /* STI_REVISION */
+ return 0x10;
+
+ case 0x10: /* STI_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */
+ return 0x00;
+
+ case 0x18: /* STI_IRQSTATUS */
+ return s->irqst;
+
+ case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */
+ return s->irqen;
+
+ case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */
+ case 0x28: /* STI_RX_DR / XTI_RXDATA */
+ /* TODO */
+ return 0;
+
+ case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */
+ return s->clkcontrol;
+
+ case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */
+ return s->serial_config;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sti_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x00: /* STI_REVISION */
+ case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x10: /* STI_SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_sti_reset(s);
+ s->sysconfig = value & 0xfe;
+ break;
+
+ case 0x18: /* STI_IRQSTATUS */
+ s->irqst &= ~value;
+ omap_sti_interrupt_update(s);
+ break;
+
+ case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */
+ s->irqen = value & 0xffff;
+ omap_sti_interrupt_update(s);
+ break;
+
+ case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */
+ s->clkcontrol = value & 0xff;
+ break;
+
+ case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */
+ s->serial_config = value & 0xff;
+ break;
+
+ case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */
+ case 0x28: /* STI_RX_DR / XTI_RXDATA */
+ /* TODO */
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_sti_ops = {
+ .read = omap_sti_read,
+ .write = omap_sti_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sti_fifo_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+ int ch = addr >> 6;
+ uint8_t byte = value;
+
+ if (size != 1) {
+ return omap_badwidth_write8(opaque, addr, size);
+ }
+
+ if (ch == STI_TRACE_CONTROL_CHANNEL) {
+ /* Flush channel <i>value</i>. */
+ qemu_chr_fe_write(s->chr, (const uint8_t *) "\r", 1);
+ } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) {
+ if (value == 0xc0 || value == 0xc3) {
+ /* Open channel <i>ch</i>. */
+ } else if (value == 0x00)
+ qemu_chr_fe_write(s->chr, (const uint8_t *) "\n", 1);
+ else
+ qemu_chr_fe_write(s->chr, &byte, 1);
+ }
+}
+
+static const MemoryRegionOps omap_sti_fifo_ops = {
+ .read = omap_sti_fifo_read,
+ .write = omap_sti_fifo_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
+ MemoryRegion *sysmem,
+ hwaddr channel_base, qemu_irq irq, omap_clk clk,
+ CharDriverState *chr)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *)
+ g_malloc0(sizeof(struct omap_sti_s));
+
+ s->irq = irq;
+ omap_sti_reset(s);
+
+ s->chr = chr ?: qemu_chr_new("null", "null", NULL);
+
+ memory_region_init_io(&s->iomem, &omap_sti_ops, s, "omap.sti",
+ omap_l4_region_size(ta, 0));
+ omap_l4_attach(ta, 0, &s->iomem);
+
+ memory_region_init_io(&s->iomem_fifo, &omap_sti_fifo_ops, s,
+ "omap.sti.fifo", 0x10000);
+ memory_region_add_subregion(sysmem, channel_base, &s->iomem_fifo);
+
+ return s;
+}
+
+/* L4 Interconnect */
+#define L4TA(n) (n)
+#define L4TAO(n) ((n) + 39)
+
+static const struct omap_l4_region_s omap_l4_region[125] = {
+ [ 1] = { 0x40800, 0x800, 32 }, /* Initiator agent */
+ [ 2] = { 0x41000, 0x1000, 32 }, /* Link agent */
+ [ 0] = { 0x40000, 0x800, 32 }, /* Address and protection */
+ [ 3] = { 0x00000, 0x1000, 32 | 16 | 8 }, /* System Control and Pinout */
+ [ 4] = { 0x01000, 0x1000, 32 | 16 | 8 }, /* L4TAO1 */
+ [ 5] = { 0x04000, 0x1000, 32 | 16 }, /* 32K Timer */
+ [ 6] = { 0x05000, 0x1000, 32 | 16 | 8 }, /* L4TAO2 */
+ [ 7] = { 0x08000, 0x800, 32 }, /* PRCM Region A */
+ [ 8] = { 0x08800, 0x800, 32 }, /* PRCM Region B */
+ [ 9] = { 0x09000, 0x1000, 32 | 16 | 8 }, /* L4TAO */
+ [ 10] = { 0x12000, 0x1000, 32 | 16 | 8 }, /* Test (BCM) */
+ [ 11] = { 0x13000, 0x1000, 32 | 16 | 8 }, /* L4TA1 */
+ [ 12] = { 0x14000, 0x1000, 32 }, /* Test/emulation (TAP) */
+ [ 13] = { 0x15000, 0x1000, 32 | 16 | 8 }, /* L4TA2 */
+ [ 14] = { 0x18000, 0x1000, 32 | 16 | 8 }, /* GPIO1 */
+ [ 16] = { 0x1a000, 0x1000, 32 | 16 | 8 }, /* GPIO2 */
+ [ 18] = { 0x1c000, 0x1000, 32 | 16 | 8 }, /* GPIO3 */
+ [ 19] = { 0x1e000, 0x1000, 32 | 16 | 8 }, /* GPIO4 */
+ [ 15] = { 0x19000, 0x1000, 32 | 16 | 8 }, /* Quad GPIO TOP */
+ [ 17] = { 0x1b000, 0x1000, 32 | 16 | 8 }, /* L4TA3 */
+ [ 20] = { 0x20000, 0x1000, 32 | 16 | 8 }, /* WD Timer 1 (Secure) */
+ [ 22] = { 0x22000, 0x1000, 32 | 16 | 8 }, /* WD Timer 2 (OMAP) */
+ [ 21] = { 0x21000, 0x1000, 32 | 16 | 8 }, /* Dual WD timer TOP */
+ [ 23] = { 0x23000, 0x1000, 32 | 16 | 8 }, /* L4TA4 */
+ [ 24] = { 0x28000, 0x1000, 32 | 16 | 8 }, /* GP Timer 1 */
+ [ 25] = { 0x29000, 0x1000, 32 | 16 | 8 }, /* L4TA7 */
+ [ 26] = { 0x48000, 0x2000, 32 | 16 | 8 }, /* Emulation (ARM11ETB) */
+ [ 27] = { 0x4a000, 0x1000, 32 | 16 | 8 }, /* L4TA9 */
+ [ 28] = { 0x50000, 0x400, 32 | 16 | 8 }, /* Display top */
+ [ 29] = { 0x50400, 0x400, 32 | 16 | 8 }, /* Display control */
+ [ 30] = { 0x50800, 0x400, 32 | 16 | 8 }, /* Display RFBI */
+ [ 31] = { 0x50c00, 0x400, 32 | 16 | 8 }, /* Display encoder */
+ [ 32] = { 0x51000, 0x1000, 32 | 16 | 8 }, /* L4TA10 */
+ [ 33] = { 0x52000, 0x400, 32 | 16 | 8 }, /* Camera top */
+ [ 34] = { 0x52400, 0x400, 32 | 16 | 8 }, /* Camera core */
+ [ 35] = { 0x52800, 0x400, 32 | 16 | 8 }, /* Camera DMA */
+ [ 36] = { 0x52c00, 0x400, 32 | 16 | 8 }, /* Camera MMU */
+ [ 37] = { 0x53000, 0x1000, 32 | 16 | 8 }, /* L4TA11 */
+ [ 38] = { 0x56000, 0x1000, 32 | 16 | 8 }, /* sDMA */
+ [ 39] = { 0x57000, 0x1000, 32 | 16 | 8 }, /* L4TA12 */
+ [ 40] = { 0x58000, 0x1000, 32 | 16 | 8 }, /* SSI top */
+ [ 41] = { 0x59000, 0x1000, 32 | 16 | 8 }, /* SSI GDD */
+ [ 42] = { 0x5a000, 0x1000, 32 | 16 | 8 }, /* SSI Port1 */
+ [ 43] = { 0x5b000, 0x1000, 32 | 16 | 8 }, /* SSI Port2 */
+ [ 44] = { 0x5c000, 0x1000, 32 | 16 | 8 }, /* L4TA13 */
+ [ 45] = { 0x5e000, 0x1000, 32 | 16 | 8 }, /* USB OTG */
+ [ 46] = { 0x5f000, 0x1000, 32 | 16 | 8 }, /* L4TAO4 */
+ [ 47] = { 0x60000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER1SDRC) */
+ [ 48] = { 0x61000, 0x1000, 32 | 16 | 8 }, /* L4TA14 */
+ [ 49] = { 0x62000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER2GPMC) */
+ [ 50] = { 0x63000, 0x1000, 32 | 16 | 8 }, /* L4TA15 */
+ [ 51] = { 0x64000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER3OCM) */
+ [ 52] = { 0x65000, 0x1000, 32 | 16 | 8 }, /* L4TA16 */
+ [ 53] = { 0x66000, 0x300, 32 | 16 | 8 }, /* Emulation (WIN_TRACER4L4) */
+ [ 54] = { 0x67000, 0x1000, 32 | 16 | 8 }, /* L4TA17 */
+ [ 55] = { 0x68000, 0x1000, 32 | 16 | 8 }, /* Emulation (XTI) */
+ [ 56] = { 0x69000, 0x1000, 32 | 16 | 8 }, /* L4TA18 */
+ [ 57] = { 0x6a000, 0x1000, 16 | 8 }, /* UART1 */
+ [ 58] = { 0x6b000, 0x1000, 32 | 16 | 8 }, /* L4TA19 */
+ [ 59] = { 0x6c000, 0x1000, 16 | 8 }, /* UART2 */
+ [ 60] = { 0x6d000, 0x1000, 32 | 16 | 8 }, /* L4TA20 */
+ [ 61] = { 0x6e000, 0x1000, 16 | 8 }, /* UART3 */
+ [ 62] = { 0x6f000, 0x1000, 32 | 16 | 8 }, /* L4TA21 */
+ [ 63] = { 0x70000, 0x1000, 16 }, /* I2C1 */
+ [ 64] = { 0x71000, 0x1000, 32 | 16 | 8 }, /* L4TAO5 */
+ [ 65] = { 0x72000, 0x1000, 16 }, /* I2C2 */
+ [ 66] = { 0x73000, 0x1000, 32 | 16 | 8 }, /* L4TAO6 */
+ [ 67] = { 0x74000, 0x1000, 16 }, /* McBSP1 */
+ [ 68] = { 0x75000, 0x1000, 32 | 16 | 8 }, /* L4TAO7 */
+ [ 69] = { 0x76000, 0x1000, 16 }, /* McBSP2 */
+ [ 70] = { 0x77000, 0x1000, 32 | 16 | 8 }, /* L4TAO8 */
+ [ 71] = { 0x24000, 0x1000, 32 | 16 | 8 }, /* WD Timer 3 (DSP) */
+ [ 72] = { 0x25000, 0x1000, 32 | 16 | 8 }, /* L4TA5 */
+ [ 73] = { 0x26000, 0x1000, 32 | 16 | 8 }, /* WD Timer 4 (IVA) */
+ [ 74] = { 0x27000, 0x1000, 32 | 16 | 8 }, /* L4TA6 */
+ [ 75] = { 0x2a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 2 */
+ [ 76] = { 0x2b000, 0x1000, 32 | 16 | 8 }, /* L4TA8 */
+ [ 77] = { 0x78000, 0x1000, 32 | 16 | 8 }, /* GP Timer 3 */
+ [ 78] = { 0x79000, 0x1000, 32 | 16 | 8 }, /* L4TA22 */
+ [ 79] = { 0x7a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 4 */
+ [ 80] = { 0x7b000, 0x1000, 32 | 16 | 8 }, /* L4TA23 */
+ [ 81] = { 0x7c000, 0x1000, 32 | 16 | 8 }, /* GP Timer 5 */
+ [ 82] = { 0x7d000, 0x1000, 32 | 16 | 8 }, /* L4TA24 */
+ [ 83] = { 0x7e000, 0x1000, 32 | 16 | 8 }, /* GP Timer 6 */
+ [ 84] = { 0x7f000, 0x1000, 32 | 16 | 8 }, /* L4TA25 */
+ [ 85] = { 0x80000, 0x1000, 32 | 16 | 8 }, /* GP Timer 7 */
+ [ 86] = { 0x81000, 0x1000, 32 | 16 | 8 }, /* L4TA26 */
+ [ 87] = { 0x82000, 0x1000, 32 | 16 | 8 }, /* GP Timer 8 */
+ [ 88] = { 0x83000, 0x1000, 32 | 16 | 8 }, /* L4TA27 */
+ [ 89] = { 0x84000, 0x1000, 32 | 16 | 8 }, /* GP Timer 9 */
+ [ 90] = { 0x85000, 0x1000, 32 | 16 | 8 }, /* L4TA28 */
+ [ 91] = { 0x86000, 0x1000, 32 | 16 | 8 }, /* GP Timer 10 */
+ [ 92] = { 0x87000, 0x1000, 32 | 16 | 8 }, /* L4TA29 */
+ [ 93] = { 0x88000, 0x1000, 32 | 16 | 8 }, /* GP Timer 11 */
+ [ 94] = { 0x89000, 0x1000, 32 | 16 | 8 }, /* L4TA30 */
+ [ 95] = { 0x8a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 12 */
+ [ 96] = { 0x8b000, 0x1000, 32 | 16 | 8 }, /* L4TA31 */
+ [ 97] = { 0x90000, 0x1000, 16 }, /* EAC */
+ [ 98] = { 0x91000, 0x1000, 32 | 16 | 8 }, /* L4TA32 */
+ [ 99] = { 0x92000, 0x1000, 16 }, /* FAC */
+ [100] = { 0x93000, 0x1000, 32 | 16 | 8 }, /* L4TA33 */
+ [101] = { 0x94000, 0x1000, 32 | 16 | 8 }, /* IPC (MAILBOX) */
+ [102] = { 0x95000, 0x1000, 32 | 16 | 8 }, /* L4TA34 */
+ [103] = { 0x98000, 0x1000, 32 | 16 | 8 }, /* SPI1 */
+ [104] = { 0x99000, 0x1000, 32 | 16 | 8 }, /* L4TA35 */
+ [105] = { 0x9a000, 0x1000, 32 | 16 | 8 }, /* SPI2 */
+ [106] = { 0x9b000, 0x1000, 32 | 16 | 8 }, /* L4TA36 */
+ [107] = { 0x9c000, 0x1000, 16 | 8 }, /* MMC SDIO */
+ [108] = { 0x9d000, 0x1000, 32 | 16 | 8 }, /* L4TAO9 */
+ [109] = { 0x9e000, 0x1000, 32 | 16 | 8 }, /* MS_PRO */
+ [110] = { 0x9f000, 0x1000, 32 | 16 | 8 }, /* L4TAO10 */
+ [111] = { 0xa0000, 0x1000, 32 }, /* RNG */
+ [112] = { 0xa1000, 0x1000, 32 | 16 | 8 }, /* L4TAO11 */
+ [113] = { 0xa2000, 0x1000, 32 }, /* DES3DES */
+ [114] = { 0xa3000, 0x1000, 32 | 16 | 8 }, /* L4TAO12 */
+ [115] = { 0xa4000, 0x1000, 32 }, /* SHA1MD5 */
+ [116] = { 0xa5000, 0x1000, 32 | 16 | 8 }, /* L4TAO13 */
+ [117] = { 0xa6000, 0x1000, 32 }, /* AES */
+ [118] = { 0xa7000, 0x1000, 32 | 16 | 8 }, /* L4TA37 */
+ [119] = { 0xa8000, 0x2000, 32 }, /* PKA */
+ [120] = { 0xaa000, 0x1000, 32 | 16 | 8 }, /* L4TA38 */
+ [121] = { 0xb0000, 0x1000, 32 }, /* MG */
+ [122] = { 0xb1000, 0x1000, 32 | 16 | 8 },
+ [123] = { 0xb2000, 0x1000, 32 }, /* HDQ/1-Wire */
+ [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */
+};
+
+static const struct omap_l4_agent_info_s omap_l4_agent_info[54] = {
+ { 0, 0, 3, 2 }, /* L4IA initiatior agent */
+ { L4TAO(1), 3, 2, 1 }, /* Control and pinout module */
+ { L4TAO(2), 5, 2, 1 }, /* 32K timer */
+ { L4TAO(3), 7, 3, 2 }, /* PRCM */
+ { L4TA(1), 10, 2, 1 }, /* BCM */
+ { L4TA(2), 12, 2, 1 }, /* Test JTAG */
+ { L4TA(3), 14, 6, 3 }, /* Quad GPIO */
+ { L4TA(4), 20, 4, 3 }, /* WD timer 1/2 */
+ { L4TA(7), 24, 2, 1 }, /* GP timer 1 */
+ { L4TA(9), 26, 2, 1 }, /* ATM11 ETB */
+ { L4TA(10), 28, 5, 4 }, /* Display subsystem */
+ { L4TA(11), 33, 5, 4 }, /* Camera subsystem */
+ { L4TA(12), 38, 2, 1 }, /* sDMA */
+ { L4TA(13), 40, 5, 4 }, /* SSI */
+ { L4TAO(4), 45, 2, 1 }, /* USB */
+ { L4TA(14), 47, 2, 1 }, /* Win Tracer1 */
+ { L4TA(15), 49, 2, 1 }, /* Win Tracer2 */
+ { L4TA(16), 51, 2, 1 }, /* Win Tracer3 */
+ { L4TA(17), 53, 2, 1 }, /* Win Tracer4 */
+ { L4TA(18), 55, 2, 1 }, /* XTI */
+ { L4TA(19), 57, 2, 1 }, /* UART1 */
+ { L4TA(20), 59, 2, 1 }, /* UART2 */
+ { L4TA(21), 61, 2, 1 }, /* UART3 */
+ { L4TAO(5), 63, 2, 1 }, /* I2C1 */
+ { L4TAO(6), 65, 2, 1 }, /* I2C2 */
+ { L4TAO(7), 67, 2, 1 }, /* McBSP1 */
+ { L4TAO(8), 69, 2, 1 }, /* McBSP2 */
+ { L4TA(5), 71, 2, 1 }, /* WD Timer 3 (DSP) */
+ { L4TA(6), 73, 2, 1 }, /* WD Timer 4 (IVA) */
+ { L4TA(8), 75, 2, 1 }, /* GP Timer 2 */
+ { L4TA(22), 77, 2, 1 }, /* GP Timer 3 */
+ { L4TA(23), 79, 2, 1 }, /* GP Timer 4 */
+ { L4TA(24), 81, 2, 1 }, /* GP Timer 5 */
+ { L4TA(25), 83, 2, 1 }, /* GP Timer 6 */
+ { L4TA(26), 85, 2, 1 }, /* GP Timer 7 */
+ { L4TA(27), 87, 2, 1 }, /* GP Timer 8 */
+ { L4TA(28), 89, 2, 1 }, /* GP Timer 9 */
+ { L4TA(29), 91, 2, 1 }, /* GP Timer 10 */
+ { L4TA(30), 93, 2, 1 }, /* GP Timer 11 */
+ { L4TA(31), 95, 2, 1 }, /* GP Timer 12 */
+ { L4TA(32), 97, 2, 1 }, /* EAC */
+ { L4TA(33), 99, 2, 1 }, /* FAC */
+ { L4TA(34), 101, 2, 1 }, /* IPC */
+ { L4TA(35), 103, 2, 1 }, /* SPI1 */
+ { L4TA(36), 105, 2, 1 }, /* SPI2 */
+ { L4TAO(9), 107, 2, 1 }, /* MMC SDIO */
+ { L4TAO(10), 109, 2, 1 },
+ { L4TAO(11), 111, 2, 1 }, /* RNG */
+ { L4TAO(12), 113, 2, 1 }, /* DES3DES */
+ { L4TAO(13), 115, 2, 1 }, /* SHA1MD5 */
+ { L4TA(37), 117, 2, 1 }, /* AES */
+ { L4TA(38), 119, 2, 1 }, /* PKA */
+ { -1, 121, 2, 1 },
+ { L4TA(39), 123, 2, 1 }, /* HDQ/1-Wire */
+};
+
+#define omap_l4ta(bus, cs) \
+ omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TA(cs))
+#define omap_l4tao(bus, cs) \
+ omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TAO(cs))
+
+/* Power, Reset, and Clock Management */
+struct omap_prcm_s {
+ qemu_irq irq[3];
+ struct omap_mpu_state_s *mpu;
+ MemoryRegion iomem0;
+ MemoryRegion iomem1;
+
+ uint32_t irqst[3];
+ uint32_t irqen[3];
+
+ uint32_t sysconfig;
+ uint32_t voltctrl;
+ uint32_t scratch[20];
+
+ uint32_t clksrc[1];
+ uint32_t clkout[1];
+ uint32_t clkemul[1];
+ uint32_t clkpol[1];
+ uint32_t clksel[8];
+ uint32_t clken[12];
+ uint32_t clkctrl[4];
+ uint32_t clkidle[7];
+ uint32_t setuptime[2];
+
+ uint32_t wkup[3];
+ uint32_t wken[3];
+ uint32_t wkst[3];
+ uint32_t rst[4];
+ uint32_t rstctrl[1];
+ uint32_t power[4];
+ uint32_t rsttime_wkup;
+
+ uint32_t ev;
+ uint32_t evtime[2];
+
+ int dpll_lock, apll_lock[2];
+};
+
+static void omap_prcm_int_update(struct omap_prcm_s *s, int dom)
+{
+ qemu_set_irq(s->irq[dom], s->irqst[dom] & s->irqen[dom]);
+ /* XXX or is the mask applied before PRCM_IRQSTATUS_* ? */
+}
+
+static uint64_t omap_prcm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
+ uint32_t ret;
+
+ if (size != 4) {
+ return omap_badwidth_read32(opaque, addr);
+ }
+
+ switch (addr) {
+ case 0x000: /* PRCM_REVISION */
+ return 0x10;
+
+ case 0x010: /* PRCM_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x018: /* PRCM_IRQSTATUS_MPU */
+ return s->irqst[0];
+
+ case 0x01c: /* PRCM_IRQENABLE_MPU */
+ return s->irqen[0];
+
+ case 0x050: /* PRCM_VOLTCTRL */
+ return s->voltctrl;
+ case 0x054: /* PRCM_VOLTST */
+ return s->voltctrl & 3;
+
+ case 0x060: /* PRCM_CLKSRC_CTRL */
+ return s->clksrc[0];
+ case 0x070: /* PRCM_CLKOUT_CTRL */
+ return s->clkout[0];
+ case 0x078: /* PRCM_CLKEMUL_CTRL */
+ return s->clkemul[0];
+ case 0x080: /* PRCM_CLKCFG_CTRL */
+ case 0x084: /* PRCM_CLKCFG_STATUS */
+ return 0;
+
+ case 0x090: /* PRCM_VOLTSETUP */
+ return s->setuptime[0];
+
+ case 0x094: /* PRCM_CLKSSETUP */
+ return s->setuptime[1];
+
+ case 0x098: /* PRCM_POLCTRL */
+ return s->clkpol[0];
+
+ case 0x0b0: /* GENERAL_PURPOSE1 */
+ case 0x0b4: /* GENERAL_PURPOSE2 */
+ case 0x0b8: /* GENERAL_PURPOSE3 */
+ case 0x0bc: /* GENERAL_PURPOSE4 */
+ case 0x0c0: /* GENERAL_PURPOSE5 */
+ case 0x0c4: /* GENERAL_PURPOSE6 */
+ case 0x0c8: /* GENERAL_PURPOSE7 */
+ case 0x0cc: /* GENERAL_PURPOSE8 */
+ case 0x0d0: /* GENERAL_PURPOSE9 */
+ case 0x0d4: /* GENERAL_PURPOSE10 */
+ case 0x0d8: /* GENERAL_PURPOSE11 */
+ case 0x0dc: /* GENERAL_PURPOSE12 */
+ case 0x0e0: /* GENERAL_PURPOSE13 */
+ case 0x0e4: /* GENERAL_PURPOSE14 */
+ case 0x0e8: /* GENERAL_PURPOSE15 */
+ case 0x0ec: /* GENERAL_PURPOSE16 */
+ case 0x0f0: /* GENERAL_PURPOSE17 */
+ case 0x0f4: /* GENERAL_PURPOSE18 */
+ case 0x0f8: /* GENERAL_PURPOSE19 */
+ case 0x0fc: /* GENERAL_PURPOSE20 */
+ return s->scratch[(addr - 0xb0) >> 2];
+
+ case 0x140: /* CM_CLKSEL_MPU */
+ return s->clksel[0];
+ case 0x148: /* CM_CLKSTCTRL_MPU */
+ return s->clkctrl[0];
+
+ case 0x158: /* RM_RSTST_MPU */
+ return s->rst[0];
+ case 0x1c8: /* PM_WKDEP_MPU */
+ return s->wkup[0];
+ case 0x1d4: /* PM_EVGENCTRL_MPU */
+ return s->ev;
+ case 0x1d8: /* PM_EVEGENONTIM_MPU */
+ return s->evtime[0];
+ case 0x1dc: /* PM_EVEGENOFFTIM_MPU */
+ return s->evtime[1];
+ case 0x1e0: /* PM_PWSTCTRL_MPU */
+ return s->power[0];
+ case 0x1e4: /* PM_PWSTST_MPU */
+ return 0;
+
+ case 0x200: /* CM_FCLKEN1_CORE */
+ return s->clken[0];
+ case 0x204: /* CM_FCLKEN2_CORE */
+ return s->clken[1];
+ case 0x210: /* CM_ICLKEN1_CORE */
+ return s->clken[2];
+ case 0x214: /* CM_ICLKEN2_CORE */
+ return s->clken[3];
+ case 0x21c: /* CM_ICLKEN4_CORE */
+ return s->clken[4];
+
+ case 0x220: /* CM_IDLEST1_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x7ffffff9;
+ case 0x224: /* CM_IDLEST2_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x00000007;
+ case 0x22c: /* CM_IDLEST4_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x0000001f;
+
+ case 0x230: /* CM_AUTOIDLE1_CORE */
+ return s->clkidle[0];
+ case 0x234: /* CM_AUTOIDLE2_CORE */
+ return s->clkidle[1];
+ case 0x238: /* CM_AUTOIDLE3_CORE */
+ return s->clkidle[2];
+ case 0x23c: /* CM_AUTOIDLE4_CORE */
+ return s->clkidle[3];
+
+ case 0x240: /* CM_CLKSEL1_CORE */
+ return s->clksel[1];
+ case 0x244: /* CM_CLKSEL2_CORE */
+ return s->clksel[2];
+
+ case 0x248: /* CM_CLKSTCTRL_CORE */
+ return s->clkctrl[1];
+
+ case 0x2a0: /* PM_WKEN1_CORE */
+ return s->wken[0];
+ case 0x2a4: /* PM_WKEN2_CORE */
+ return s->wken[1];
+
+ case 0x2b0: /* PM_WKST1_CORE */
+ return s->wkst[0];
+ case 0x2b4: /* PM_WKST2_CORE */
+ return s->wkst[1];
+ case 0x2c8: /* PM_WKDEP_CORE */
+ return 0x1e;
+
+ case 0x2e0: /* PM_PWSTCTRL_CORE */
+ return s->power[1];
+ case 0x2e4: /* PM_PWSTST_CORE */
+ return 0x000030 | (s->power[1] & 0xfc00);
+
+ case 0x300: /* CM_FCLKEN_GFX */
+ return s->clken[5];
+ case 0x310: /* CM_ICLKEN_GFX */
+ return s->clken[6];
+ case 0x320: /* CM_IDLEST_GFX */
+ /* TODO: check the actual iclk status */
+ return 0x00000001;
+ case 0x340: /* CM_CLKSEL_GFX */
+ return s->clksel[3];
+ case 0x348: /* CM_CLKSTCTRL_GFX */
+ return s->clkctrl[2];
+ case 0x350: /* RM_RSTCTRL_GFX */
+ return s->rstctrl[0];
+ case 0x358: /* RM_RSTST_GFX */
+ return s->rst[1];
+ case 0x3c8: /* PM_WKDEP_GFX */
+ return s->wkup[1];
+
+ case 0x3e0: /* PM_PWSTCTRL_GFX */
+ return s->power[2];
+ case 0x3e4: /* PM_PWSTST_GFX */
+ return s->power[2] & 3;
+
+ case 0x400: /* CM_FCLKEN_WKUP */
+ return s->clken[7];
+ case 0x410: /* CM_ICLKEN_WKUP */
+ return s->clken[8];
+ case 0x420: /* CM_IDLEST_WKUP */
+ /* TODO: check the actual iclk status */
+ return 0x0000003f;
+ case 0x430: /* CM_AUTOIDLE_WKUP */
+ return s->clkidle[4];
+ case 0x440: /* CM_CLKSEL_WKUP */
+ return s->clksel[4];
+ case 0x450: /* RM_RSTCTRL_WKUP */
+ return 0;
+ case 0x454: /* RM_RSTTIME_WKUP */
+ return s->rsttime_wkup;
+ case 0x458: /* RM_RSTST_WKUP */
+ return s->rst[2];
+ case 0x4a0: /* PM_WKEN_WKUP */
+ return s->wken[2];
+ case 0x4b0: /* PM_WKST_WKUP */
+ return s->wkst[2];
+
+ case 0x500: /* CM_CLKEN_PLL */
+ return s->clken[9];
+ case 0x520: /* CM_IDLEST_CKGEN */
+ ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8);
+ if (!(s->clksel[6] & 3))
+ /* Core uses 32-kHz clock */
+ ret |= 3 << 0;
+ else if (!s->dpll_lock)
+ /* DPLL not locked, core uses ref_clk */
+ ret |= 1 << 0;
+ else
+ /* Core uses DPLL */
+ ret |= 2 << 0;
+ return ret;
+ case 0x530: /* CM_AUTOIDLE_PLL */
+ return s->clkidle[5];
+ case 0x540: /* CM_CLKSEL1_PLL */
+ return s->clksel[5];
+ case 0x544: /* CM_CLKSEL2_PLL */
+ return s->clksel[6];
+
+ case 0x800: /* CM_FCLKEN_DSP */
+ return s->clken[10];
+ case 0x810: /* CM_ICLKEN_DSP */
+ return s->clken[11];
+ case 0x820: /* CM_IDLEST_DSP */
+ /* TODO: check the actual iclk status */
+ return 0x00000103;
+ case 0x830: /* CM_AUTOIDLE_DSP */
+ return s->clkidle[6];
+ case 0x840: /* CM_CLKSEL_DSP */
+ return s->clksel[7];
+ case 0x848: /* CM_CLKSTCTRL_DSP */
+ return s->clkctrl[3];
+ case 0x850: /* RM_RSTCTRL_DSP */
+ return 0;
+ case 0x858: /* RM_RSTST_DSP */
+ return s->rst[3];
+ case 0x8c8: /* PM_WKDEP_DSP */
+ return s->wkup[2];
+ case 0x8e0: /* PM_PWSTCTRL_DSP */
+ return s->power[3];
+ case 0x8e4: /* PM_PWSTST_DSP */
+ return 0x008030 | (s->power[3] & 0x3003);
+
+ case 0x8f0: /* PRCM_IRQSTATUS_DSP */
+ return s->irqst[1];
+ case 0x8f4: /* PRCM_IRQENABLE_DSP */
+ return s->irqen[1];
+
+ case 0x8f8: /* PRCM_IRQSTATUS_IVA */
+ return s->irqst[2];
+ case 0x8fc: /* PRCM_IRQENABLE_IVA */
+ return s->irqen[2];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_prcm_apll_update(struct omap_prcm_s *s)
+{
+ int mode[2];
+
+ mode[0] = (s->clken[9] >> 6) & 3;
+ s->apll_lock[0] = (mode[0] == 3);
+ mode[1] = (s->clken[9] >> 2) & 3;
+ s->apll_lock[1] = (mode[1] == 3);
+ /* TODO: update clocks */
+
+ if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[1] == 2)
+ fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n",
+ __FUNCTION__);
+}
+
+static void omap_prcm_dpll_update(struct omap_prcm_s *s)
+{
+ omap_clk dpll = omap_findclk(s->mpu, "dpll");
+ omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll");
+ omap_clk core = omap_findclk(s->mpu, "core_clk");
+ int mode = (s->clken[9] >> 0) & 3;
+ int mult, div;
+
+ mult = (s->clksel[5] >> 12) & 0x3ff;
+ div = (s->clksel[5] >> 8) & 0xf;
+ if (mult == 0 || mult == 1)
+ mode = 1; /* Bypass */
+
+ s->dpll_lock = 0;
+ switch (mode) {
+ case 0:
+ fprintf(stderr, "%s: bad EN_DPLL\n", __FUNCTION__);
+ break;
+ case 1: /* Low-power bypass mode (Default) */
+ case 2: /* Fast-relock bypass mode */
+ omap_clk_setrate(dpll, 1, 1);
+ omap_clk_setrate(dpll_x2, 1, 1);
+ break;
+ case 3: /* Lock mode */
+ s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)). */
+
+ omap_clk_setrate(dpll, div + 1, mult);
+ omap_clk_setrate(dpll_x2, div + 1, mult * 2);
+ break;
+ }
+
+ switch ((s->clksel[6] >> 0) & 3) {
+ case 0:
+ omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz"));
+ break;
+ case 1:
+ omap_clk_reparent(core, dpll);
+ break;
+ case 2:
+ /* Default */
+ omap_clk_reparent(core, dpll_x2);
+ break;
+ case 3:
+ fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __FUNCTION__);
+ break;
+ }
+}
+
+static void omap_prcm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
+
+ if (size != 4) {
+ return omap_badwidth_write32(opaque, addr, value);
+ }
+
+ switch (addr) {
+ case 0x000: /* PRCM_REVISION */
+ case 0x054: /* PRCM_VOLTST */
+ case 0x084: /* PRCM_CLKCFG_STATUS */
+ case 0x1e4: /* PM_PWSTST_MPU */
+ case 0x220: /* CM_IDLEST1_CORE */
+ case 0x224: /* CM_IDLEST2_CORE */
+ case 0x22c: /* CM_IDLEST4_CORE */
+ case 0x2c8: /* PM_WKDEP_CORE */
+ case 0x2e4: /* PM_PWSTST_CORE */
+ case 0x320: /* CM_IDLEST_GFX */
+ case 0x3e4: /* PM_PWSTST_GFX */
+ case 0x420: /* CM_IDLEST_WKUP */
+ case 0x520: /* CM_IDLEST_CKGEN */
+ case 0x820: /* CM_IDLEST_DSP */
+ case 0x8e4: /* PM_PWSTST_DSP */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x010: /* PRCM_SYSCONFIG */
+ s->sysconfig = value & 1;
+ break;
+
+ case 0x018: /* PRCM_IRQSTATUS_MPU */
+ s->irqst[0] &= ~value;
+ omap_prcm_int_update(s, 0);
+ break;
+ case 0x01c: /* PRCM_IRQENABLE_MPU */
+ s->irqen[0] = value & 0x3f;
+ omap_prcm_int_update(s, 0);
+ break;
+
+ case 0x050: /* PRCM_VOLTCTRL */
+ s->voltctrl = value & 0xf1c3;
+ break;
+
+ case 0x060: /* PRCM_CLKSRC_CTRL */
+ s->clksrc[0] = value & 0xdb;
+ /* TODO update clocks */
+ break;
+
+ case 0x070: /* PRCM_CLKOUT_CTRL */
+ s->clkout[0] = value & 0xbbbb;
+ /* TODO update clocks */
+ break;
+
+ case 0x078: /* PRCM_CLKEMUL_CTRL */
+ s->clkemul[0] = value & 1;
+ /* TODO update clocks */
+ break;
+
+ case 0x080: /* PRCM_CLKCFG_CTRL */
+ break;
+
+ case 0x090: /* PRCM_VOLTSETUP */
+ s->setuptime[0] = value & 0xffff;
+ break;
+ case 0x094: /* PRCM_CLKSSETUP */
+ s->setuptime[1] = value & 0xffff;
+ break;
+
+ case 0x098: /* PRCM_POLCTRL */
+ s->clkpol[0] = value & 0x701;
+ break;
+
+ case 0x0b0: /* GENERAL_PURPOSE1 */
+ case 0x0b4: /* GENERAL_PURPOSE2 */
+ case 0x0b8: /* GENERAL_PURPOSE3 */
+ case 0x0bc: /* GENERAL_PURPOSE4 */
+ case 0x0c0: /* GENERAL_PURPOSE5 */
+ case 0x0c4: /* GENERAL_PURPOSE6 */
+ case 0x0c8: /* GENERAL_PURPOSE7 */
+ case 0x0cc: /* GENERAL_PURPOSE8 */
+ case 0x0d0: /* GENERAL_PURPOSE9 */
+ case 0x0d4: /* GENERAL_PURPOSE10 */
+ case 0x0d8: /* GENERAL_PURPOSE11 */
+ case 0x0dc: /* GENERAL_PURPOSE12 */
+ case 0x0e0: /* GENERAL_PURPOSE13 */
+ case 0x0e4: /* GENERAL_PURPOSE14 */
+ case 0x0e8: /* GENERAL_PURPOSE15 */
+ case 0x0ec: /* GENERAL_PURPOSE16 */
+ case 0x0f0: /* GENERAL_PURPOSE17 */
+ case 0x0f4: /* GENERAL_PURPOSE18 */
+ case 0x0f8: /* GENERAL_PURPOSE19 */
+ case 0x0fc: /* GENERAL_PURPOSE20 */
+ s->scratch[(addr - 0xb0) >> 2] = value;
+ break;
+
+ case 0x140: /* CM_CLKSEL_MPU */
+ s->clksel[0] = value & 0x1f;
+ /* TODO update clocks */
+ break;
+ case 0x148: /* CM_CLKSTCTRL_MPU */
+ s->clkctrl[0] = value & 0x1f;
+ break;
+
+ case 0x158: /* RM_RSTST_MPU */
+ s->rst[0] &= ~value;
+ break;
+ case 0x1c8: /* PM_WKDEP_MPU */
+ s->wkup[0] = value & 0x15;
+ break;
+
+ case 0x1d4: /* PM_EVGENCTRL_MPU */
+ s->ev = value & 0x1f;
+ break;
+ case 0x1d8: /* PM_EVEGENONTIM_MPU */
+ s->evtime[0] = value;
+ break;
+ case 0x1dc: /* PM_EVEGENOFFTIM_MPU */
+ s->evtime[1] = value;
+ break;
+
+ case 0x1e0: /* PM_PWSTCTRL_MPU */
+ s->power[0] = value & 0xc0f;
+ break;
+
+ case 0x200: /* CM_FCLKEN1_CORE */
+ s->clken[0] = value & 0xbfffffff;
+ /* TODO update clocks */
+ /* The EN_EAC bit only gets/puts func_96m_clk. */
+ break;
+ case 0x204: /* CM_FCLKEN2_CORE */
+ s->clken[1] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x210: /* CM_ICLKEN1_CORE */
+ s->clken[2] = value & 0xfffffff9;
+ /* TODO update clocks */
+ /* The EN_EAC bit only gets/puts core_l4_iclk. */
+ break;
+ case 0x214: /* CM_ICLKEN2_CORE */
+ s->clken[3] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x21c: /* CM_ICLKEN4_CORE */
+ s->clken[4] = value & 0x0000001f;
+ /* TODO update clocks */
+ break;
+
+ case 0x230: /* CM_AUTOIDLE1_CORE */
+ s->clkidle[0] = value & 0xfffffff9;
+ /* TODO update clocks */
+ break;
+ case 0x234: /* CM_AUTOIDLE2_CORE */
+ s->clkidle[1] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x238: /* CM_AUTOIDLE3_CORE */
+ s->clkidle[2] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x23c: /* CM_AUTOIDLE4_CORE */
+ s->clkidle[3] = value & 0x0000001f;
+ /* TODO update clocks */
+ break;
+
+ case 0x240: /* CM_CLKSEL1_CORE */
+ s->clksel[1] = value & 0x0fffbf7f;
+ /* TODO update clocks */
+ break;
+
+ case 0x244: /* CM_CLKSEL2_CORE */
+ s->clksel[2] = value & 0x00fffffc;
+ /* TODO update clocks */
+ break;
+
+ case 0x248: /* CM_CLKSTCTRL_CORE */
+ s->clkctrl[1] = value & 0x7;
+ break;
+
+ case 0x2a0: /* PM_WKEN1_CORE */
+ s->wken[0] = value & 0x04667ff8;
+ break;
+ case 0x2a4: /* PM_WKEN2_CORE */
+ s->wken[1] = value & 0x00000005;
+ break;
+
+ case 0x2b0: /* PM_WKST1_CORE */
+ s->wkst[0] &= ~value;
+ break;
+ case 0x2b4: /* PM_WKST2_CORE */
+ s->wkst[1] &= ~value;
+ break;
+
+ case 0x2e0: /* PM_PWSTCTRL_CORE */
+ s->power[1] = (value & 0x00fc3f) | (1 << 2);
+ break;
+
+ case 0x300: /* CM_FCLKEN_GFX */
+ s->clken[5] = value & 6;
+ /* TODO update clocks */
+ break;
+ case 0x310: /* CM_ICLKEN_GFX */
+ s->clken[6] = value & 1;
+ /* TODO update clocks */
+ break;
+ case 0x340: /* CM_CLKSEL_GFX */
+ s->clksel[3] = value & 7;
+ /* TODO update clocks */
+ break;
+ case 0x348: /* CM_CLKSTCTRL_GFX */
+ s->clkctrl[2] = value & 1;
+ break;
+ case 0x350: /* RM_RSTCTRL_GFX */
+ s->rstctrl[0] = value & 1;
+ /* TODO: reset */
+ break;
+ case 0x358: /* RM_RSTST_GFX */
+ s->rst[1] &= ~value;
+ break;
+ case 0x3c8: /* PM_WKDEP_GFX */
+ s->wkup[1] = value & 0x13;
+ break;
+ case 0x3e0: /* PM_PWSTCTRL_GFX */
+ s->power[2] = (value & 0x00c0f) | (3 << 2);
+ break;
+
+ case 0x400: /* CM_FCLKEN_WKUP */
+ s->clken[7] = value & 0xd;
+ /* TODO update clocks */
+ break;
+ case 0x410: /* CM_ICLKEN_WKUP */
+ s->clken[8] = value & 0x3f;
+ /* TODO update clocks */
+ break;
+ case 0x430: /* CM_AUTOIDLE_WKUP */
+ s->clkidle[4] = value & 0x0000003f;
+ /* TODO update clocks */
+ break;
+ case 0x440: /* CM_CLKSEL_WKUP */
+ s->clksel[4] = value & 3;
+ /* TODO update clocks */
+ break;
+ case 0x450: /* RM_RSTCTRL_WKUP */
+ /* TODO: reset */
+ if (value & 2)
+ qemu_system_reset_request();
+ break;
+ case 0x454: /* RM_RSTTIME_WKUP */
+ s->rsttime_wkup = value & 0x1fff;
+ break;
+ case 0x458: /* RM_RSTST_WKUP */
+ s->rst[2] &= ~value;
+ break;
+ case 0x4a0: /* PM_WKEN_WKUP */
+ s->wken[2] = value & 0x00000005;
+ break;
+ case 0x4b0: /* PM_WKST_WKUP */
+ s->wkst[2] &= ~value;
+ break;
+
+ case 0x500: /* CM_CLKEN_PLL */
+ if (value & 0xffffff30)
+ fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for "
+ "future compatibility\n", __FUNCTION__);
+ if ((s->clken[9] ^ value) & 0xcc) {
+ s->clken[9] &= ~0xcc;
+ s->clken[9] |= value & 0xcc;
+ omap_prcm_apll_update(s);
+ }
+ if ((s->clken[9] ^ value) & 3) {
+ s->clken[9] &= ~3;
+ s->clken[9] |= value & 3;
+ omap_prcm_dpll_update(s);
+ }
+ break;
+ case 0x530: /* CM_AUTOIDLE_PLL */
+ s->clkidle[5] = value & 0x000000cf;
+ /* TODO update clocks */
+ break;
+ case 0x540: /* CM_CLKSEL1_PLL */
+ if (value & 0xfc4000d7)
+ fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for "
+ "future compatibility\n", __FUNCTION__);
+ if ((s->clksel[5] ^ value) & 0x003fff00) {
+ s->clksel[5] = value & 0x03bfff28;
+ omap_prcm_dpll_update(s);
+ }
+ /* TODO update the other clocks */
+
+ s->clksel[5] = value & 0x03bfff28;
+ break;
+ case 0x544: /* CM_CLKSEL2_PLL */
+ if (value & ~3)
+ fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for "
+ "future compatibility\n", __FUNCTION__);
+ if (s->clksel[6] != (value & 3)) {
+ s->clksel[6] = value & 3;
+ omap_prcm_dpll_update(s);
+ }
+ break;
+
+ case 0x800: /* CM_FCLKEN_DSP */
+ s->clken[10] = value & 0x501;
+ /* TODO update clocks */
+ break;
+ case 0x810: /* CM_ICLKEN_DSP */
+ s->clken[11] = value & 0x2;
+ /* TODO update clocks */
+ break;
+ case 0x830: /* CM_AUTOIDLE_DSP */
+ s->clkidle[6] = value & 0x2;
+ /* TODO update clocks */
+ break;
+ case 0x840: /* CM_CLKSEL_DSP */
+ s->clksel[7] = value & 0x3fff;
+ /* TODO update clocks */
+ break;
+ case 0x848: /* CM_CLKSTCTRL_DSP */
+ s->clkctrl[3] = value & 0x101;
+ break;
+ case 0x850: /* RM_RSTCTRL_DSP */
+ /* TODO: reset */
+ break;
+ case 0x858: /* RM_RSTST_DSP */
+ s->rst[3] &= ~value;
+ break;
+ case 0x8c8: /* PM_WKDEP_DSP */
+ s->wkup[2] = value & 0x13;
+ break;
+ case 0x8e0: /* PM_PWSTCTRL_DSP */
+ s->power[3] = (value & 0x03017) | (3 << 2);
+ break;
+
+ case 0x8f0: /* PRCM_IRQSTATUS_DSP */
+ s->irqst[1] &= ~value;
+ omap_prcm_int_update(s, 1);
+ break;
+ case 0x8f4: /* PRCM_IRQENABLE_DSP */
+ s->irqen[1] = value & 0x7;
+ omap_prcm_int_update(s, 1);
+ break;
+
+ case 0x8f8: /* PRCM_IRQSTATUS_IVA */
+ s->irqst[2] &= ~value;
+ omap_prcm_int_update(s, 2);
+ break;
+ case 0x8fc: /* PRCM_IRQENABLE_IVA */
+ s->irqen[2] = value & 0x7;
+ omap_prcm_int_update(s, 2);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_prcm_ops = {
+ .read = omap_prcm_read,
+ .write = omap_prcm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_prcm_reset(struct omap_prcm_s *s)
+{
+ s->sysconfig = 0;
+ s->irqst[0] = 0;
+ s->irqst[1] = 0;
+ s->irqst[2] = 0;
+ s->irqen[0] = 0;
+ s->irqen[1] = 0;
+ s->irqen[2] = 0;
+ s->voltctrl = 0x1040;
+ s->ev = 0x14;
+ s->evtime[0] = 0;
+ s->evtime[1] = 0;
+ s->clkctrl[0] = 0;
+ s->clkctrl[1] = 0;
+ s->clkctrl[2] = 0;
+ s->clkctrl[3] = 0;
+ s->clken[1] = 7;
+ s->clken[3] = 7;
+ s->clken[4] = 0;
+ s->clken[5] = 0;
+ s->clken[6] = 0;
+ s->clken[7] = 0xc;
+ s->clken[8] = 0x3e;
+ s->clken[9] = 0x0d;
+ s->clken[10] = 0;
+ s->clken[11] = 0;
+ s->clkidle[0] = 0;
+ s->clkidle[2] = 7;
+ s->clkidle[3] = 0;
+ s->clkidle[4] = 0;
+ s->clkidle[5] = 0x0c;
+ s->clkidle[6] = 0;
+ s->clksel[0] = 0x01;
+ s->clksel[1] = 0x02100121;
+ s->clksel[2] = 0x00000000;
+ s->clksel[3] = 0x01;
+ s->clksel[4] = 0;
+ s->clksel[7] = 0x0121;
+ s->wkup[0] = 0x15;
+ s->wkup[1] = 0x13;
+ s->wkup[2] = 0x13;
+ s->wken[0] = 0x04667ff8;
+ s->wken[1] = 0x00000005;
+ s->wken[2] = 5;
+ s->wkst[0] = 0;
+ s->wkst[1] = 0;
+ s->wkst[2] = 0;
+ s->power[0] = 0x00c;
+ s->power[1] = 4;
+ s->power[2] = 0x0000c;
+ s->power[3] = 0x14;
+ s->rstctrl[0] = 1;
+ s->rst[3] = 1;
+ omap_prcm_apll_update(s);
+ omap_prcm_dpll_update(s);
+}
+
+static void omap_prcm_coldreset(struct omap_prcm_s *s)
+{
+ s->setuptime[0] = 0;
+ s->setuptime[1] = 0;
+ memset(&s->scratch, 0, sizeof(s->scratch));
+ s->rst[0] = 0x01;
+ s->rst[1] = 0x00;
+ s->rst[2] = 0x01;
+ s->clken[0] = 0;
+ s->clken[2] = 0;
+ s->clkidle[1] = 0;
+ s->clksel[5] = 0;
+ s->clksel[6] = 2;
+ s->clksrc[0] = 0x43;
+ s->clkout[0] = 0x0303;
+ s->clkemul[0] = 0;
+ s->clkpol[0] = 0x100;
+ s->rsttime_wkup = 0x1002;
+
+ omap_prcm_reset(s);
+}
+
+static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
+ qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
+ struct omap_mpu_state_s *mpu)
+{
+ struct omap_prcm_s *s = (struct omap_prcm_s *)
+ g_malloc0(sizeof(struct omap_prcm_s));
+
+ s->irq[0] = mpu_int;
+ s->irq[1] = dsp_int;
+ s->irq[2] = iva_int;
+ s->mpu = mpu;
+ omap_prcm_coldreset(s);
+
+ memory_region_init_io(&s->iomem0, &omap_prcm_ops, s, "omap.pcrm0",
+ omap_l4_region_size(ta, 0));
+ memory_region_init_io(&s->iomem1, &omap_prcm_ops, s, "omap.pcrm1",
+ omap_l4_region_size(ta, 1));
+ omap_l4_attach(ta, 0, &s->iomem0);
+ omap_l4_attach(ta, 1, &s->iomem1);
+
+ return s;
+}
+
+/* System and Pinout control */
+struct omap_sysctl_s {
+ struct omap_mpu_state_s *mpu;
+ MemoryRegion iomem;
+
+ uint32_t sysconfig;
+ uint32_t devconfig;
+ uint32_t psaconfig;
+ uint32_t padconf[0x45];
+ uint8_t obs;
+ uint32_t msuspendmux[5];
+};
+
+static uint32_t omap_sysctl_read8(void *opaque, hwaddr addr)
+{
+
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+ int pad_offset, byte_offset;
+ int value;
+
+ switch (addr) {
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ pad_offset = (addr - 0x30) >> 2;
+ byte_offset = (addr - 0x30) & (4 - 1);
+
+ value = s->padconf[pad_offset];
+ value = (value >> (byte_offset * 8)) & 0xff;
+
+ return value;
+
+ default:
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static uint32_t omap_sysctl_read(void *opaque, hwaddr addr)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* CONTROL_REVISION */
+ return 0x20;
+
+ case 0x010: /* CONTROL_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ return s->padconf[(addr - 0x30) >> 2];
+
+ case 0x270: /* CONTROL_DEBOBS */
+ return s->obs;
+
+ case 0x274: /* CONTROL_DEVCONF */
+ return s->devconfig;
+
+ case 0x28c: /* CONTROL_EMU_SUPPORT */
+ return 0;
+
+ case 0x290: /* CONTROL_MSUSPENDMUX_0 */
+ return s->msuspendmux[0];
+ case 0x294: /* CONTROL_MSUSPENDMUX_1 */
+ return s->msuspendmux[1];
+ case 0x298: /* CONTROL_MSUSPENDMUX_2 */
+ return s->msuspendmux[2];
+ case 0x29c: /* CONTROL_MSUSPENDMUX_3 */
+ return s->msuspendmux[3];
+ case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */
+ return s->msuspendmux[4];
+ case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */
+ return 0;
+
+ case 0x2b8: /* CONTROL_PSA_CTRL */
+ return s->psaconfig;
+ case 0x2bc: /* CONTROL_PSA_CMD */
+ case 0x2c0: /* CONTROL_PSA_VALUE */
+ return 0;
+
+ case 0x2b0: /* CONTROL_SEC_CTRL */
+ return 0x800000f1;
+ case 0x2d0: /* CONTROL_SEC_EMU */
+ return 0x80000015;
+ case 0x2d4: /* CONTROL_SEC_TAP */
+ return 0x8000007f;
+ case 0x2b4: /* CONTROL_SEC_TEST */
+ case 0x2f0: /* CONTROL_SEC_STATUS */
+ case 0x2f4: /* CONTROL_SEC_ERR_STATUS */
+ /* Secure mode is not present on general-pusrpose device. Outside
+ * secure mode these values cannot be read or written. */
+ return 0;
+
+ case 0x2d8: /* CONTROL_OCM_RAM_PERM */
+ return 0xff;
+ case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */
+ case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */
+ case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */
+ /* No secure mode so no Extended Secure RAM present. */
+ return 0;
+
+ case 0x2f8: /* CONTROL_STATUS */
+ /* Device Type => General-purpose */
+ return 0x0300;
+ case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */
+
+ case 0x300: /* CONTROL_RPUB_KEY_H_0 */
+ case 0x304: /* CONTROL_RPUB_KEY_H_1 */
+ case 0x308: /* CONTROL_RPUB_KEY_H_2 */
+ case 0x30c: /* CONTROL_RPUB_KEY_H_3 */
+ return 0xdecafbad;
+
+ case 0x310: /* CONTROL_RAND_KEY_0 */
+ case 0x314: /* CONTROL_RAND_KEY_1 */
+ case 0x318: /* CONTROL_RAND_KEY_2 */
+ case 0x31c: /* CONTROL_RAND_KEY_3 */
+ case 0x320: /* CONTROL_CUST_KEY_0 */
+ case 0x324: /* CONTROL_CUST_KEY_1 */
+ case 0x330: /* CONTROL_TEST_KEY_0 */
+ case 0x334: /* CONTROL_TEST_KEY_1 */
+ case 0x338: /* CONTROL_TEST_KEY_2 */
+ case 0x33c: /* CONTROL_TEST_KEY_3 */
+ case 0x340: /* CONTROL_TEST_KEY_4 */
+ case 0x344: /* CONTROL_TEST_KEY_5 */
+ case 0x348: /* CONTROL_TEST_KEY_6 */
+ case 0x34c: /* CONTROL_TEST_KEY_7 */
+ case 0x350: /* CONTROL_TEST_KEY_8 */
+ case 0x354: /* CONTROL_TEST_KEY_9 */
+ /* Can only be accessed in secure mode and when C_FieldAccEnable
+ * bit is set in CONTROL_SEC_CTRL.
+ * TODO: otherwise an interconnect access error is generated. */
+ return 0;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sysctl_write8(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+ int pad_offset, byte_offset;
+ int prev_value;
+
+ switch (addr) {
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ pad_offset = (addr - 0x30) >> 2;
+ byte_offset = (addr - 0x30) & (4 - 1);
+
+ prev_value = s->padconf[pad_offset];
+ prev_value &= ~(0xff << (byte_offset * 8));
+ prev_value |= ((value & 0x1f1f1f1f) << (byte_offset * 8)) & 0x1f1f1f1f;
+ s->padconf[pad_offset] = prev_value;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ break;
+ }
+}
+
+static void omap_sysctl_write(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* CONTROL_REVISION */
+ case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */
+ case 0x2c0: /* CONTROL_PSA_VALUE */
+ case 0x2f8: /* CONTROL_STATUS */
+ case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */
+ case 0x300: /* CONTROL_RPUB_KEY_H_0 */
+ case 0x304: /* CONTROL_RPUB_KEY_H_1 */
+ case 0x308: /* CONTROL_RPUB_KEY_H_2 */
+ case 0x30c: /* CONTROL_RPUB_KEY_H_3 */
+ case 0x310: /* CONTROL_RAND_KEY_0 */
+ case 0x314: /* CONTROL_RAND_KEY_1 */
+ case 0x318: /* CONTROL_RAND_KEY_2 */
+ case 0x31c: /* CONTROL_RAND_KEY_3 */
+ case 0x320: /* CONTROL_CUST_KEY_0 */
+ case 0x324: /* CONTROL_CUST_KEY_1 */
+ case 0x330: /* CONTROL_TEST_KEY_0 */
+ case 0x334: /* CONTROL_TEST_KEY_1 */
+ case 0x338: /* CONTROL_TEST_KEY_2 */
+ case 0x33c: /* CONTROL_TEST_KEY_3 */
+ case 0x340: /* CONTROL_TEST_KEY_4 */
+ case 0x344: /* CONTROL_TEST_KEY_5 */
+ case 0x348: /* CONTROL_TEST_KEY_6 */
+ case 0x34c: /* CONTROL_TEST_KEY_7 */
+ case 0x350: /* CONTROL_TEST_KEY_8 */
+ case 0x354: /* CONTROL_TEST_KEY_9 */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x010: /* CONTROL_SYSCONFIG */
+ s->sysconfig = value & 0x1e;
+ break;
+
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ /* XXX: should check constant bits */
+ s->padconf[(addr - 0x30) >> 2] = value & 0x1f1f1f1f;
+ break;
+
+ case 0x270: /* CONTROL_DEBOBS */
+ s->obs = value & 0xff;
+ break;
+
+ case 0x274: /* CONTROL_DEVCONF */
+ s->devconfig = value & 0xffffc7ff;
+ break;
+
+ case 0x28c: /* CONTROL_EMU_SUPPORT */
+ break;
+
+ case 0x290: /* CONTROL_MSUSPENDMUX_0 */
+ s->msuspendmux[0] = value & 0x3fffffff;
+ break;
+ case 0x294: /* CONTROL_MSUSPENDMUX_1 */
+ s->msuspendmux[1] = value & 0x3fffffff;
+ break;
+ case 0x298: /* CONTROL_MSUSPENDMUX_2 */
+ s->msuspendmux[2] = value & 0x3fffffff;
+ break;
+ case 0x29c: /* CONTROL_MSUSPENDMUX_3 */
+ s->msuspendmux[3] = value & 0x3fffffff;
+ break;
+ case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */
+ s->msuspendmux[4] = value & 0x3fffffff;
+ break;
+
+ case 0x2b8: /* CONTROL_PSA_CTRL */
+ s->psaconfig = value & 0x1c;
+ s->psaconfig |= (value & 0x20) ? 2 : 1;
+ break;
+ case 0x2bc: /* CONTROL_PSA_CMD */
+ break;
+
+ case 0x2b0: /* CONTROL_SEC_CTRL */
+ case 0x2b4: /* CONTROL_SEC_TEST */
+ case 0x2d0: /* CONTROL_SEC_EMU */
+ case 0x2d4: /* CONTROL_SEC_TAP */
+ case 0x2d8: /* CONTROL_OCM_RAM_PERM */
+ case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */
+ case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */
+ case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */
+ case 0x2f0: /* CONTROL_SEC_STATUS */
+ case 0x2f4: /* CONTROL_SEC_ERR_STATUS */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static const MemoryRegionOps omap_sysctl_ops = {
+ .old_mmio = {
+ .read = {
+ omap_sysctl_read8,
+ omap_badwidth_read32, /* TODO */
+ omap_sysctl_read,
+ },
+ .write = {
+ omap_sysctl_write8,
+ omap_badwidth_write32, /* TODO */
+ omap_sysctl_write,
+ },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void omap_sysctl_reset(struct omap_sysctl_s *s)
+{
+ /* (power-on reset) */
+ s->sysconfig = 0;
+ s->obs = 0;
+ s->devconfig = 0x0c000000;
+ s->msuspendmux[0] = 0x00000000;
+ s->msuspendmux[1] = 0x00000000;
+ s->msuspendmux[2] = 0x00000000;
+ s->msuspendmux[3] = 0x00000000;
+ s->msuspendmux[4] = 0x00000000;
+ s->psaconfig = 1;
+
+ s->padconf[0x00] = 0x000f0f0f;
+ s->padconf[0x01] = 0x00000000;
+ s->padconf[0x02] = 0x00000000;
+ s->padconf[0x03] = 0x00000000;
+ s->padconf[0x04] = 0x00000000;
+ s->padconf[0x05] = 0x00000000;
+ s->padconf[0x06] = 0x00000000;
+ s->padconf[0x07] = 0x00000000;
+ s->padconf[0x08] = 0x08080800;
+ s->padconf[0x09] = 0x08080808;
+ s->padconf[0x0a] = 0x08080808;
+ s->padconf[0x0b] = 0x08080808;
+ s->padconf[0x0c] = 0x08080808;
+ s->padconf[0x0d] = 0x08080800;
+ s->padconf[0x0e] = 0x08080808;
+ s->padconf[0x0f] = 0x08080808;
+ s->padconf[0x10] = 0x18181808; /* | 0x07070700 if SBoot3 */
+ s->padconf[0x11] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x12] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x13] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x14] = 0x18181818; /* | 0x00070707 if SBoot3 */
+ s->padconf[0x15] = 0x18181818;
+ s->padconf[0x16] = 0x18181818; /* | 0x07000000 if SBoot3 */
+ s->padconf[0x17] = 0x1f001f00;
+ s->padconf[0x18] = 0x1f1f1f1f;
+ s->padconf[0x19] = 0x00000000;
+ s->padconf[0x1a] = 0x1f180000;
+ s->padconf[0x1b] = 0x00001f1f;
+ s->padconf[0x1c] = 0x1f001f00;
+ s->padconf[0x1d] = 0x00000000;
+ s->padconf[0x1e] = 0x00000000;
+ s->padconf[0x1f] = 0x08000000;
+ s->padconf[0x20] = 0x08080808;
+ s->padconf[0x21] = 0x08080808;
+ s->padconf[0x22] = 0x0f080808;
+ s->padconf[0x23] = 0x0f0f0f0f;
+ s->padconf[0x24] = 0x000f0f0f;
+ s->padconf[0x25] = 0x1f1f1f0f;
+ s->padconf[0x26] = 0x080f0f1f;
+ s->padconf[0x27] = 0x070f1808;
+ s->padconf[0x28] = 0x0f070707;
+ s->padconf[0x29] = 0x000f0f1f;
+ s->padconf[0x2a] = 0x0f0f0f1f;
+ s->padconf[0x2b] = 0x08000000;
+ s->padconf[0x2c] = 0x0000001f;
+ s->padconf[0x2d] = 0x0f0f1f00;
+ s->padconf[0x2e] = 0x1f1f0f0f;
+ s->padconf[0x2f] = 0x0f1f1f1f;
+ s->padconf[0x30] = 0x0f0f0f0f;
+ s->padconf[0x31] = 0x0f1f0f1f;
+ s->padconf[0x32] = 0x0f0f0f0f;
+ s->padconf[0x33] = 0x0f1f0f1f;
+ s->padconf[0x34] = 0x1f1f0f0f;
+ s->padconf[0x35] = 0x0f0f1f1f;
+ s->padconf[0x36] = 0x0f0f1f0f;
+ s->padconf[0x37] = 0x0f0f0f0f;
+ s->padconf[0x38] = 0x1f18180f;
+ s->padconf[0x39] = 0x1f1f1f1f;
+ s->padconf[0x3a] = 0x00001f1f;
+ s->padconf[0x3b] = 0x00000000;
+ s->padconf[0x3c] = 0x00000000;
+ s->padconf[0x3d] = 0x0f0f0f0f;
+ s->padconf[0x3e] = 0x18000f0f;
+ s->padconf[0x3f] = 0x00070000;
+ s->padconf[0x40] = 0x00000707;
+ s->padconf[0x41] = 0x0f1f0700;
+ s->padconf[0x42] = 0x1f1f070f;
+ s->padconf[0x43] = 0x0008081f;
+ s->padconf[0x44] = 0x00000800;
+}
+
+static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
+ omap_clk iclk, struct omap_mpu_state_s *mpu)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *)
+ g_malloc0(sizeof(struct omap_sysctl_s));
+
+ s->mpu = mpu;
+ omap_sysctl_reset(s);
+
+ memory_region_init_io(&s->iomem, &omap_sysctl_ops, s, "omap.sysctl",
+ omap_l4_region_size(ta, 0));
+ omap_l4_attach(ta, 0, &s->iomem);
+
+ return s;
+}
+
+/* General chip reset */
+static void omap2_mpu_reset(void *opaque)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ omap_dma_reset(mpu->dma);
+ omap_prcm_reset(mpu->prcm);
+ omap_sysctl_reset(mpu->sysc);
+ omap_gp_timer_reset(mpu->gptimer[0]);
+ omap_gp_timer_reset(mpu->gptimer[1]);
+ omap_gp_timer_reset(mpu->gptimer[2]);
+ omap_gp_timer_reset(mpu->gptimer[3]);
+ omap_gp_timer_reset(mpu->gptimer[4]);
+ omap_gp_timer_reset(mpu->gptimer[5]);
+ omap_gp_timer_reset(mpu->gptimer[6]);
+ omap_gp_timer_reset(mpu->gptimer[7]);
+ omap_gp_timer_reset(mpu->gptimer[8]);
+ omap_gp_timer_reset(mpu->gptimer[9]);
+ omap_gp_timer_reset(mpu->gptimer[10]);
+ omap_gp_timer_reset(mpu->gptimer[11]);
+ omap_synctimer_reset(mpu->synctimer);
+ omap_sdrc_reset(mpu->sdrc);
+ omap_gpmc_reset(mpu->gpmc);
+ omap_dss_reset(mpu->dss);
+ omap_uart_reset(mpu->uart[0]);
+ omap_uart_reset(mpu->uart[1]);
+ omap_uart_reset(mpu->uart[2]);
+ omap_mmc_reset(mpu->mmc);
+ omap_mcspi_reset(mpu->mcspi[0]);
+ omap_mcspi_reset(mpu->mcspi[1]);
+ cpu_reset(CPU(mpu->cpu));
+}
+
+static int omap2_validate_addr(struct omap_mpu_state_s *s,
+ hwaddr addr)
+{
+ return 1;
+}
+
+static const struct dma_irq_map omap2_dma_irq_map[] = {
+ { 0, OMAP_INT_24XX_SDMA_IRQ0 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ1 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ2 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ3 },
+};
+
+struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
+ unsigned long sdram_size,
+ const char *core)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
+ g_malloc0(sizeof(struct omap_mpu_state_s));
+ qemu_irq *cpu_irq;
+ qemu_irq dma_irqs[4];
+ DriveInfo *dinfo;
+ int i;
+ SysBusDevice *busdev;
+ struct omap_target_agent_s *ta;
+
+ /* Core */
+ s->mpu_model = omap2420;
+ s->cpu = cpu_arm_init(core ?: "arm1136-r2");
+ if (s->cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->sdram_size = sdram_size;
+ s->sram_size = OMAP242X_SRAM_SIZE;
+
+ s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
+
+ /* Clocks */
+ omap_clk_init(s);
+
+ /* Memory-mapped stuff */
+ memory_region_init_ram(&s->sdram, "omap2.dram", s->sdram_size);
+ vmstate_register_ram_global(&s->sdram);
+ memory_region_add_subregion(sysmem, OMAP2_Q2_BASE, &s->sdram);
+ memory_region_init_ram(&s->sram, "omap2.sram", s->sram_size);
+ vmstate_register_ram_global(&s->sram);
+ memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram);
+
+ s->l4 = omap_l4_init(sysmem, OMAP2_L4_BASE, 54);
+
+ /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */
+ cpu_irq = arm_pic_init_cpu(s->cpu);
+ s->ih[0] = qdev_create(NULL, "omap2-intc");
+ qdev_prop_set_uint8(s->ih[0], "revision", 0x21);
+ qdev_prop_set_ptr(s->ih[0], "fclk", omap_findclk(s, "mpu_intc_fclk"));
+ qdev_prop_set_ptr(s->ih[0], "iclk", omap_findclk(s, "mpu_intc_iclk"));
+ qdev_init_nofail(s->ih[0]);
+ busdev = SYS_BUS_DEVICE(s->ih[0]);
+ sysbus_connect_irq(busdev, 0, cpu_irq[ARM_PIC_CPU_IRQ]);
+ sysbus_connect_irq(busdev, 1, cpu_irq[ARM_PIC_CPU_FIQ]);
+ sysbus_mmio_map(busdev, 0, 0x480fe000);
+ s->prcm = omap_prcm_init(omap_l4tao(s->l4, 3),
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_24XX_PRCM_MPU_IRQ),
+ NULL, NULL, s);
+
+ s->sysc = omap_sysctl_init(omap_l4tao(s->l4, 1),
+ omap_findclk(s, "omapctrl_iclk"), s);
+
+ for (i = 0; i < 4; i++) {
+ dma_irqs[i] = qdev_get_gpio_in(s->ih[omap2_dma_irq_map[i].ih],
+ omap2_dma_irq_map[i].intr);
+ }
+ s->dma = omap_dma4_init(0x48056000, dma_irqs, sysmem, s, 256, 32,
+ omap_findclk(s, "sdma_iclk"),
+ omap_findclk(s, "sdma_fclk"));
+ s->port->addr_valid = omap2_validate_addr;
+
+ /* Register SDRAM and SRAM ports for fast DMA transfers. */
+ soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->sdram),
+ OMAP2_Q2_BASE, s->sdram_size);
+ soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->sram),
+ OMAP2_SRAM_BASE, s->sram_size);
+
+ s->uart[0] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 19),
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_24XX_UART1_IRQ),
+ omap_findclk(s, "uart1_fclk"),
+ omap_findclk(s, "uart1_iclk"),
+ s->drq[OMAP24XX_DMA_UART1_TX],
+ s->drq[OMAP24XX_DMA_UART1_RX],
+ "uart1",
+ serial_hds[0]);
+ s->uart[1] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 20),
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_24XX_UART2_IRQ),
+ omap_findclk(s, "uart2_fclk"),
+ omap_findclk(s, "uart2_iclk"),
+ s->drq[OMAP24XX_DMA_UART2_TX],
+ s->drq[OMAP24XX_DMA_UART2_RX],
+ "uart2",
+ serial_hds[0] ? serial_hds[1] : NULL);
+ s->uart[2] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 21),
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_24XX_UART3_IRQ),
+ omap_findclk(s, "uart3_fclk"),
+ omap_findclk(s, "uart3_iclk"),
+ s->drq[OMAP24XX_DMA_UART3_TX],
+ s->drq[OMAP24XX_DMA_UART3_RX],
+ "uart3",
+ serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
+
+ s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER1),
+ omap_findclk(s, "wu_gpt1_clk"),
+ omap_findclk(s, "wu_l4_iclk"));
+ s->gptimer[1] = omap_gp_timer_init(omap_l4ta(s->l4, 8),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER2),
+ omap_findclk(s, "core_gpt2_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[2] = omap_gp_timer_init(omap_l4ta(s->l4, 22),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER3),
+ omap_findclk(s, "core_gpt3_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[3] = omap_gp_timer_init(omap_l4ta(s->l4, 23),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER4),
+ omap_findclk(s, "core_gpt4_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[4] = omap_gp_timer_init(omap_l4ta(s->l4, 24),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER5),
+ omap_findclk(s, "core_gpt5_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[5] = omap_gp_timer_init(omap_l4ta(s->l4, 25),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER6),
+ omap_findclk(s, "core_gpt6_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[6] = omap_gp_timer_init(omap_l4ta(s->l4, 26),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER7),
+ omap_findclk(s, "core_gpt7_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[7] = omap_gp_timer_init(omap_l4ta(s->l4, 27),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER8),
+ omap_findclk(s, "core_gpt8_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[8] = omap_gp_timer_init(omap_l4ta(s->l4, 28),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER9),
+ omap_findclk(s, "core_gpt9_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[9] = omap_gp_timer_init(omap_l4ta(s->l4, 29),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER10),
+ omap_findclk(s, "core_gpt10_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[10] = omap_gp_timer_init(omap_l4ta(s->l4, 30),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER11),
+ omap_findclk(s, "core_gpt11_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[11] = omap_gp_timer_init(omap_l4ta(s->l4, 31),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER12),
+ omap_findclk(s, "core_gpt12_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ omap_tap_init(omap_l4ta(s->l4, 2), s);
+
+ s->synctimer = omap_synctimer_init(omap_l4tao(s->l4, 2), s,
+ omap_findclk(s, "clk32-kHz"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ s->i2c[0] = qdev_create(NULL, "omap_i2c");
+ qdev_prop_set_uint8(s->i2c[0], "revision", 0x34);
+ qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk"));
+ qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk"));
+ qdev_init_nofail(s->i2c[0]);
+ busdev = SYS_BUS_DEVICE(s->i2c[0]);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ));
+ sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]);
+ sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C1_RX]);
+ sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 5), 0));
+
+ s->i2c[1] = qdev_create(NULL, "omap_i2c");
+ qdev_prop_set_uint8(s->i2c[1], "revision", 0x34);
+ qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk"));
+ qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk"));
+ qdev_init_nofail(s->i2c[1]);
+ busdev = SYS_BUS_DEVICE(s->i2c[1]);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ));
+ sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]);
+ sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C2_RX]);
+ sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 6), 0));
+
+ s->gpio = qdev_create(NULL, "omap2-gpio");
+ qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model);
+ qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk"));
+ if (s->mpu_model == omap2430) {
+ qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk"));
+ }
+ qdev_init_nofail(s->gpio);
+ busdev = SYS_BUS_DEVICE(s->gpio);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK1));
+ sysbus_connect_irq(busdev, 3,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK2));
+ sysbus_connect_irq(busdev, 6,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK3));
+ sysbus_connect_irq(busdev, 9,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK4));
+ if (s->mpu_model == omap2430) {
+ sysbus_connect_irq(busdev, 12,
+ qdev_get_gpio_in(s->ih[0],
+ OMAP_INT_243X_GPIO_BANK5));
+ }
+ ta = omap_l4ta(s->l4, 3);
+ sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1));
+ sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0));
+ sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2));
+ sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4));
+ sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5));
+
+ s->sdrc = omap_sdrc_init(sysmem, 0x68009000);
+ s->gpmc = omap_gpmc_init(s, 0x6800a000,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPMC_IRQ),
+ s->drq[OMAP24XX_DMA_GPMC]);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), dinfo->bdrv,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ),
+ &s->drq[OMAP24XX_DMA_MMC1_TX],
+ omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk"));
+
+ s->mcspi[0] = omap_mcspi_init(omap_l4ta(s->l4, 35), 4,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI1_IRQ),
+ &s->drq[OMAP24XX_DMA_SPI1_TX0],
+ omap_findclk(s, "spi1_fclk"),
+ omap_findclk(s, "spi1_iclk"));
+ s->mcspi[1] = omap_mcspi_init(omap_l4ta(s->l4, 36), 2,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI2_IRQ),
+ &s->drq[OMAP24XX_DMA_SPI2_TX0],
+ omap_findclk(s, "spi2_fclk"),
+ omap_findclk(s, "spi2_iclk"));
+
+ s->dss = omap_dss_init(omap_l4ta(s->l4, 10), sysmem, 0x68000800,
+ /* XXX wire M_IRQ_25, D_L2_IRQ_30 and I_IRQ_13 together */
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_DSS_IRQ),
+ s->drq[OMAP24XX_DMA_DSS],
+ omap_findclk(s, "dss_clk1"), omap_findclk(s, "dss_clk2"),
+ omap_findclk(s, "dss_54m_clk"),
+ omap_findclk(s, "dss_l3_iclk"),
+ omap_findclk(s, "dss_l4_iclk"));
+
+ omap_sti_init(omap_l4ta(s->l4, 18), sysmem, 0x54000000,
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_STI),
+ omap_findclk(s, "emul_ck"),
+ serial_hds[0] && serial_hds[1] && serial_hds[2] ?
+ serial_hds[3] : NULL);
+
+ s->eac = omap_eac_init(omap_l4ta(s->l4, 32),
+ qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_EAC_IRQ),
+ /* Ten consecutive lines */
+ &s->drq[OMAP24XX_DMA_EAC_AC_RD],
+ omap_findclk(s, "func_96m_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ /* All register mappings (includin those not currenlty implemented):
+ * SystemControlMod 48000000 - 48000fff
+ * SystemControlL4 48001000 - 48001fff
+ * 32kHz Timer Mod 48004000 - 48004fff
+ * 32kHz Timer L4 48005000 - 48005fff
+ * PRCM ModA 48008000 - 480087ff
+ * PRCM ModB 48008800 - 48008fff
+ * PRCM L4 48009000 - 48009fff
+ * TEST-BCM Mod 48012000 - 48012fff
+ * TEST-BCM L4 48013000 - 48013fff
+ * TEST-TAP Mod 48014000 - 48014fff
+ * TEST-TAP L4 48015000 - 48015fff
+ * GPIO1 Mod 48018000 - 48018fff
+ * GPIO Top 48019000 - 48019fff
+ * GPIO2 Mod 4801a000 - 4801afff
+ * GPIO L4 4801b000 - 4801bfff
+ * GPIO3 Mod 4801c000 - 4801cfff
+ * GPIO4 Mod 4801e000 - 4801efff
+ * WDTIMER1 Mod 48020000 - 48010fff
+ * WDTIMER Top 48021000 - 48011fff
+ * WDTIMER2 Mod 48022000 - 48012fff
+ * WDTIMER L4 48023000 - 48013fff
+ * WDTIMER3 Mod 48024000 - 48014fff
+ * WDTIMER3 L4 48025000 - 48015fff
+ * WDTIMER4 Mod 48026000 - 48016fff
+ * WDTIMER4 L4 48027000 - 48017fff
+ * GPTIMER1 Mod 48028000 - 48018fff
+ * GPTIMER1 L4 48029000 - 48019fff
+ * GPTIMER2 Mod 4802a000 - 4801afff
+ * GPTIMER2 L4 4802b000 - 4801bfff
+ * L4-Config AP 48040000 - 480407ff
+ * L4-Config IP 48040800 - 48040fff
+ * L4-Config LA 48041000 - 48041fff
+ * ARM11ETB Mod 48048000 - 48049fff
+ * ARM11ETB L4 4804a000 - 4804afff
+ * DISPLAY Top 48050000 - 480503ff
+ * DISPLAY DISPC 48050400 - 480507ff
+ * DISPLAY RFBI 48050800 - 48050bff
+ * DISPLAY VENC 48050c00 - 48050fff
+ * DISPLAY L4 48051000 - 48051fff
+ * CAMERA Top 48052000 - 480523ff
+ * CAMERA core 48052400 - 480527ff
+ * CAMERA DMA 48052800 - 48052bff
+ * CAMERA MMU 48052c00 - 48052fff
+ * CAMERA L4 48053000 - 48053fff
+ * SDMA Mod 48056000 - 48056fff
+ * SDMA L4 48057000 - 48057fff
+ * SSI Top 48058000 - 48058fff
+ * SSI GDD 48059000 - 48059fff
+ * SSI Port1 4805a000 - 4805afff
+ * SSI Port2 4805b000 - 4805bfff
+ * SSI L4 4805c000 - 4805cfff
+ * USB Mod 4805e000 - 480fefff
+ * USB L4 4805f000 - 480fffff
+ * WIN_TRACER1 Mod 48060000 - 48060fff
+ * WIN_TRACER1 L4 48061000 - 48061fff
+ * WIN_TRACER2 Mod 48062000 - 48062fff
+ * WIN_TRACER2 L4 48063000 - 48063fff
+ * WIN_TRACER3 Mod 48064000 - 48064fff
+ * WIN_TRACER3 L4 48065000 - 48065fff
+ * WIN_TRACER4 Top 48066000 - 480660ff
+ * WIN_TRACER4 ETT 48066100 - 480661ff
+ * WIN_TRACER4 WT 48066200 - 480662ff
+ * WIN_TRACER4 L4 48067000 - 48067fff
+ * XTI Mod 48068000 - 48068fff
+ * XTI L4 48069000 - 48069fff
+ * UART1 Mod 4806a000 - 4806afff
+ * UART1 L4 4806b000 - 4806bfff
+ * UART2 Mod 4806c000 - 4806cfff
+ * UART2 L4 4806d000 - 4806dfff
+ * UART3 Mod 4806e000 - 4806efff
+ * UART3 L4 4806f000 - 4806ffff
+ * I2C1 Mod 48070000 - 48070fff
+ * I2C1 L4 48071000 - 48071fff
+ * I2C2 Mod 48072000 - 48072fff
+ * I2C2 L4 48073000 - 48073fff
+ * McBSP1 Mod 48074000 - 48074fff
+ * McBSP1 L4 48075000 - 48075fff
+ * McBSP2 Mod 48076000 - 48076fff
+ * McBSP2 L4 48077000 - 48077fff
+ * GPTIMER3 Mod 48078000 - 48078fff
+ * GPTIMER3 L4 48079000 - 48079fff
+ * GPTIMER4 Mod 4807a000 - 4807afff
+ * GPTIMER4 L4 4807b000 - 4807bfff
+ * GPTIMER5 Mod 4807c000 - 4807cfff
+ * GPTIMER5 L4 4807d000 - 4807dfff
+ * GPTIMER6 Mod 4807e000 - 4807efff
+ * GPTIMER6 L4 4807f000 - 4807ffff
+ * GPTIMER7 Mod 48080000 - 48080fff
+ * GPTIMER7 L4 48081000 - 48081fff
+ * GPTIMER8 Mod 48082000 - 48082fff
+ * GPTIMER8 L4 48083000 - 48083fff
+ * GPTIMER9 Mod 48084000 - 48084fff
+ * GPTIMER9 L4 48085000 - 48085fff
+ * GPTIMER10 Mod 48086000 - 48086fff
+ * GPTIMER10 L4 48087000 - 48087fff
+ * GPTIMER11 Mod 48088000 - 48088fff
+ * GPTIMER11 L4 48089000 - 48089fff
+ * GPTIMER12 Mod 4808a000 - 4808afff
+ * GPTIMER12 L4 4808b000 - 4808bfff
+ * EAC Mod 48090000 - 48090fff
+ * EAC L4 48091000 - 48091fff
+ * FAC Mod 48092000 - 48092fff
+ * FAC L4 48093000 - 48093fff
+ * MAILBOX Mod 48094000 - 48094fff
+ * MAILBOX L4 48095000 - 48095fff
+ * SPI1 Mod 48098000 - 48098fff
+ * SPI1 L4 48099000 - 48099fff
+ * SPI2 Mod 4809a000 - 4809afff
+ * SPI2 L4 4809b000 - 4809bfff
+ * MMC/SDIO Mod 4809c000 - 4809cfff
+ * MMC/SDIO L4 4809d000 - 4809dfff
+ * MS_PRO Mod 4809e000 - 4809efff
+ * MS_PRO L4 4809f000 - 4809ffff
+ * RNG Mod 480a0000 - 480a0fff
+ * RNG L4 480a1000 - 480a1fff
+ * DES3DES Mod 480a2000 - 480a2fff
+ * DES3DES L4 480a3000 - 480a3fff
+ * SHA1MD5 Mod 480a4000 - 480a4fff
+ * SHA1MD5 L4 480a5000 - 480a5fff
+ * AES Mod 480a6000 - 480a6fff
+ * AES L4 480a7000 - 480a7fff
+ * PKA Mod 480a8000 - 480a9fff
+ * PKA L4 480aa000 - 480aafff
+ * MG Mod 480b0000 - 480b0fff
+ * MG L4 480b1000 - 480b1fff
+ * HDQ/1-wire Mod 480b2000 - 480b2fff
+ * HDQ/1-wire L4 480b3000 - 480b3fff
+ * MPU interrupt 480fe000 - 480fefff
+ * STI channel base 54000000 - 5400ffff
+ * IVA RAM 5c000000 - 5c01ffff
+ * IVA ROM 5c020000 - 5c027fff
+ * IMG_BUF_A 5c040000 - 5c040fff
+ * IMG_BUF_B 5c042000 - 5c042fff
+ * VLCDS 5c048000 - 5c0487ff
+ * IMX_COEF 5c049000 - 5c04afff
+ * IMX_CMD 5c051000 - 5c051fff
+ * VLCDQ 5c053000 - 5c0533ff
+ * VLCDH 5c054000 - 5c054fff
+ * SEQ_CMD 5c055000 - 5c055fff
+ * IMX_REG 5c056000 - 5c0560ff
+ * VLCD_REG 5c056100 - 5c0561ff
+ * SEQ_REG 5c056200 - 5c0562ff
+ * IMG_BUF_REG 5c056300 - 5c0563ff
+ * SEQIRQ_REG 5c056400 - 5c0564ff
+ * OCP_REG 5c060000 - 5c060fff
+ * SYSC_REG 5c070000 - 5c070fff
+ * MMU_REG 5d000000 - 5d000fff
+ * sDMA R 68000400 - 680005ff
+ * sDMA W 68000600 - 680007ff
+ * Display Control 68000800 - 680009ff
+ * DSP subsystem 68000a00 - 68000bff
+ * MPU subsystem 68000c00 - 68000dff
+ * IVA subsystem 68001000 - 680011ff
+ * USB 68001200 - 680013ff
+ * Camera 68001400 - 680015ff
+ * VLYNQ (firewall) 68001800 - 68001bff
+ * VLYNQ 68001e00 - 68001fff
+ * SSI 68002000 - 680021ff
+ * L4 68002400 - 680025ff
+ * DSP (firewall) 68002800 - 68002bff
+ * DSP subsystem 68002e00 - 68002fff
+ * IVA (firewall) 68003000 - 680033ff
+ * IVA 68003600 - 680037ff
+ * GFX 68003a00 - 68003bff
+ * CMDWR emulation 68003c00 - 68003dff
+ * SMS 68004000 - 680041ff
+ * OCM 68004200 - 680043ff
+ * GPMC 68004400 - 680045ff
+ * RAM (firewall) 68005000 - 680053ff
+ * RAM (err login) 68005400 - 680057ff
+ * ROM (firewall) 68005800 - 68005bff
+ * ROM (err login) 68005c00 - 68005fff
+ * GPMC (firewall) 68006000 - 680063ff
+ * GPMC (err login) 68006400 - 680067ff
+ * SMS (err login) 68006c00 - 68006fff
+ * SMS registers 68008000 - 68008fff
+ * SDRC registers 68009000 - 68009fff
+ * GPMC registers 6800a000 6800afff
+ */
+
+ qemu_register_reset(omap2_mpu_reset, s);
+
+ return s;
+}
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..8223600
--- /dev/null
+++ b/hw/arm/pic_cpu.c
@@ -0,0 +1,66 @@
+/*
+ * 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"
+#include "sysemu/kvm.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);
+ }
+}
+
+static void kvm_arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+#ifdef CONFIG_KVM
+ ARMCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT;
+
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_IRQ;
+ break;
+ case ARM_PIC_CPU_FIQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_FIQ;
+ break;
+ default:
+ hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
+ }
+ kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT;
+ kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0);
+#endif
+}
+
+qemu_irq *arm_pic_init_cpu(ARMCPU *cpu)
+{
+ if (kvm_enabled()) {
+ return qemu_allocate_irqs(kvm_arm_pic_cpu_handler, cpu, 2);
+ }
+ return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2);
+}
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
new file mode 100644
index 0000000..c0f50c9
--- /dev/null
+++ b/hw/arm/pxa2xx.c
@@ -0,0 +1,2291 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/pxa.h"
+#include "sysemu/sysemu.h"
+#include "hw/serial.h"
+#include "hw/i2c.h"
+#include "hw/ssi.h"
+#include "char/char.h"
+#include "sysemu/blockdev.h"
+
+static struct {
+ hwaddr io_base;
+ int irqn;
+} pxa255_serial[] = {
+ { 0x40100000, PXA2XX_PIC_FFUART },
+ { 0x40200000, PXA2XX_PIC_BTUART },
+ { 0x40700000, PXA2XX_PIC_STUART },
+ { 0x41600000, PXA25X_PIC_HWUART },
+ { 0, 0 }
+}, pxa270_serial[] = {
+ { 0x40100000, PXA2XX_PIC_FFUART },
+ { 0x40200000, PXA2XX_PIC_BTUART },
+ { 0x40700000, PXA2XX_PIC_STUART },
+ { 0, 0 }
+};
+
+typedef struct PXASSPDef {
+ hwaddr io_base;
+ int irqn;
+} PXASSPDef;
+
+#if 0
+static PXASSPDef pxa250_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0, 0 }
+};
+#endif
+
+static PXASSPDef pxa255_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41400000, PXA25X_PIC_NSSP },
+ { 0, 0 }
+};
+
+#if 0
+static PXASSPDef pxa26x_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41400000, PXA25X_PIC_NSSP },
+ { 0x41500000, PXA26X_PIC_ASSP },
+ { 0, 0 }
+};
+#endif
+
+static PXASSPDef pxa27x_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41700000, PXA27X_PIC_SSP2 },
+ { 0x41900000, PXA2XX_PIC_SSP3 },
+ { 0, 0 }
+};
+
+#define PMCR 0x00 /* Power Manager Control register */
+#define PSSR 0x04 /* Power Manager Sleep Status register */
+#define PSPR 0x08 /* Power Manager Scratch-Pad register */
+#define PWER 0x0c /* Power Manager Wake-Up Enable register */
+#define PRER 0x10 /* Power Manager Rising-Edge Detect Enable register */
+#define PFER 0x14 /* Power Manager Falling-Edge Detect Enable register */
+#define PEDR 0x18 /* Power Manager Edge-Detect Status register */
+#define PCFR 0x1c /* Power Manager General Configuration register */
+#define PGSR0 0x20 /* Power Manager GPIO Sleep-State register 0 */
+#define PGSR1 0x24 /* Power Manager GPIO Sleep-State register 1 */
+#define PGSR2 0x28 /* Power Manager GPIO Sleep-State register 2 */
+#define PGSR3 0x2c /* Power Manager GPIO Sleep-State register 3 */
+#define RCSR 0x30 /* Reset Controller Status register */
+#define PSLR 0x34 /* Power Manager Sleep Configuration register */
+#define PTSR 0x38 /* Power Manager Standby Configuration register */
+#define PVCR 0x40 /* Power Manager Voltage Change Control register */
+#define PUCR 0x4c /* Power Manager USIM Card Control/Status register */
+#define PKWR 0x50 /* Power Manager Keyboard Wake-Up Enable register */
+#define PKSR 0x54 /* Power Manager Keyboard Level-Detect Status */
+#define PCMD0 0x80 /* Power Manager I2C Command register File 0 */
+#define PCMD31 0xfc /* Power Manager I2C Command register File 31 */
+
+static uint64_t pxa2xx_pm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case PMCR ... PCMD31:
+ if (addr & 3)
+ goto fail;
+
+ return s->pm_regs[addr >> 2];
+ default:
+ fail:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_pm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case PMCR:
+ /* Clear the write-one-to-clear bits... */
+ s->pm_regs[addr >> 2] &= ~(value & 0x2a);
+ /* ...and set the plain r/w bits */
+ s->pm_regs[addr >> 2] &= ~0x15;
+ s->pm_regs[addr >> 2] |= value & 0x15;
+ break;
+
+ case PSSR: /* Read-clean registers */
+ case RCSR:
+ case PKSR:
+ s->pm_regs[addr >> 2] &= ~value;
+ break;
+
+ default: /* Read-write registers */
+ if (!(addr & 3)) {
+ s->pm_regs[addr >> 2] = value;
+ break;
+ }
+
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps pxa2xx_pm_ops = {
+ .read = pxa2xx_pm_read,
+ .write = pxa2xx_pm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_pm = {
+ .name = "pxa2xx_pm",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(pm_regs, PXA2xxState, 0x40),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define CCCR 0x00 /* Core Clock Configuration register */
+#define CKEN 0x04 /* Clock Enable register */
+#define OSCC 0x08 /* Oscillator Configuration register */
+#define CCSR 0x0c /* Core Clock Status register */
+
+static uint64_t pxa2xx_cm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case CCCR:
+ case CKEN:
+ case OSCC:
+ return s->cm_regs[addr >> 2];
+
+ case CCSR:
+ return s->cm_regs[CCCR >> 2] | (3 << 28);
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_cm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case CCCR:
+ case CKEN:
+ s->cm_regs[addr >> 2] = value;
+ break;
+
+ case OSCC:
+ s->cm_regs[addr >> 2] &= ~0x6c;
+ s->cm_regs[addr >> 2] |= value & 0x6e;
+ if ((value >> 1) & 1) /* OON */
+ s->cm_regs[addr >> 2] |= 1 << 0; /* Oscillator is now stable */
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps pxa2xx_cm_ops = {
+ .read = pxa2xx_cm_read,
+ .write = pxa2xx_cm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_cm = {
+ .name = "pxa2xx_cm",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(cm_regs, PXA2xxState, 4),
+ VMSTATE_UINT32(clkcfg, PXA2xxState),
+ VMSTATE_UINT32(pmnc, PXA2xxState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ *value = s->clkcfg;
+ return 0;
+}
+
+static int pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ s->clkcfg = value & 0xf;
+ if (value & 2) {
+ printf("%s: CPU frequency change attempt\n", __func__);
+ }
+ return 0;
+}
+
+static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ static const char *pwrmode[8] = {
+ "Normal", "Idle", "Deep-idle", "Standby",
+ "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep",
+ };
+
+ if (value & 8) {
+ printf("%s: CPU voltage change attempt\n", __func__);
+ }
+ switch (value & 7) {
+ case 0:
+ /* Do nothing */
+ break;
+
+ case 1:
+ /* Idle */
+ if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT);
+ break;
+ }
+ /* Fall through. */
+
+ case 2:
+ /* Deep-Idle */
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT);
+ s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
+ goto message;
+
+ case 3:
+ s->cpu->env.uncached_cpsr =
+ ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
+ s->cpu->env.cp15.c1_sys = 0;
+ s->cpu->env.cp15.c1_coproc = 0;
+ s->cpu->env.cp15.c2_base0 = 0;
+ s->cpu->env.cp15.c3 = 0;
+ s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
+ s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
+
+ /*
+ * The scratch-pad register is almost universally used
+ * for storing the return address on suspend. For the
+ * lack of a resuming bootloader, perform a jump
+ * directly to that address.
+ */
+ memset(s->cpu->env.regs, 0, 4 * 15);
+ s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2];
+
+#if 0
+ buffer = 0xe59ff000; /* ldr pc, [pc, #0] */
+ cpu_physical_memory_write(0, &buffer, 4);
+ buffer = s->pm_regs[PSPR >> 2];
+ cpu_physical_memory_write(8, &buffer, 4);
+#endif
+
+ /* Suspend */
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
+
+ goto message;
+
+ default:
+ message:
+ printf("%s: machine entered %s mode\n", __func__,
+ pwrmode[value & 7]);
+ }
+
+ return 0;
+}
+
+static int pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ *value = s->pmnc;
+ return 0;
+}
+
+static int pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ s->pmnc = value;
+ return 0;
+}
+
+static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ PXA2xxState *s = (PXA2xxState *)ri->opaque;
+ if (s->pmnc & 1) {
+ *value = qemu_get_clock_ns(vm_clock);
+ } else {
+ *value = 0;
+ }
+ return 0;
+}
+
+static const ARMCPRegInfo pxa_cp_reginfo[] = {
+ /* cp14 crm==1: perf registers */
+ { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write },
+ { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore },
+ { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "CPFLAG", .cp = 14, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "CPEVTSEL", .cp = 14, .crn = 8, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ /* cp14 crm==2: performance count registers */
+ { .name = "CPPMN0", .cp = 14, .crn = 0, .crm = 2, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "CPPMN1", .cp = 14, .crn = 1, .crm = 2, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "CPPMN3", .cp = 14, .crn = 2, .crm = 3, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ /* cp14 crn==6: CLKCFG */
+ { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write },
+ /* cp14 crn==7: PWRMODE */
+ { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write },
+ REGINFO_SENTINEL
+};
+
+static void pxa2xx_setup_cp14(PXA2xxState *s)
+{
+ define_arm_cp_regs_with_opaque(s->cpu, pxa_cp_reginfo, s);
+}
+
+#define MDCNFG 0x00 /* SDRAM Configuration register */
+#define MDREFR 0x04 /* SDRAM Refresh Control register */
+#define MSC0 0x08 /* Static Memory Control register 0 */
+#define MSC1 0x0c /* Static Memory Control register 1 */
+#define MSC2 0x10 /* Static Memory Control register 2 */
+#define MECR 0x14 /* Expansion Memory Bus Config register */
+#define SXCNFG 0x1c /* Synchronous Static Memory Config register */
+#define MCMEM0 0x28 /* PC Card Memory Socket 0 Timing register */
+#define MCMEM1 0x2c /* PC Card Memory Socket 1 Timing register */
+#define MCATT0 0x30 /* PC Card Attribute Socket 0 register */
+#define MCATT1 0x34 /* PC Card Attribute Socket 1 register */
+#define MCIO0 0x38 /* PC Card I/O Socket 0 Timing register */
+#define MCIO1 0x3c /* PC Card I/O Socket 1 Timing register */
+#define MDMRS 0x40 /* SDRAM Mode Register Set Config register */
+#define BOOT_DEF 0x44 /* Boot-time Default Configuration register */
+#define ARB_CNTL 0x48 /* Arbiter Control register */
+#define BSCNTR0 0x4c /* Memory Buffer Strength Control register 0 */
+#define BSCNTR1 0x50 /* Memory Buffer Strength Control register 1 */
+#define LCDBSCNTR 0x54 /* LCD Buffer Strength Control register */
+#define MDMRSLP 0x58 /* Low Power SDRAM Mode Set Config register */
+#define BSCNTR2 0x5c /* Memory Buffer Strength Control register 2 */
+#define BSCNTR3 0x60 /* Memory Buffer Strength Control register 3 */
+#define SA1110 0x64 /* SA-1110 Memory Compatibility register */
+
+static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case MDCNFG ... SA1110:
+ if ((addr & 3) == 0)
+ return s->mm_regs[addr >> 2];
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_mm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case MDCNFG ... SA1110:
+ if ((addr & 3) == 0) {
+ s->mm_regs[addr >> 2] = value;
+ break;
+ }
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps pxa2xx_mm_ops = {
+ .read = pxa2xx_mm_read,
+ .write = pxa2xx_mm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_mm = {
+ .name = "pxa2xx_mm",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mm_regs, PXA2xxState, 0x1a),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* Synchronous Serial Ports */
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+ int enable;
+ SSIBus *bus;
+
+ uint32_t sscr[2];
+ uint32_t sspsp;
+ uint32_t ssto;
+ uint32_t ssitr;
+ uint32_t sssr;
+ uint8_t sstsa;
+ uint8_t ssrsa;
+ uint8_t ssacd;
+
+ uint32_t rx_fifo[16];
+ int rx_level;
+ int rx_start;
+} PXA2xxSSPState;
+
+#define SSCR0 0x00 /* SSP Control register 0 */
+#define SSCR1 0x04 /* SSP Control register 1 */
+#define SSSR 0x08 /* SSP Status register */
+#define SSITR 0x0c /* SSP Interrupt Test register */
+#define SSDR 0x10 /* SSP Data register */
+#define SSTO 0x28 /* SSP Time-Out register */
+#define SSPSP 0x2c /* SSP Programmable Serial Protocol register */
+#define SSTSA 0x30 /* SSP TX Time Slot Active register */
+#define SSRSA 0x34 /* SSP RX Time Slot Active register */
+#define SSTSS 0x38 /* SSP Time Slot Status register */
+#define SSACD 0x3c /* SSP Audio Clock Divider register */
+
+/* Bitfields for above registers */
+#define SSCR0_SPI(x) (((x) & 0x30) == 0x00)
+#define SSCR0_SSP(x) (((x) & 0x30) == 0x10)
+#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20)
+#define SSCR0_PSP(x) (((x) & 0x30) == 0x30)
+#define SSCR0_SSE (1 << 7)
+#define SSCR0_RIM (1 << 22)
+#define SSCR0_TIM (1 << 23)
+#define SSCR0_MOD (1 << 31)
+#define SSCR0_DSS(x) (((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1)
+#define SSCR1_RIE (1 << 0)
+#define SSCR1_TIE (1 << 1)
+#define SSCR1_LBM (1 << 2)
+#define SSCR1_MWDS (1 << 5)
+#define SSCR1_TFT(x) ((((x) >> 6) & 0xf) + 1)
+#define SSCR1_RFT(x) ((((x) >> 10) & 0xf) + 1)
+#define SSCR1_EFWR (1 << 14)
+#define SSCR1_PINTE (1 << 18)
+#define SSCR1_TINTE (1 << 19)
+#define SSCR1_RSRE (1 << 20)
+#define SSCR1_TSRE (1 << 21)
+#define SSCR1_EBCEI (1 << 29)
+#define SSITR_INT (7 << 5)
+#define SSSR_TNF (1 << 2)
+#define SSSR_RNE (1 << 3)
+#define SSSR_TFS (1 << 5)
+#define SSSR_RFS (1 << 6)
+#define SSSR_ROR (1 << 7)
+#define SSSR_PINT (1 << 18)
+#define SSSR_TINT (1 << 19)
+#define SSSR_EOC (1 << 20)
+#define SSSR_TUR (1 << 21)
+#define SSSR_BCE (1 << 23)
+#define SSSR_RW 0x00bc0080
+
+static void pxa2xx_ssp_int_update(PXA2xxSSPState *s)
+{
+ int level = 0;
+
+ level |= s->ssitr & SSITR_INT;
+ level |= (s->sssr & SSSR_BCE) && (s->sscr[1] & SSCR1_EBCEI);
+ level |= (s->sssr & SSSR_TUR) && !(s->sscr[0] & SSCR0_TIM);
+ level |= (s->sssr & SSSR_EOC) && (s->sssr & (SSSR_TINT | SSSR_PINT));
+ level |= (s->sssr & SSSR_TINT) && (s->sscr[1] & SSCR1_TINTE);
+ level |= (s->sssr & SSSR_PINT) && (s->sscr[1] & SSCR1_PINTE);
+ level |= (s->sssr & SSSR_ROR) && !(s->sscr[0] & SSCR0_RIM);
+ level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE);
+ level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE);
+ qemu_set_irq(s->irq, !!level);
+}
+
+static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s)
+{
+ s->sssr &= ~(0xf << 12); /* Clear RFL */
+ s->sssr &= ~(0xf << 8); /* Clear TFL */
+ s->sssr &= ~SSSR_TFS;
+ s->sssr &= ~SSSR_TNF;
+ if (s->enable) {
+ s->sssr |= ((s->rx_level - 1) & 0xf) << 12;
+ if (s->rx_level >= SSCR1_RFT(s->sscr[1]))
+ s->sssr |= SSSR_RFS;
+ else
+ s->sssr &= ~SSSR_RFS;
+ if (s->rx_level)
+ s->sssr |= SSSR_RNE;
+ else
+ s->sssr &= ~SSSR_RNE;
+ /* TX FIFO is never filled, so it is always in underrun
+ condition if SSP is enabled */
+ s->sssr |= SSSR_TFS;
+ s->sssr |= SSSR_TNF;
+ }
+
+ pxa2xx_ssp_int_update(s);
+}
+
+static uint64_t pxa2xx_ssp_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ uint32_t retval;
+
+ switch (addr) {
+ case SSCR0:
+ return s->sscr[0];
+ case SSCR1:
+ return s->sscr[1];
+ case SSPSP:
+ return s->sspsp;
+ case SSTO:
+ return s->ssto;
+ case SSITR:
+ return s->ssitr;
+ case SSSR:
+ return s->sssr | s->ssitr;
+ case SSDR:
+ if (!s->enable)
+ return 0xffffffff;
+ if (s->rx_level < 1) {
+ printf("%s: SSP Rx Underrun\n", __FUNCTION__);
+ return 0xffffffff;
+ }
+ s->rx_level --;
+ retval = s->rx_fifo[s->rx_start ++];
+ s->rx_start &= 0xf;
+ pxa2xx_ssp_fifo_update(s);
+ return retval;
+ case SSTSA:
+ return s->sstsa;
+ case SSRSA:
+ return s->ssrsa;
+ case SSTSS:
+ return 0;
+ case SSACD:
+ return s->ssacd;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_ssp_write(void *opaque, hwaddr addr,
+ uint64_t value64, unsigned size)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ uint32_t value = value64;
+
+ switch (addr) {
+ case SSCR0:
+ s->sscr[0] = value & 0xc7ffffff;
+ s->enable = value & SSCR0_SSE;
+ if (value & SSCR0_MOD)
+ printf("%s: Attempt to use network mode\n", __FUNCTION__);
+ if (s->enable && SSCR0_DSS(value) < 4)
+ printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
+ SSCR0_DSS(value));
+ if (!(value & SSCR0_SSE)) {
+ s->sssr = 0;
+ s->ssitr = 0;
+ s->rx_level = 0;
+ }
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSCR1:
+ s->sscr[1] = value;
+ if (value & (SSCR1_LBM | SSCR1_EFWR))
+ printf("%s: Attempt to use SSP test mode\n", __FUNCTION__);
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSPSP:
+ s->sspsp = value;
+ break;
+
+ case SSTO:
+ s->ssto = value;
+ break;
+
+ case SSITR:
+ s->ssitr = value & SSITR_INT;
+ pxa2xx_ssp_int_update(s);
+ break;
+
+ case SSSR:
+ s->sssr &= ~(value & SSSR_RW);
+ pxa2xx_ssp_int_update(s);
+ break;
+
+ case SSDR:
+ if (SSCR0_UWIRE(s->sscr[0])) {
+ if (s->sscr[1] & SSCR1_MWDS)
+ value &= 0xffff;
+ else
+ value &= 0xff;
+ } else
+ /* Note how 32bits overflow does no harm here */
+ value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
+
+ /* Data goes from here to the Tx FIFO and is shifted out from
+ * there directly to the slave, no need to buffer it.
+ */
+ if (s->enable) {
+ uint32_t readval;
+ readval = ssi_transfer(s->bus, value);
+ if (s->rx_level < 0x10) {
+ s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = readval;
+ } else {
+ s->sssr |= SSSR_ROR;
+ }
+ }
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSTSA:
+ s->sstsa = value;
+ break;
+
+ case SSRSA:
+ s->ssrsa = value;
+ break;
+
+ case SSACD:
+ s->ssacd = value;
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps pxa2xx_ssp_ops = {
+ .read = pxa2xx_ssp_read,
+ .write = pxa2xx_ssp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->enable);
+
+ qemu_put_be32s(f, &s->sscr[0]);
+ qemu_put_be32s(f, &s->sscr[1]);
+ qemu_put_be32s(f, &s->sspsp);
+ qemu_put_be32s(f, &s->ssto);
+ qemu_put_be32s(f, &s->ssitr);
+ qemu_put_be32s(f, &s->sssr);
+ qemu_put_8s(f, &s->sstsa);
+ qemu_put_8s(f, &s->ssrsa);
+ qemu_put_8s(f, &s->ssacd);
+
+ qemu_put_byte(f, s->rx_level);
+ for (i = 0; i < s->rx_level; i ++)
+ qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
+}
+
+static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ int i;
+
+ s->enable = qemu_get_be32(f);
+
+ qemu_get_be32s(f, &s->sscr[0]);
+ qemu_get_be32s(f, &s->sscr[1]);
+ qemu_get_be32s(f, &s->sspsp);
+ qemu_get_be32s(f, &s->ssto);
+ qemu_get_be32s(f, &s->ssitr);
+ qemu_get_be32s(f, &s->sssr);
+ qemu_get_8s(f, &s->sstsa);
+ qemu_get_8s(f, &s->ssrsa);
+ qemu_get_8s(f, &s->ssacd);
+
+ s->rx_level = qemu_get_byte(f);
+ s->rx_start = 0;
+ for (i = 0; i < s->rx_level; i ++)
+ s->rx_fifo[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static int pxa2xx_ssp_init(SysBusDevice *dev)
+{
+ PXA2xxSSPState *s = FROM_SYSBUS(PXA2xxSSPState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ memory_region_init_io(&s->iomem, &pxa2xx_ssp_ops, s, "pxa2xx-ssp", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ register_savevm(&dev->qdev, "pxa2xx_ssp", -1, 0,
+ pxa2xx_ssp_save, pxa2xx_ssp_load, s);
+
+ s->bus = ssi_create_bus(&dev->qdev, "ssi");
+ return 0;
+}
+
+/* Real-Time Clock */
+#define RCNR 0x00 /* RTC Counter register */
+#define RTAR 0x04 /* RTC Alarm register */
+#define RTSR 0x08 /* RTC Status register */
+#define RTTR 0x0c /* RTC Timer Trim register */
+#define RDCR 0x10 /* RTC Day Counter register */
+#define RYCR 0x14 /* RTC Year Counter register */
+#define RDAR1 0x18 /* RTC Wristwatch Day Alarm register 1 */
+#define RYAR1 0x1c /* RTC Wristwatch Year Alarm register 1 */
+#define RDAR2 0x20 /* RTC Wristwatch Day Alarm register 2 */
+#define RYAR2 0x24 /* RTC Wristwatch Year Alarm register 2 */
+#define SWCR 0x28 /* RTC Stopwatch Counter register */
+#define SWAR1 0x2c /* RTC Stopwatch Alarm register 1 */
+#define SWAR2 0x30 /* RTC Stopwatch Alarm register 2 */
+#define RTCPICR 0x34 /* RTC Periodic Interrupt Counter register */
+#define PIAR 0x38 /* RTC Periodic Interrupt Alarm register */
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t rdar1;
+ uint32_t rdar2;
+ uint32_t ryar1;
+ uint32_t ryar2;
+ uint32_t swar1;
+ uint32_t swar2;
+ uint32_t piar;
+ uint32_t last_rcnr;
+ uint32_t last_rdcr;
+ uint32_t last_rycr;
+ uint32_t last_swcr;
+ uint32_t last_rtcpicr;
+ int64_t last_hz;
+ int64_t last_sw;
+ int64_t last_pi;
+ QEMUTimer *rtc_hz;
+ QEMUTimer *rtc_rdal1;
+ QEMUTimer *rtc_rdal2;
+ QEMUTimer *rtc_swal1;
+ QEMUTimer *rtc_swal2;
+ QEMUTimer *rtc_pi;
+ qemu_irq rtc_irq;
+} PXA2xxRTCState;
+
+static inline void pxa2xx_rtc_int_update(PXA2xxRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, !!(s->rtsr & 0x2553));
+}
+
+static void pxa2xx_rtc_hzupdate(PXA2xxRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rtc_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_rdcr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static void pxa2xx_rtc_swupdate(PXA2xxRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rtc_clock);
+ if (s->rtsr & (1 << 12))
+ s->last_swcr += (rt - s->last_sw) / 10;
+ s->last_sw = rt;
+}
+
+static void pxa2xx_rtc_piupdate(PXA2xxRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rtc_clock);
+ if (s->rtsr & (1 << 15))
+ s->last_swcr += rt - s->last_pi;
+ s->last_pi = rt;
+}
+
+static inline void pxa2xx_rtc_alarm_update(PXA2xxRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0)))
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ else
+ qemu_del_timer(s->rtc_hz);
+
+ if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4)))
+ qemu_mod_timer(s->rtc_rdal1, s->last_hz +
+ (((s->rdar1 - s->last_rdcr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_rdal1);
+
+ if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6)))
+ qemu_mod_timer(s->rtc_rdal2, s->last_hz +
+ (((s->rdar2 - s->last_rdcr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_rdal2);
+
+ if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8)))
+ qemu_mod_timer(s->rtc_swal1, s->last_sw +
+ (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_swal1);
+
+ if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10)))
+ qemu_mod_timer(s->rtc_swal2, s->last_sw +
+ (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_swal2);
+
+ if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13)))
+ qemu_mod_timer(s->rtc_pi, s->last_pi +
+ (s->piar & 0xffff) - s->last_rtcpicr);
+ else
+ qemu_del_timer(s->rtc_pi);
+}
+
+static inline void pxa2xx_rtc_hz_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal1_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 4);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal2_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 6);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal1_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 8);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal2_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 10);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_pi_tick(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ s->rtsr |= (1 << 13);
+ pxa2xx_rtc_piupdate(s);
+ s->last_rtcpicr = 0;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static uint64_t pxa2xx_rtc_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RDAR1:
+ return s->rdar1;
+ case RDAR2:
+ return s->rdar2;
+ case RYAR1:
+ return s->ryar1;
+ case RYAR2:
+ return s->ryar2;
+ case SWAR1:
+ return s->swar1;
+ case SWAR2:
+ return s->swar2;
+ case PIAR:
+ return s->piar;
+ case RCNR:
+ return s->last_rcnr + ((qemu_get_clock_ms(rtc_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ case RDCR:
+ return s->last_rdcr + ((qemu_get_clock_ms(rtc_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ case RYCR:
+ return s->last_rycr;
+ case SWCR:
+ if (s->rtsr & (1 << 12))
+ return s->last_swcr + (qemu_get_clock_ms(rtc_clock) - s->last_sw) / 10;
+ else
+ return s->last_swcr;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_rtc_write(void *opaque, hwaddr addr,
+ uint64_t value64, unsigned size)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+ uint32_t value = value64;
+
+ switch (addr) {
+ case RTTR:
+ if (!(s->rttr & (1 << 31))) {
+ pxa2xx_rtc_hzupdate(s);
+ s->rttr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ }
+ break;
+
+ case RTSR:
+ if ((s->rtsr ^ value) & (1 << 15))
+ pxa2xx_rtc_piupdate(s);
+
+ if ((s->rtsr ^ value) & (1 << 12))
+ pxa2xx_rtc_swupdate(s);
+
+ if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac))
+ pxa2xx_rtc_alarm_update(s, value);
+
+ s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac));
+ pxa2xx_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDAR1:
+ s->rdar1 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDAR2:
+ s->rdar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYAR1:
+ s->ryar1 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYAR2:
+ s->ryar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case SWAR1:
+ pxa2xx_rtc_swupdate(s);
+ s->swar1 = value;
+ s->last_swcr = 0;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case SWAR2:
+ s->swar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case PIAR:
+ s->piar = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ pxa2xx_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDCR:
+ pxa2xx_rtc_hzupdate(s);
+ s->last_rdcr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYCR:
+ s->last_rycr = value;
+ break;
+
+ case SWCR:
+ pxa2xx_rtc_swupdate(s);
+ s->last_swcr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RTCPICR:
+ pxa2xx_rtc_piupdate(s);
+ s->last_rtcpicr = value & 0xffff;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_rtc_ops = {
+ .read = pxa2xx_rtc_read,
+ .write = pxa2xx_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pxa2xx_rtc_init(SysBusDevice *dev)
+{
+ PXA2xxRTCState *s = FROM_SYSBUS(PXA2xxRTCState, dev);
+ struct tm tm;
+ int wom;
+
+ s->rttr = 0x7fff;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+ wom = ((tm.tm_mday - 1) / 7) + 1;
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_rdcr = (wom << 20) | ((tm.tm_wday + 1) << 17) |
+ (tm.tm_hour << 12) | (tm.tm_min << 6) | tm.tm_sec;
+ s->last_rycr = ((tm.tm_year + 1900) << 9) |
+ ((tm.tm_mon + 1) << 5) | tm.tm_mday;
+ s->last_swcr = (tm.tm_hour << 19) |
+ (tm.tm_min << 13) | (tm.tm_sec << 7);
+ s->last_rtcpicr = 0;
+ s->last_hz = s->last_sw = s->last_pi = qemu_get_clock_ms(rtc_clock);
+
+ s->rtc_hz = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_hz_tick, s);
+ s->rtc_rdal1 = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_rdal1_tick, s);
+ s->rtc_rdal2 = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_rdal2_tick, s);
+ s->rtc_swal1 = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_swal1_tick, s);
+ s->rtc_swal2 = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_swal2_tick, s);
+ s->rtc_pi = qemu_new_timer_ms(rtc_clock, pxa2xx_rtc_pi_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+
+ memory_region_init_io(&s->iomem, &pxa2xx_rtc_ops, s, "pxa2xx-rtc", 0x10000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void pxa2xx_rtc_pre_save(void *opaque)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+
+ pxa2xx_rtc_hzupdate(s);
+ pxa2xx_rtc_piupdate(s);
+ pxa2xx_rtc_swupdate(s);
+}
+
+static int pxa2xx_rtc_post_load(void *opaque, int version_id)
+{
+ PXA2xxRTCState *s = (PXA2xxRTCState *) opaque;
+
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_pxa2xx_rtc_regs = {
+ .name = "pxa2xx_rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = pxa2xx_rtc_pre_save,
+ .post_load = pxa2xx_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, PXA2xxRTCState),
+ VMSTATE_UINT32(rtsr, PXA2xxRTCState),
+ VMSTATE_UINT32(rtar, PXA2xxRTCState),
+ VMSTATE_UINT32(rdar1, PXA2xxRTCState),
+ VMSTATE_UINT32(rdar2, PXA2xxRTCState),
+ VMSTATE_UINT32(ryar1, PXA2xxRTCState),
+ VMSTATE_UINT32(ryar2, PXA2xxRTCState),
+ VMSTATE_UINT32(swar1, PXA2xxRTCState),
+ VMSTATE_UINT32(swar2, PXA2xxRTCState),
+ VMSTATE_UINT32(piar, PXA2xxRTCState),
+ VMSTATE_UINT32(last_rcnr, PXA2xxRTCState),
+ VMSTATE_UINT32(last_rdcr, PXA2xxRTCState),
+ VMSTATE_UINT32(last_rycr, PXA2xxRTCState),
+ VMSTATE_UINT32(last_swcr, PXA2xxRTCState),
+ VMSTATE_UINT32(last_rtcpicr, PXA2xxRTCState),
+ VMSTATE_INT64(last_hz, PXA2xxRTCState),
+ VMSTATE_INT64(last_sw, PXA2xxRTCState),
+ VMSTATE_INT64(last_pi, PXA2xxRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_rtc_init;
+ dc->desc = "PXA2xx RTC Controller";
+ dc->vmsd = &vmstate_pxa2xx_rtc_regs;
+}
+
+static const TypeInfo pxa2xx_rtc_sysbus_info = {
+ .name = "pxa2xx_rtc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxRTCState),
+ .class_init = pxa2xx_rtc_sysbus_class_init,
+};
+
+/* I2C Interface */
+typedef struct {
+ I2CSlave i2c;
+ PXA2xxI2CState *host;
+} PXA2xxI2CSlaveState;
+
+struct PXA2xxI2CState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ PXA2xxI2CSlaveState *slave;
+ i2c_bus *bus;
+ qemu_irq irq;
+ uint32_t offset;
+ uint32_t region_size;
+
+ uint16_t control;
+ uint16_t status;
+ uint8_t ibmr;
+ uint8_t data;
+};
+
+#define IBMR 0x80 /* I2C Bus Monitor register */
+#define IDBR 0x88 /* I2C Data Buffer register */
+#define ICR 0x90 /* I2C Control register */
+#define ISR 0x98 /* I2C Status register */
+#define ISAR 0xa0 /* I2C Slave Address register */
+
+static void pxa2xx_i2c_update(PXA2xxI2CState *s)
+{
+ uint16_t level = 0;
+ level |= s->status & s->control & (1 << 10); /* BED */
+ level |= (s->status & (1 << 7)) && (s->control & (1 << 9)); /* IRF */
+ level |= (s->status & (1 << 6)) && (s->control & (1 << 8)); /* ITE */
+ level |= s->status & (1 << 9); /* SAD */
+ qemu_set_irq(s->irq, !!level);
+}
+
+/* These are only stubs now. */
+static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+
+ switch (event) {
+ case I2C_START_SEND:
+ s->status |= (1 << 9); /* set SAD */
+ s->status &= ~(1 << 0); /* clear RWM */
+ break;
+ case I2C_START_RECV:
+ s->status |= (1 << 9); /* set SAD */
+ s->status |= 1 << 0; /* set RWM */
+ break;
+ case I2C_FINISH:
+ s->status |= (1 << 4); /* set SSD */
+ break;
+ case I2C_NACK:
+ s->status |= 1 << 1; /* set ACKNAK */
+ break;
+ }
+ pxa2xx_i2c_update(s);
+}
+
+static int pxa2xx_i2c_rx(I2CSlave *i2c)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+ if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+ return 0;
+
+ if (s->status & (1 << 0)) { /* RWM */
+ s->status |= 1 << 6; /* set ITE */
+ }
+ pxa2xx_i2c_update(s);
+
+ return s->data;
+}
+
+static int pxa2xx_i2c_tx(I2CSlave *i2c, uint8_t data)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+ if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+ return 1;
+
+ if (!(s->status & (1 << 0))) { /* RWM */
+ s->status |= 1 << 7; /* set IRF */
+ s->data = data;
+ }
+ pxa2xx_i2c_update(s);
+
+ return 1;
+}
+
+static uint64_t pxa2xx_i2c_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
+
+ addr -= s->offset;
+ switch (addr) {
+ case ICR:
+ return s->control;
+ case ISR:
+ return s->status | (i2c_bus_busy(s->bus) << 2);
+ case ISAR:
+ return s->slave->i2c.address;
+ case IDBR:
+ return s->data;
+ case IBMR:
+ if (s->status & (1 << 2))
+ s->ibmr ^= 3; /* Fake SCL and SDA pin changes */
+ else
+ s->ibmr = 0;
+ return s->ibmr;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_i2c_write(void *opaque, hwaddr addr,
+ uint64_t value64, unsigned size)
+{
+ PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
+ uint32_t value = value64;
+ int ack;
+
+ addr -= s->offset;
+ switch (addr) {
+ case ICR:
+ s->control = value & 0xfff7;
+ if ((value & (1 << 3)) && (value & (1 << 6))) { /* TB and IUE */
+ /* TODO: slave mode */
+ if (value & (1 << 0)) { /* START condition */
+ if (s->data & 1)
+ s->status |= 1 << 0; /* set RWM */
+ else
+ s->status &= ~(1 << 0); /* clear RWM */
+ ack = !i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+ } else {
+ if (s->status & (1 << 0)) { /* RWM */
+ s->data = i2c_recv(s->bus);
+ if (value & (1 << 2)) /* ACKNAK */
+ i2c_nack(s->bus);
+ ack = 1;
+ } else
+ ack = !i2c_send(s->bus, s->data);
+ }
+
+ if (value & (1 << 1)) /* STOP condition */
+ i2c_end_transfer(s->bus);
+
+ if (ack) {
+ if (value & (1 << 0)) /* START condition */
+ s->status |= 1 << 6; /* set ITE */
+ else
+ if (s->status & (1 << 0)) /* RWM */
+ s->status |= 1 << 7; /* set IRF */
+ else
+ s->status |= 1 << 6; /* set ITE */
+ s->status &= ~(1 << 1); /* clear ACKNAK */
+ } else {
+ s->status |= 1 << 6; /* set ITE */
+ s->status |= 1 << 10; /* set BED */
+ s->status |= 1 << 1; /* set ACKNAK */
+ }
+ }
+ if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */
+ if (value & (1 << 4)) /* MA */
+ i2c_end_transfer(s->bus);
+ pxa2xx_i2c_update(s);
+ break;
+
+ case ISR:
+ s->status &= ~(value & 0x07f0);
+ pxa2xx_i2c_update(s);
+ break;
+
+ case ISAR:
+ i2c_set_slave_address(&s->slave->i2c, value & 0x7f);
+ break;
+
+ case IDBR:
+ s->data = value & 0xff;
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_i2c_ops = {
+ .read = pxa2xx_i2c_read,
+ .write = pxa2xx_i2c_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_i2c_slave = {
+ .name = "pxa2xx_i2c_slave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_I2C_SLAVE(i2c, PXA2xxI2CSlaveState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pxa2xx_i2c = {
+ .name = "pxa2xx_i2c",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(control, PXA2xxI2CState),
+ VMSTATE_UINT16(status, PXA2xxI2CState),
+ VMSTATE_UINT8(ibmr, PXA2xxI2CState),
+ VMSTATE_UINT8(data, PXA2xxI2CState),
+ VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState,
+ vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState *),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pxa2xx_i2c_slave_init(I2CSlave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static void pxa2xx_i2c_slave_class_init(ObjectClass *klass, void *data)
+{
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->init = pxa2xx_i2c_slave_init;
+ k->event = pxa2xx_i2c_event;
+ k->recv = pxa2xx_i2c_rx;
+ k->send = pxa2xx_i2c_tx;
+}
+
+static const TypeInfo pxa2xx_i2c_slave_info = {
+ .name = "pxa2xx-i2c-slave",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(PXA2xxI2CSlaveState),
+ .class_init = pxa2xx_i2c_slave_class_init,
+};
+
+PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base,
+ qemu_irq irq, uint32_t region_size)
+{
+ DeviceState *dev;
+ SysBusDevice *i2c_dev;
+ PXA2xxI2CState *s;
+
+ i2c_dev = SYS_BUS_DEVICE(qdev_create(NULL, "pxa2xx_i2c"));
+ qdev_prop_set_uint32(&i2c_dev->qdev, "size", region_size + 1);
+ qdev_prop_set_uint32(&i2c_dev->qdev, "offset", base & region_size);
+
+ qdev_init_nofail(&i2c_dev->qdev);
+
+ sysbus_mmio_map(i2c_dev, 0, base & ~region_size);
+ sysbus_connect_irq(i2c_dev, 0, irq);
+
+ s = FROM_SYSBUS(PXA2xxI2CState, i2c_dev);
+ /* FIXME: Should the slave device really be on a separate bus? */
+ dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0);
+ s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE(dev));
+ s->slave->host = s;
+
+ return s;
+}
+
+static int pxa2xx_i2c_initfn(SysBusDevice *dev)
+{
+ PXA2xxI2CState *s = FROM_SYSBUS(PXA2xxI2CState, dev);
+
+ s->bus = i2c_init_bus(&dev->qdev, "i2c");
+
+ memory_region_init_io(&s->iomem, &pxa2xx_i2c_ops, s,
+ "pxa2xx-i2x", s->region_size);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ return 0;
+}
+
+i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s)
+{
+ return s->bus;
+}
+
+static Property pxa2xx_i2c_properties[] = {
+ DEFINE_PROP_UINT32("size", PXA2xxI2CState, region_size, 0x10000),
+ DEFINE_PROP_UINT32("offset", PXA2xxI2CState, offset, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_i2c_initfn;
+ dc->desc = "PXA2xx I2C Bus Controller";
+ dc->vmsd = &vmstate_pxa2xx_i2c;
+ dc->props = pxa2xx_i2c_properties;
+}
+
+static const TypeInfo pxa2xx_i2c_info = {
+ .name = "pxa2xx_i2c",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxI2CState),
+ .class_init = pxa2xx_i2c_class_init,
+};
+
+/* PXA Inter-IC Sound Controller */
+static void pxa2xx_i2s_reset(PXA2xxI2SState *i2s)
+{
+ i2s->rx_len = 0;
+ i2s->tx_len = 0;
+ i2s->fifo_len = 0;
+ i2s->clk = 0x1a;
+ i2s->control[0] = 0x00;
+ i2s->control[1] = 0x00;
+ i2s->status = 0x00;
+ i2s->mask = 0x00;
+}
+
+#define SACR_TFTH(val) ((val >> 8) & 0xf)
+#define SACR_RFTH(val) ((val >> 12) & 0xf)
+#define SACR_DREC(val) (val & (1 << 3))
+#define SACR_DPRL(val) (val & (1 << 4))
+
+static inline void pxa2xx_i2s_update(PXA2xxI2SState *i2s)
+{
+ int rfs, tfs;
+ rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len &&
+ !SACR_DREC(i2s->control[1]);
+ tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) &&
+ i2s->enable && !SACR_DPRL(i2s->control[1]);
+
+ qemu_set_irq(i2s->rx_dma, rfs);
+ qemu_set_irq(i2s->tx_dma, tfs);
+
+ i2s->status &= 0xe0;
+ if (i2s->fifo_len < 16 || !i2s->enable)
+ i2s->status |= 1 << 0; /* TNF */
+ if (i2s->rx_len)
+ i2s->status |= 1 << 1; /* RNE */
+ if (i2s->enable)
+ i2s->status |= 1 << 2; /* BSY */
+ if (tfs)
+ i2s->status |= 1 << 3; /* TFS */
+ if (rfs)
+ i2s->status |= 1 << 4; /* RFS */
+ if (!(i2s->tx_len && i2s->enable))
+ i2s->status |= i2s->fifo_len << 8; /* TFL */
+ i2s->status |= MAX(i2s->rx_len, 0xf) << 12; /* RFL */
+
+ qemu_set_irq(i2s->irq, i2s->status & i2s->mask);
+}
+
+#define SACR0 0x00 /* Serial Audio Global Control register */
+#define SACR1 0x04 /* Serial Audio I2S/MSB-Justified Control register */
+#define SASR0 0x0c /* Serial Audio Interface and FIFO Status register */
+#define SAIMR 0x14 /* Serial Audio Interrupt Mask register */
+#define SAICR 0x18 /* Serial Audio Interrupt Clear register */
+#define SADIV 0x60 /* Serial Audio Clock Divider register */
+#define SADR 0x80 /* Serial Audio Data register */
+
+static uint64_t pxa2xx_i2s_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+
+ switch (addr) {
+ case SACR0:
+ return s->control[0];
+ case SACR1:
+ return s->control[1];
+ case SASR0:
+ return s->status;
+ case SAIMR:
+ return s->mask;
+ case SAICR:
+ return 0;
+ case SADIV:
+ return s->clk;
+ case SADR:
+ if (s->rx_len > 0) {
+ s->rx_len --;
+ pxa2xx_i2s_update(s);
+ return s->codec_in(s->opaque);
+ }
+ return 0;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_i2s_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+ uint32_t *sample;
+
+ switch (addr) {
+ case SACR0:
+ if (value & (1 << 3)) /* RST */
+ pxa2xx_i2s_reset(s);
+ s->control[0] = value & 0xff3d;
+ if (!s->enable && (value & 1) && s->tx_len) { /* ENB */
+ for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++)
+ s->codec_out(s->opaque, *sample);
+ s->status &= ~(1 << 7); /* I2SOFF */
+ }
+ if (value & (1 << 4)) /* EFWR */
+ printf("%s: Attempt to use special function\n", __FUNCTION__);
+ s->enable = (value & 9) == 1; /* ENB && !RST*/
+ pxa2xx_i2s_update(s);
+ break;
+ case SACR1:
+ s->control[1] = value & 0x0039;
+ if (value & (1 << 5)) /* ENLBF */
+ printf("%s: Attempt to use loopback function\n", __FUNCTION__);
+ if (value & (1 << 4)) /* DPRL */
+ s->fifo_len = 0;
+ pxa2xx_i2s_update(s);
+ break;
+ case SAIMR:
+ s->mask = value & 0x0078;
+ pxa2xx_i2s_update(s);
+ break;
+ case SAICR:
+ s->status &= ~(value & (3 << 5));
+ pxa2xx_i2s_update(s);
+ break;
+ case SADIV:
+ s->clk = value & 0x007f;
+ break;
+ case SADR:
+ if (s->tx_len && s->enable) {
+ s->tx_len --;
+ pxa2xx_i2s_update(s);
+ s->codec_out(s->opaque, value);
+ } else if (s->fifo_len < 16) {
+ s->fifo[s->fifo_len ++] = value;
+ pxa2xx_i2s_update(s);
+ }
+ break;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_i2s_ops = {
+ .read = pxa2xx_i2s_read,
+ .write = pxa2xx_i2s_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_i2s = {
+ .name = "pxa2xx_i2s",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(control, PXA2xxI2SState, 2),
+ VMSTATE_UINT32(status, PXA2xxI2SState),
+ VMSTATE_UINT32(mask, PXA2xxI2SState),
+ VMSTATE_UINT32(clk, PXA2xxI2SState),
+ VMSTATE_INT32(enable, PXA2xxI2SState),
+ VMSTATE_INT32(rx_len, PXA2xxI2SState),
+ VMSTATE_INT32(tx_len, PXA2xxI2SState),
+ VMSTATE_INT32(fifo_len, PXA2xxI2SState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+ uint32_t *sample;
+
+ /* Signal FIFO errors */
+ if (s->enable && s->tx_len)
+ s->status |= 1 << 5; /* TUR */
+ if (s->enable && s->rx_len)
+ s->status |= 1 << 6; /* ROR */
+
+ /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to
+ * handle the cases where it makes a difference. */
+ s->tx_len = tx - s->fifo_len;
+ s->rx_len = rx;
+ /* Note that is s->codec_out wasn't set, we wouldn't get called. */
+ if (s->enable)
+ for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++)
+ s->codec_out(s->opaque, *sample);
+ pxa2xx_i2s_update(s);
+}
+
+static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
+ hwaddr base,
+ qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *)
+ g_malloc0(sizeof(PXA2xxI2SState));
+
+ s->irq = irq;
+ s->rx_dma = rx_dma;
+ s->tx_dma = tx_dma;
+ s->data_req = pxa2xx_i2s_data_req;
+
+ pxa2xx_i2s_reset(s);
+
+ memory_region_init_io(&s->iomem, &pxa2xx_i2s_ops, s,
+ "pxa2xx-i2s", 0x100000);
+ memory_region_add_subregion(sysmem, base, &s->iomem);
+
+ vmstate_register(NULL, base, &vmstate_pxa2xx_i2s, s);
+
+ return s;
+}
+
+/* PXA Fast Infra-red Communications Port */
+struct PXA2xxFIrState {
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq rx_dma;
+ qemu_irq tx_dma;
+ int enable;
+ CharDriverState *chr;
+
+ uint8_t control[3];
+ uint8_t status[2];
+
+ int rx_len;
+ int rx_start;
+ uint8_t rx_fifo[64];
+};
+
+static void pxa2xx_fir_reset(PXA2xxFIrState *s)
+{
+ s->control[0] = 0x00;
+ s->control[1] = 0x00;
+ s->control[2] = 0x00;
+ s->status[0] = 0x00;
+ s->status[1] = 0x00;
+ s->enable = 0;
+}
+
+static inline void pxa2xx_fir_update(PXA2xxFIrState *s)
+{
+ static const int tresh[4] = { 8, 16, 32, 0 };
+ int intr = 0;
+ if ((s->control[0] & (1 << 4)) && /* RXE */
+ s->rx_len >= tresh[s->control[2] & 3]) /* TRIG */
+ s->status[0] |= 1 << 4; /* RFS */
+ else
+ s->status[0] &= ~(1 << 4); /* RFS */
+ if (s->control[0] & (1 << 3)) /* TXE */
+ s->status[0] |= 1 << 3; /* TFS */
+ else
+ s->status[0] &= ~(1 << 3); /* TFS */
+ if (s->rx_len)
+ s->status[1] |= 1 << 2; /* RNE */
+ else
+ s->status[1] &= ~(1 << 2); /* RNE */
+ if (s->control[0] & (1 << 4)) /* RXE */
+ s->status[1] |= 1 << 0; /* RSY */
+ else
+ s->status[1] &= ~(1 << 0); /* RSY */
+
+ intr |= (s->control[0] & (1 << 5)) && /* RIE */
+ (s->status[0] & (1 << 4)); /* RFS */
+ intr |= (s->control[0] & (1 << 6)) && /* TIE */
+ (s->status[0] & (1 << 3)); /* TFS */
+ intr |= (s->control[2] & (1 << 4)) && /* TRAIL */
+ (s->status[0] & (1 << 6)); /* EOC */
+ intr |= (s->control[0] & (1 << 2)) && /* TUS */
+ (s->status[0] & (1 << 1)); /* TUR */
+ intr |= s->status[0] & 0x25; /* FRE, RAB, EIF */
+
+ qemu_set_irq(s->rx_dma, (s->status[0] >> 4) & 1);
+ qemu_set_irq(s->tx_dma, (s->status[0] >> 3) & 1);
+
+ qemu_set_irq(s->irq, intr && s->enable);
+}
+
+#define ICCR0 0x00 /* FICP Control register 0 */
+#define ICCR1 0x04 /* FICP Control register 1 */
+#define ICCR2 0x08 /* FICP Control register 2 */
+#define ICDR 0x0c /* FICP Data register */
+#define ICSR0 0x14 /* FICP Status register 0 */
+#define ICSR1 0x18 /* FICP Status register 1 */
+#define ICFOR 0x1c /* FICP FIFO Occupancy Status register */
+
+static uint64_t pxa2xx_fir_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ uint8_t ret;
+
+ switch (addr) {
+ case ICCR0:
+ return s->control[0];
+ case ICCR1:
+ return s->control[1];
+ case ICCR2:
+ return s->control[2];
+ case ICDR:
+ s->status[0] &= ~0x01;
+ s->status[1] &= ~0x72;
+ if (s->rx_len) {
+ s->rx_len --;
+ ret = s->rx_fifo[s->rx_start ++];
+ s->rx_start &= 63;
+ pxa2xx_fir_update(s);
+ return ret;
+ }
+ printf("%s: Rx FIFO underrun.\n", __FUNCTION__);
+ break;
+ case ICSR0:
+ return s->status[0];
+ case ICSR1:
+ return s->status[1] | (1 << 3); /* TNF */
+ case ICFOR:
+ return s->rx_len;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_fir_write(void *opaque, hwaddr addr,
+ uint64_t value64, unsigned size)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ uint32_t value = value64;
+ uint8_t ch;
+
+ switch (addr) {
+ case ICCR0:
+ s->control[0] = value;
+ if (!(value & (1 << 4))) /* RXE */
+ s->rx_len = s->rx_start = 0;
+ if (!(value & (1 << 3))) { /* TXE */
+ /* Nop */
+ }
+ s->enable = value & 1; /* ITR */
+ if (!s->enable)
+ s->status[0] = 0;
+ pxa2xx_fir_update(s);
+ break;
+ case ICCR1:
+ s->control[1] = value;
+ break;
+ case ICCR2:
+ s->control[2] = value & 0x3f;
+ pxa2xx_fir_update(s);
+ break;
+ case ICDR:
+ if (s->control[2] & (1 << 2)) /* TXP */
+ ch = value;
+ else
+ ch = ~value;
+ if (s->chr && s->enable && (s->control[0] & (1 << 3))) /* TXE */
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ break;
+ case ICSR0:
+ s->status[0] &= ~(value & 0x66);
+ pxa2xx_fir_update(s);
+ break;
+ case ICFOR:
+ break;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_fir_ops = {
+ .read = pxa2xx_fir_read,
+ .write = pxa2xx_fir_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pxa2xx_fir_is_empty(void *opaque)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ return (s->rx_len < 64);
+}
+
+static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ if (!(s->control[0] & (1 << 4))) /* RXE */
+ return;
+
+ while (size --) {
+ s->status[1] |= 1 << 4; /* EOF */
+ if (s->rx_len >= 64) {
+ s->status[1] |= 1 << 6; /* ROR */
+ break;
+ }
+
+ if (s->control[2] & (1 << 3)) /* RXP */
+ s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = *(buf ++);
+ else
+ s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = ~*(buf ++);
+ }
+
+ pxa2xx_fir_update(s);
+}
+
+static void pxa2xx_fir_event(void *opaque, int event)
+{
+}
+
+static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->enable);
+
+ qemu_put_8s(f, &s->control[0]);
+ qemu_put_8s(f, &s->control[1]);
+ qemu_put_8s(f, &s->control[2]);
+ qemu_put_8s(f, &s->status[0]);
+ qemu_put_8s(f, &s->status[1]);
+
+ qemu_put_byte(f, s->rx_len);
+ for (i = 0; i < s->rx_len; i ++)
+ qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
+}
+
+static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ int i;
+
+ s->enable = qemu_get_be32(f);
+
+ qemu_get_8s(f, &s->control[0]);
+ qemu_get_8s(f, &s->control[1]);
+ qemu_get_8s(f, &s->control[2]);
+ qemu_get_8s(f, &s->status[0]);
+ qemu_get_8s(f, &s->status[1]);
+
+ s->rx_len = qemu_get_byte(f);
+ s->rx_start = 0;
+ for (i = 0; i < s->rx_len; i ++)
+ s->rx_fifo[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
+ hwaddr base,
+ qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma,
+ CharDriverState *chr)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *)
+ g_malloc0(sizeof(PXA2xxFIrState));
+
+ s->irq = irq;
+ s->rx_dma = rx_dma;
+ s->tx_dma = tx_dma;
+ s->chr = chr;
+
+ pxa2xx_fir_reset(s);
+
+ memory_region_init_io(&s->iomem, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000);
+ memory_region_add_subregion(sysmem, base, &s->iomem);
+
+ if (chr)
+ qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
+ pxa2xx_fir_rx, pxa2xx_fir_event, s);
+
+ register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
+ pxa2xx_fir_load, s);
+
+ return s;
+}
+
+static void pxa2xx_reset(void *opaque, int line, int level)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */
+ cpu_reset(CPU(s->cpu));
+ /* TODO: reset peripherals */
+ }
+}
+
+/* Initialise a PXA270 integrated chip (ARM based core). */
+PXA2xxState *pxa270_init(MemoryRegion *address_space,
+ unsigned int sdram_size, const char *revision)
+{
+ PXA2xxState *s;
+ int i;
+ DriveInfo *dinfo;
+ s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+
+ if (revision && strncmp(revision, "pxa27", 5)) {
+ fprintf(stderr, "Machine requires a PXA27x processor.\n");
+ exit(1);
+ }
+ if (!revision)
+ revision = "pxa270";
+
+ s->cpu = cpu_arm_init(revision);
+ if (s->cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0];
+
+ /* SDRAM & Internal Memory Storage */
+ memory_region_init_ram(&s->sdram, "pxa270.sdram", sdram_size);
+ vmstate_register_ram_global(&s->sdram);
+ memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
+ memory_region_init_ram(&s->internal, "pxa270.internal", 0x40000);
+ vmstate_register_ram_global(&s->internal);
+ memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
+ &s->internal);
+
+ s->pic = pxa2xx_pic_init(0x40d00000, s->cpu);
+
+ s->dma = pxa27x_dma_init(0x40000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA));
+
+ sysbus_create_varargs("pxa27x-timer", 0x40a00000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3),
+ qdev_get_gpio_in(s->pic, PXA27X_PIC_OST_4_11),
+ NULL);
+
+ s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, dinfo->bdrv,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));
+
+ for (i = 0; pxa270_serial[i].io_base; i++) {
+ if (serial_hds[i]) {
+ serial_mm_init(address_space, pxa270_serial[i].io_base, 2,
+ qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn),
+ 14857000 / 16, serial_hds[i],
+ DEVICE_NATIVE_ENDIAN);
+ } else {
+ break;
+ }
+ }
+ if (serial_hds[i])
+ s->fir = pxa2xx_fir_init(address_space, 0x40800000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP),
+ serial_hds[i]);
+
+ s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD));
+
+ s->cm_base = 0x41300000;
+ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */
+ s->clkcfg = 0x00000009; /* Turbo mode active */
+ memory_region_init_io(&s->cm_iomem, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000);
+ memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s);
+
+ pxa2xx_setup_cp14(s);
+
+ s->mm_base = 0x48000000;
+ s->mm_regs[MDMRS >> 2] = 0x00020002;
+ s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+ s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */
+ memory_region_init_io(&s->mm_iomem, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000);
+ memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s);
+
+ s->pm_base = 0x40f00000;
+ memory_region_init_io(&s->pm_iomem, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100);
+ memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
+
+ for (i = 0; pxa27x_ssp[i].io_base; i ++);
+ s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ for (i = 0; pxa27x_ssp[i].io_base; i ++) {
+ DeviceState *dev;
+ dev = sysbus_create_simple("pxa2xx-ssp", pxa27x_ssp[i].io_base,
+ qdev_get_gpio_in(s->pic, pxa27x_ssp[i].irqn));
+ s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
+ }
+
+ if (usb_enabled(false)) {
+ sysbus_create_simple("sysbus-ohci", 0x4c000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1));
+ }
+
+ s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000);
+ s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000);
+
+ sysbus_create_simple("pxa2xx_rtc", 0x40900000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM));
+
+ s->i2c[0] = pxa2xx_i2c_init(0x40301600,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff);
+ s->i2c[1] = pxa2xx_i2c_init(0x40f00100,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff);
+
+ s->i2s = pxa2xx_i2s_init(address_space, 0x40400000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S));
+
+ s->kp = pxa27x_keypad_init(address_space, 0x41500000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_KEYPAD));
+
+ /* GPIO1 resets the processor */
+ /* The handler can be overridden by board-specific code */
+ qdev_connect_gpio_out(s->gpio, 1, s->reset);
+ return s;
+}
+
+/* Initialise a PXA255 integrated chip (ARM based core). */
+PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
+{
+ PXA2xxState *s;
+ int i;
+ DriveInfo *dinfo;
+
+ s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+
+ s->cpu = cpu_arm_init("pxa255");
+ if (s->cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0];
+
+ /* SDRAM & Internal Memory Storage */
+ memory_region_init_ram(&s->sdram, "pxa255.sdram", sdram_size);
+ vmstate_register_ram_global(&s->sdram);
+ memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
+ memory_region_init_ram(&s->internal, "pxa255.internal",
+ PXA2XX_INTERNAL_SIZE);
+ vmstate_register_ram_global(&s->internal);
+ memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
+ &s->internal);
+
+ s->pic = pxa2xx_pic_init(0x40d00000, s->cpu);
+
+ s->dma = pxa255_dma_init(0x40000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA));
+
+ sysbus_create_varargs("pxa25x-timer", 0x40a00000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2),
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3),
+ NULL);
+
+ s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, dinfo->bdrv,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));
+
+ for (i = 0; pxa255_serial[i].io_base; i++) {
+ if (serial_hds[i]) {
+ serial_mm_init(address_space, pxa255_serial[i].io_base, 2,
+ qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn),
+ 14745600 / 16, serial_hds[i],
+ DEVICE_NATIVE_ENDIAN);
+ } else {
+ break;
+ }
+ }
+ if (serial_hds[i])
+ s->fir = pxa2xx_fir_init(address_space, 0x40800000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP),
+ serial_hds[i]);
+
+ s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD));
+
+ s->cm_base = 0x41300000;
+ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */
+ s->clkcfg = 0x00000009; /* Turbo mode active */
+ memory_region_init_io(&s->cm_iomem, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000);
+ memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s);
+
+ pxa2xx_setup_cp14(s);
+
+ s->mm_base = 0x48000000;
+ s->mm_regs[MDMRS >> 2] = 0x00020002;
+ s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+ s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */
+ memory_region_init_io(&s->mm_iomem, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000);
+ memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s);
+
+ s->pm_base = 0x40f00000;
+ memory_region_init_io(&s->pm_iomem, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100);
+ memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem);
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
+
+ for (i = 0; pxa255_ssp[i].io_base; i ++);
+ s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ for (i = 0; pxa255_ssp[i].io_base; i ++) {
+ DeviceState *dev;
+ dev = sysbus_create_simple("pxa2xx-ssp", pxa255_ssp[i].io_base,
+ qdev_get_gpio_in(s->pic, pxa255_ssp[i].irqn));
+ s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
+ }
+
+ if (usb_enabled(false)) {
+ sysbus_create_simple("sysbus-ohci", 0x4c000000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1));
+ }
+
+ s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000);
+ s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000);
+
+ sysbus_create_simple("pxa2xx_rtc", 0x40900000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM));
+
+ s->i2c[0] = pxa2xx_i2c_init(0x40301600,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff);
+ s->i2c[1] = pxa2xx_i2c_init(0x40f00100,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff);
+
+ s->i2s = pxa2xx_i2s_init(address_space, 0x40400000,
+ qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S),
+ qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S),
+ qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S));
+
+ /* GPIO1 resets the processor */
+ /* The handler can be overridden by board-specific code */
+ qdev_connect_gpio_out(s->gpio, 1, s->reset);
+ return s;
+}
+
+static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = pxa2xx_ssp_init;
+}
+
+static const TypeInfo pxa2xx_ssp_info = {
+ .name = "pxa2xx-ssp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxSSPState),
+ .class_init = pxa2xx_ssp_class_init,
+};
+
+static void pxa2xx_register_types(void)
+{
+ type_register_static(&pxa2xx_i2c_slave_info);
+ type_register_static(&pxa2xx_ssp_info);
+ type_register_static(&pxa2xx_i2c_info);
+ type_register_static(&pxa2xx_rtc_sysbus_info);
+}
+
+type_init(pxa2xx_register_types)
diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c
new file mode 100644
index 0000000..eef8411
--- /dev/null
+++ b/hw/arm/pxa2xx_gpio.c
@@ -0,0 +1,350 @@
+/*
+ * Intel XScale PXA255/270 GPIO controller emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pxa.h"
+
+#define PXA2XX_GPIO_BANKS 4
+
+typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo;
+struct PXA2xxGPIOInfo {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq0, irq1, irqX;
+ int lines;
+ int ncpu;
+ ARMCPU *cpu;
+
+ /* XXX: GNU C vectors are more suitable */
+ uint32_t ilevel[PXA2XX_GPIO_BANKS];
+ uint32_t olevel[PXA2XX_GPIO_BANKS];
+ uint32_t dir[PXA2XX_GPIO_BANKS];
+ uint32_t rising[PXA2XX_GPIO_BANKS];
+ uint32_t falling[PXA2XX_GPIO_BANKS];
+ uint32_t status[PXA2XX_GPIO_BANKS];
+ uint32_t gpsr[PXA2XX_GPIO_BANKS];
+ uint32_t gafr[PXA2XX_GPIO_BANKS * 2];
+
+ uint32_t prev_level[PXA2XX_GPIO_BANKS];
+ qemu_irq handler[PXA2XX_GPIO_BANKS * 32];
+ qemu_irq read_notify;
+};
+
+static struct {
+ enum {
+ GPIO_NONE,
+ GPLR,
+ GPSR,
+ GPCR,
+ GPDR,
+ GRER,
+ GFER,
+ GEDR,
+ GAFR_L,
+ GAFR_U,
+ } reg;
+ int bank;
+} pxa2xx_gpio_regs[0x200] = {
+ [0 ... 0x1ff] = { GPIO_NONE, 0 },
+#define PXA2XX_REG(reg, a0, a1, a2, a3) \
+ [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 },
+
+ PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100)
+ PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118)
+ PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124)
+ PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c)
+ PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130)
+ PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c)
+ PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148)
+ PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c)
+ PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070)
+};
+
+static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s)
+{
+ if (s->status[0] & (1 << 0))
+ qemu_irq_raise(s->irq0);
+ else
+ qemu_irq_lower(s->irq0);
+
+ if (s->status[0] & (1 << 1))
+ qemu_irq_raise(s->irq1);
+ else
+ qemu_irq_lower(s->irq1);
+
+ if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3])
+ qemu_irq_raise(s->irqX);
+ else
+ qemu_irq_lower(s->irqX);
+}
+
+/* Bitmap of pins used as standby and sleep wake-up sources. */
+static const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = {
+ 0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f,
+};
+
+static void pxa2xx_gpio_set(void *opaque, int line, int level)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ int bank;
+ uint32_t mask;
+
+ if (line >= s->lines) {
+ printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
+ return;
+ }
+
+ bank = line >> 5;
+ mask = 1 << (line & 31);
+
+ if (level) {
+ s->status[bank] |= s->rising[bank] & mask &
+ ~s->ilevel[bank] & ~s->dir[bank];
+ s->ilevel[bank] |= mask;
+ } else {
+ s->status[bank] |= s->falling[bank] & mask &
+ s->ilevel[bank] & ~s->dir[bank];
+ s->ilevel[bank] &= ~mask;
+ }
+
+ if (s->status[bank] & mask)
+ pxa2xx_gpio_irq_update(s);
+
+ /* Wake-up GPIOs */
+ if (s->cpu->env.halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_EXITTB);
+ }
+}
+
+static void pxa2xx_gpio_handler_update(PXA2xxGPIOInfo *s) {
+ uint32_t level, diff;
+ int i, bit, line;
+ for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) {
+ level = s->olevel[i] & s->dir[i];
+
+ for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ line = bit + 32 * i;
+ qemu_set_irq(s->handler[line], (level >> bit) & 1);
+ }
+
+ s->prev_level[i] = level;
+ }
+}
+
+static uint64_t pxa2xx_gpio_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ uint32_t ret;
+ int bank;
+ if (offset >= 0x200)
+ return 0;
+
+ bank = pxa2xx_gpio_regs[offset].bank;
+ switch (pxa2xx_gpio_regs[offset].reg) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir[bank];
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register " REG_FMT "\n",
+ __FUNCTION__, offset);
+ return s->gpsr[bank]; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register " REG_FMT "\n",
+ __FUNCTION__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising[bank];
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling[bank];
+
+ case GAFR_L: /* GPIO Alternate Function registers */
+ return s->gafr[bank * 2];
+
+ case GAFR_U: /* GPIO Alternate Function registers */
+ return s->gafr[bank * 2 + 1];
+
+ case GPLR: /* GPIO Pin-Level registers */
+ ret = (s->olevel[bank] & s->dir[bank]) |
+ (s->ilevel[bank] & ~s->dir[bank]);
+ qemu_irq_raise(s->read_notify);
+ return ret;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status[bank];
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_gpio_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ int bank;
+ if (offset >= 0x200)
+ return;
+
+ bank = pxa2xx_gpio_regs[offset].bank;
+ switch (pxa2xx_gpio_regs[offset].reg) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir[bank] = value;
+ pxa2xx_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel[bank] |= value;
+ pxa2xx_gpio_handler_update(s);
+ s->gpsr[bank] = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel[bank] &= ~value;
+ pxa2xx_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising[bank] = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling[bank] = value;
+ break;
+
+ case GAFR_L: /* GPIO Alternate Function registers */
+ s->gafr[bank * 2] = value;
+ break;
+
+ case GAFR_U: /* GPIO Alternate Function registers */
+ s->gafr[bank * 2 + 1] = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status[bank] &= ~value;
+ pxa2xx_gpio_irq_update(s);
+ break;
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static const MemoryRegionOps pxa_gpio_ops = {
+ .read = pxa2xx_gpio_read,
+ .write = pxa2xx_gpio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+DeviceState *pxa2xx_gpio_init(hwaddr base,
+ ARMCPU *cpu, DeviceState *pic, int lines)
+{
+ CPUState *cs = CPU(cpu);
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "pxa2xx-gpio");
+ qdev_prop_set_int32(dev, "lines", lines);
+ qdev_prop_set_int32(dev, "ncpu", cs->cpu_index);
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+ qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1,
+ qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_1));
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2,
+ qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_X));
+
+ return dev;
+}
+
+static int pxa2xx_gpio_initfn(SysBusDevice *dev)
+{
+ PXA2xxGPIOInfo *s;
+
+ s = FROM_SYSBUS(PXA2xxGPIOInfo, dev);
+
+ s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
+
+ qdev_init_gpio_in(&dev->qdev, pxa2xx_gpio_set, s->lines);
+ qdev_init_gpio_out(&dev->qdev, s->handler, s->lines);
+
+ memory_region_init_io(&s->iomem, &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq0);
+ sysbus_init_irq(dev, &s->irq1);
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+/*
+ * Registers a callback to notify on GPLR reads. This normally
+ * shouldn't be needed but it is used for the hack on Spitz machines.
+ */
+void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler)
+{
+ PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, SYS_BUS_DEVICE(dev));
+ s->read_notify = handler;
+}
+
+static const VMStateDescription vmstate_pxa2xx_gpio_regs = {
+ .name = "pxa2xx-gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(lines, PXA2xxGPIOInfo),
+ VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static Property pxa2xx_gpio_properties[] = {
+ DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0),
+ DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_gpio_initfn;
+ dc->desc = "PXA2xx GPIO controller";
+ dc->props = pxa2xx_gpio_properties;
+}
+
+static const TypeInfo pxa2xx_gpio_info = {
+ .name = "pxa2xx-gpio",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxGPIOInfo),
+ .class_init = pxa2xx_gpio_class_init,
+};
+
+static void pxa2xx_gpio_register_types(void)
+{
+ type_register_static(&pxa2xx_gpio_info);
+}
+
+type_init(pxa2xx_gpio_register_types)
diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c
new file mode 100644
index 0000000..145fc78
--- /dev/null
+++ b/hw/arm/pxa2xx_pic.c
@@ -0,0 +1,334 @@
+/*
+ * Intel XScale PXA Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/hw.h"
+#include "hw/pxa.h"
+#include "hw/sysbus.h"
+
+#define ICIP 0x00 /* Interrupt Controller IRQ Pending register */
+#define ICMR 0x04 /* Interrupt Controller Mask register */
+#define ICLR 0x08 /* Interrupt Controller Level register */
+#define ICFP 0x0c /* Interrupt Controller FIQ Pending register */
+#define ICPR 0x10 /* Interrupt Controller Pending register */
+#define ICCR 0x14 /* Interrupt Controller Control register */
+#define ICHP 0x18 /* Interrupt Controller Highest Priority register */
+#define IPR0 0x1c /* Interrupt Controller Priority register 0 */
+#define IPR31 0x98 /* Interrupt Controller Priority register 31 */
+#define ICIP2 0x9c /* Interrupt Controller IRQ Pending register 2 */
+#define ICMR2 0xa0 /* Interrupt Controller Mask register 2 */
+#define ICLR2 0xa4 /* Interrupt Controller Level register 2 */
+#define ICFP2 0xa8 /* Interrupt Controller FIQ Pending register 2 */
+#define ICPR2 0xac /* Interrupt Controller Pending register 2 */
+#define IPR32 0xb0 /* Interrupt Controller Priority register 32 */
+#define IPR39 0xcc /* Interrupt Controller Priority register 39 */
+
+#define PXA2XX_PIC_SRCS 40
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ ARMCPU *cpu;
+ uint32_t int_enabled[2];
+ uint32_t int_pending[2];
+ uint32_t is_fiq[2];
+ uint32_t int_idle;
+ uint32_t priority[PXA2XX_PIC_SRCS];
+} PXA2xxPICState;
+
+static void pxa2xx_pic_update(void *opaque)
+{
+ uint32_t mask[2];
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ if (s->cpu->env.halted) {
+ mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle);
+ mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle);
+ if (mask[0] || mask[1]) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_EXITTB);
+ }
+ }
+
+ mask[0] = s->int_pending[0] & s->int_enabled[0];
+ mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+ if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1])) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
+ } else {
+ cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
+ }
+
+ if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1])) {
+ cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
+ }
+}
+
+/* Note: Here level means state of the signal on a pin, not
+ * IRQ/FIQ distinction as in PXA Developer Manual. */
+static void pxa2xx_pic_set_irq(void *opaque, int irq, int level)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+ int int_set = (irq >= 32);
+ irq &= 31;
+
+ if (level)
+ s->int_pending[int_set] |= 1 << irq;
+ else
+ s->int_pending[int_set] &= ~(1 << irq);
+
+ pxa2xx_pic_update(opaque);
+}
+
+static inline uint32_t pxa2xx_pic_highest(PXA2xxPICState *s) {
+ int i, int_set, irq;
+ uint32_t bit, mask[2];
+ uint32_t ichp = 0x003f003f; /* Both IDs invalid */
+
+ mask[0] = s->int_pending[0] & s->int_enabled[0];
+ mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+ for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) {
+ irq = s->priority[i] & 0x3f;
+ if ((s->priority[i] & (1 << 31)) && irq < PXA2XX_PIC_SRCS) {
+ /* Source peripheral ID is valid. */
+ bit = 1 << (irq & 31);
+ int_set = (irq >= 32);
+
+ if (mask[int_set] & bit & s->is_fiq[int_set]) {
+ /* FIQ asserted */
+ ichp &= 0xffff0000;
+ ichp |= (1 << 15) | irq;
+ }
+
+ if (mask[int_set] & bit & ~s->is_fiq[int_set]) {
+ /* IRQ asserted */
+ ichp &= 0x0000ffff;
+ ichp |= (1 << 31) | (irq << 16);
+ }
+ }
+ }
+
+ return ichp;
+}
+
+static uint64_t pxa2xx_pic_mem_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ switch (offset) {
+ case ICIP: /* IRQ Pending register */
+ return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0];
+ case ICIP2: /* IRQ Pending register 2 */
+ return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1];
+ case ICMR: /* Mask register */
+ return s->int_enabled[0];
+ case ICMR2: /* Mask register 2 */
+ return s->int_enabled[1];
+ case ICLR: /* Level register */
+ return s->is_fiq[0];
+ case ICLR2: /* Level register 2 */
+ return s->is_fiq[1];
+ case ICCR: /* Idle mask */
+ return (s->int_idle == 0);
+ case ICFP: /* FIQ Pending register */
+ return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0];
+ case ICFP2: /* FIQ Pending register 2 */
+ return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1];
+ case ICPR: /* Pending register */
+ return s->int_pending[0];
+ case ICPR2: /* Pending register 2 */
+ return s->int_pending[1];
+ case IPR0 ... IPR31:
+ return s->priority[0 + ((offset - IPR0 ) >> 2)];
+ case IPR32 ... IPR39:
+ return s->priority[32 + ((offset - IPR32) >> 2)];
+ case ICHP: /* Highest Priority register */
+ return pxa2xx_pic_highest(s);
+ default:
+ printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+ return 0;
+ }
+}
+
+static void pxa2xx_pic_mem_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ switch (offset) {
+ case ICMR: /* Mask register */
+ s->int_enabled[0] = value;
+ break;
+ case ICMR2: /* Mask register 2 */
+ s->int_enabled[1] = value;
+ break;
+ case ICLR: /* Level register */
+ s->is_fiq[0] = value;
+ break;
+ case ICLR2: /* Level register 2 */
+ s->is_fiq[1] = value;
+ break;
+ case ICCR: /* Idle mask */
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ case IPR0 ... IPR31:
+ s->priority[0 + ((offset - IPR0 ) >> 2)] = value & 0x8000003f;
+ break;
+ case IPR32 ... IPR39:
+ s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f;
+ break;
+ default:
+ printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+ return;
+ }
+ pxa2xx_pic_update(opaque);
+}
+
+/* Interrupt Controller Coprocessor Space Register Mapping */
+static const int pxa2xx_cp_reg_map[0x10] = {
+ [0x0 ... 0xf] = -1,
+ [0x0] = ICIP,
+ [0x1] = ICMR,
+ [0x2] = ICLR,
+ [0x3] = ICFP,
+ [0x4] = ICPR,
+ [0x5] = ICHP,
+ [0x6] = ICIP2,
+ [0x7] = ICMR2,
+ [0x8] = ICLR2,
+ [0x9] = ICFP2,
+ [0xa] = ICPR2,
+};
+
+static int pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ int offset = pxa2xx_cp_reg_map[ri->crn];
+ *value = pxa2xx_pic_mem_read(ri->opaque, offset, 4);
+ return 0;
+}
+
+static int pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ int offset = pxa2xx_cp_reg_map[ri->crn];
+ pxa2xx_pic_mem_write(ri->opaque, offset, value, 4);
+ return 0;
+}
+
+#define REGINFO_FOR_PIC_CP(NAME, CRN) \
+ { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \
+ .access = PL1_RW, \
+ .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write }
+
+static const ARMCPRegInfo pxa_pic_cp_reginfo[] = {
+ REGINFO_FOR_PIC_CP("ICIP", 0),
+ REGINFO_FOR_PIC_CP("ICMR", 1),
+ REGINFO_FOR_PIC_CP("ICLR", 2),
+ REGINFO_FOR_PIC_CP("ICFP", 3),
+ REGINFO_FOR_PIC_CP("ICPR", 4),
+ REGINFO_FOR_PIC_CP("ICHP", 5),
+ REGINFO_FOR_PIC_CP("ICIP2", 6),
+ REGINFO_FOR_PIC_CP("ICMR2", 7),
+ REGINFO_FOR_PIC_CP("ICLR2", 8),
+ REGINFO_FOR_PIC_CP("ICFP2", 9),
+ REGINFO_FOR_PIC_CP("ICPR2", 0xa),
+ REGINFO_SENTINEL
+};
+
+static const MemoryRegionOps pxa2xx_pic_ops = {
+ .read = pxa2xx_pic_mem_read,
+ .write = pxa2xx_pic_mem_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pxa2xx_pic_post_load(void *opaque, int version_id)
+{
+ pxa2xx_pic_update(opaque);
+ return 0;
+}
+
+DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu)
+{
+ CPUARMState *env = &cpu->env;
+ DeviceState *dev = qdev_create(NULL, "pxa2xx_pic");
+ PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, SYS_BUS_DEVICE(dev));
+
+ s->cpu = cpu;
+
+ s->int_pending[0] = 0;
+ s->int_pending[1] = 0;
+ s->int_enabled[0] = 0;
+ s->int_enabled[1] = 0;
+ s->is_fiq[0] = 0;
+ s->is_fiq[1] = 0;
+
+ qdev_init_nofail(dev);
+
+ qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS);
+
+ /* Enable IC memory-mapped registers access. */
+ memory_region_init_io(&s->iomem, &pxa2xx_pic_ops, s,
+ "pxa2xx-pic", 0x00100000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+
+ /* Enable IC coprocessor access. */
+ define_arm_cp_regs_with_opaque(arm_env_get_cpu(env), pxa_pic_cp_reginfo, s);
+
+ return dev;
+}
+
+static VMStateDescription vmstate_pxa2xx_pic_regs = {
+ .name = "pxa2xx_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = pxa2xx_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(int_enabled, PXA2xxPICState, 2),
+ VMSTATE_UINT32_ARRAY(int_pending, PXA2xxPICState, 2),
+ VMSTATE_UINT32_ARRAY(is_fiq, PXA2xxPICState, 2),
+ VMSTATE_UINT32(int_idle, PXA2xxPICState),
+ VMSTATE_UINT32_ARRAY(priority, PXA2xxPICState, PXA2XX_PIC_SRCS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static int pxa2xx_pic_initfn(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static void pxa2xx_pic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_pic_initfn;
+ dc->desc = "PXA2xx PIC";
+ dc->vmsd = &vmstate_pxa2xx_pic_regs;
+}
+
+static const TypeInfo pxa2xx_pic_info = {
+ .name = "pxa2xx_pic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxPICState),
+ .class_init = pxa2xx_pic_class_init,
+};
+
+static void pxa2xx_pic_register_types(void)
+{
+ type_register_static(&pxa2xx_pic_info);
+}
+
+type_init(pxa2xx_pic_register_types)
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);
OpenPOWER on IntegriCloud