[Title] arm_emulator branch merged into master
authorjinseok86.kim <jinseok86.kim@samsung.com>
Mon, 12 Dec 2011 06:55:02 +0000 (15:55 +0900)
committerjinseok86.kim <jinseok86.kim@samsung.com>
Mon, 12 Dec 2011 06:55:02 +0000 (15:55 +0900)
[Type] Support
[Module]
[Priority]
[CQ#]
[Redmine#]
[Problem]
[Cause]
[Solution]
[TestCase]

Skin HW Button is disabled due to multiple keyboard support. (Needs re-implementation)

111 files changed:
Makefile.objs
Makefile.target
block.c
block/raw-posix.c
block/raw.c
block/vvfat.c
block_int.h
configure
console.h
default-configs/arm-softmmu.mak
hw/adb.c
hw/ak8973.c [new file with mode: 0644]
hw/ak8973.h [new file with mode: 0644]
hw/arm-misc.h
hw/arm_boot.c
hw/bitbang_i2c.c
hw/boards.h
hw/eepro100.c
hw/escc.c
hw/flash.h
hw/gles1_calls.c [new file with mode: 0644]
hw/gles2.c [new file with mode: 0644]
hw/gles2.h [new file with mode: 0644]
hw/gles2_calls.c [new file with mode: 0644]
hw/gles2_calls.h [new file with mode: 0644]
hw/gles2_types.h [new file with mode: 0644]
hw/i2c-addressable.c [new file with mode: 0644]
hw/i2c-addressable.h [new file with mode: 0644]
hw/i2c.h
hw/ide/mmio.c
hw/iommu.c [new file with mode: 0644]
hw/mcs5000_tk.c [new file with mode: 0644]
hw/musicpal.c
hw/nseries.c
hw/onenand.c
hw/palm.c
hw/pci_host_template.h [new file with mode: 0644]
hw/pci_ids.h
hw/pl192.c [new file with mode: 0644]
hw/pl330.c [new file with mode: 0644]
hw/primecell.h
hw/ps2.c
hw/pxa2xx_keypad.c
hw/qdev.c
hw/qdev.h
hw/qt602240_ts.c [new file with mode: 0644]
hw/s5pc1xx.c [new file with mode: 0644]
hw/s5pc1xx.h [new file with mode: 0644]
hw/s5pc1xx_ac97.c [new file with mode: 0644]
hw/s5pc1xx_clk.c [new file with mode: 0644]
hw/s5pc1xx_debug.c [new file with mode: 0644]
hw/s5pc1xx_gpio.c [new file with mode: 0644]
hw/s5pc1xx_gpio_regs.h [new file with mode: 0644]
hw/s5pc1xx_hsmmc_regs.h [new file with mode: 0644]
hw/s5pc1xx_i2c.c [new file with mode: 0644]
hw/s5pc1xx_i2c_gpio.c [new file with mode: 0644]
hw/s5pc1xx_i2s.c [new file with mode: 0644]
hw/s5pc1xx_jpeg.c [new file with mode: 0644]
hw/s5pc1xx_keyif.c [new file with mode: 0644]
hw/s5pc1xx_lcd.c [new file with mode: 0644]
hw/s5pc1xx_mmc.c [new file with mode: 0644]
hw/s5pc1xx_nand.c [new file with mode: 0644]
hw/s5pc1xx_onedram.c [new file with mode: 0644]
hw/s5pc1xx_onedram.h [new file with mode: 0644]
hw/s5pc1xx_onenand.c [new file with mode: 0644]
hw/s5pc1xx_pcm.c [new file with mode: 0644]
hw/s5pc1xx_pmu.c [new file with mode: 0644]
hw/s5pc1xx_pwm.c [new file with mode: 0644]
hw/s5pc1xx_rtc.c [new file with mode: 0644]
hw/s5pc1xx_spdif.c [new file with mode: 0644]
hw/s5pc1xx_spi.c [new file with mode: 0644]
hw/s5pc1xx_srom.c [new file with mode: 0644]
hw/s5pc1xx_st.c [new file with mode: 0644]
hw/s5pc1xx_tsadc.c [new file with mode: 0644]
hw/s5pc1xx_uart.c [new file with mode: 0644]
hw/s5pc1xx_usb_otg.c [new file with mode: 0644]
hw/s5pc1xx_wdt.c [new file with mode: 0644]
hw/smb380.c [new file with mode: 0644]
hw/smbus_smb380.c [new file with mode: 0644]
hw/spitz.c
hw/stellaris_input.c
hw/syborg_keyboard.c
hw/usb-ehci.c [new file with mode: 0644]
hw/usb-ehci.h [new file with mode: 0644]
hw/usb-hid.c
hw/usb-ohci.c
hw/wm8994.c [new file with mode: 0644]
hw/wm8994_reg.h [new file with mode: 0644]
hw/xenfb.c
input.c
linux-user/elfload32.c [new file with mode: 0644]
monitor.c
qemu-options.hx
qemu_configure.sh
qmp-commands.hx
slirp/ip_icmp.h
target-arm/cpu.h
target-arm/helper.c
target-arm/helper_gpi_dummy.c [new file with mode: 0644]
target-arm/helpers.h
target-arm/machine.c
target-arm/neon_helper.c
target-arm/op_helper.c
target-arm/opengl_dummy.c [new file with mode: 0644]
target-arm/translate.c
tizen/src/Makefile.in
tizen/src/arch_arm.c
tizen/src/event_handler.c
ui/vnc-enc-tight.c
usb-linux.c
vl.c

index 3b52e33..2215e94 100644 (file)
@@ -83,7 +83,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o
 common-obj-$(CONFIG_ADS7846) += ads7846.o
 common-obj-$(CONFIG_MAX111X) += max111x.o
 common-obj-$(CONFIG_DS1338) += ds1338.o
-common-obj-y += i2c.o smbus.o smbus_eeprom.o
+common-obj-y += i2c.o i2c-addressable.o smbus.o smbus_eeprom.o
 common-obj-y += eeprom93xx.o
 common-obj-y += scsi-disk.o cdrom.o
 common-obj-y += scsi-generic.o scsi-bus.o
@@ -190,7 +190,7 @@ hw-obj-$(CONFIG_I8254) += i8254.o
 hw-obj-$(CONFIG_PCSPK) += pcspk.o
 hw-obj-$(CONFIG_PCKBD) += pckbd.o
 hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
-hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
+hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
 hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
index 9fd41e9..a899f8e 100644 (file)
@@ -322,7 +322,7 @@ obj-arm-y += arm-semi.o
 obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
 obj-arm-y += gumstix.o
-obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o
+obj-arm-y += zaurus.o ide/microdrive.o tosa.o tc6393xb.o
 obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
                omap_gpio.o omap_intc.o omap_uart.o
 obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
@@ -336,6 +336,59 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
 obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
 obj-arm-y += syborg_virtio.o
 
+#########################################################
+# Samsung additional hardware
+obj-arm-y += pl192.o pl330.o
+obj-arm-y += s5pc1xx.o s5pc1xx_pmu.o s5pc1xx_onedram.o s5pc1xx_usb_otg.o
+obj-arm-y += s5pc1xx_i2c.o s5pc1xx_gpio.o s5pc1xx_i2c_gpio.o s5pc1xx_spi.o
+obj-arm-y += s5pc1xx_clk.o s5pc1xx_rtc.o s5pc1xx_pwm.o s5pc1xx_wdt.o s5pc1xx_st.o
+obj-arm-y += s5pc1xx_nand.o s5pc1xx_onenand.o s5pc1xx_srom.o s5pc1xx_mmc.o
+obj-arm-y += s5pc1xx_uart.o s5pc1xx_tsadc.o s5pc1xx_keyif.o s5pc1xx_lcd.o
+obj-arm-y += s5pc1xx_ac97.o s5pc1xx_pcm.o s5pc1xx_spdif.o s5pc1xx_i2s.o
+obj-arm-y += s5pc1xx_jpeg.o
+obj-arm-y += qt602240_ts.o mcs5000_tk.o wm8994.o ak8973.o
+obj-arm-y += smb380.o
+obj-arm-y += ide/mmio.o
+
+# USB layer
+obj-arm-$(CONFIG_USB_OHCI) += usb-ohci.o
+
+#ifeq ($(HOST_USB), libusb)
+#LIBS += $(LIBUSB_LIBS)
+#endif
+
+##########################################################
+# virtio, opengl for arm(dummy) 
+obj-arm-y += helper_gpi_dummy.o opengl_dummy.o
+##########################################################
+
+ifeq ($(TARGET_BASE_ARCH), arm)
+ifdef CONFIG_JPEG
+LIBS+=-lm -ljpeg
+CFLAGS+= $(VNC_JPEG_CFLAGS)
+endif
+ifdef CONFIG_FFMPEG
+LIBS+=-lavformat -lavcodec -lavutil -lswscale -lbz2
+CFLAGS+= $(FFMPEG_CFLAGS)
+endif
+
+ifdef CONFIG_GLES2
+CFLAGS+=-I$(DGLES2)/include
+LIBS+=-L$(DGLES2)/lib
+ifdef CONFIG_GLES2_STATIC
+LIBS+=-Wl,-Bstatic -lEGL -lGLESv2 -lOSMesa -Wl,-Bdynamic
+else
+LIBS+=-lEGL -lGLESv2
+endif
+ifndef CONFIG_WIN32
+LIBS+=-LX11 -ldl
+endif
+obj-arm-y += gles2.o gles2_calls.o gles1_calls.o
+endif # CONFIG_GLES2
+endif # TARGET_BASE_ARCH==arm
+
+#############################################################
+
 obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
 obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
 obj-sh4-y += ide/mmio.o
@@ -375,16 +428,17 @@ main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
 monitor.o: hmp-commands.h qmp-commands.h
 
+
 $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
 
 obj-y += $(addprefix ../, $(common-obj-y))
 obj-y += $(addprefix ../libdis/, $(libdis-y))
 obj-y += $(libobj-y)
 obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
+obj-y += $(addprefix ../, $(trace-obj-y))
 
 endif # CONFIG_SOFTMMU
 
-obj-y += $(addprefix ../, $(trace-obj-y))
 obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
 
 #$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
@@ -425,9 +479,9 @@ $(TARGET_PATH)/libGL.so.1: opengl_client.c gl_func.h
 endif
 
 # arm
-#gles1_calls.o: gles1_calls.c gles2.h gles2_calls.h
-#gles2_calls.o: gles2_calls.c gles2.h gles2_calls.h
-#gles2.o: gles2.c gles2.h gles2_calls.h
+gles1_calls.o: gles1_calls.c gles2.h gles2_calls.h
+gles2_calls.o: gles2_calls.c gles2.h gles2_calls.h
+gles2.o: gles2.c gles2.h gles2_calls.h
 
 ##########################################################
 
diff --git a/block.c b/block.c
index b203875..5bedad0 100644 (file)
--- a/block.c
+++ b/block.c
@@ -532,6 +532,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
               BlockDriver *drv)
 {
     int ret;
+    int probed = 0;
 
     if (flags & BDRV_O_SNAPSHOT) {
         BlockDriverState *bs1;
@@ -592,6 +593,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
     /* Find the right image format driver */
     if (!drv) {
         ret = find_image_format(filename, &drv);
+        probed = 1;
     }
 
     if (!drv) {
@@ -604,6 +606,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
         goto unlink_and_fail;
     }
 
+    bs->probed = probed;
+
     /* If there is a backing file, use it */
     if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
         char backing_filename[PATH_MAX];
index 6b72470..4b4ce6f 100644 (file)
@@ -1218,6 +1218,9 @@ static int cdrom_probe_device(const char *filename)
     int fd, ret;
     int prio = 0;
 
+    if (strstart(filename, "/dev/cd", NULL))
+        prio = 50;
+
     fd = open(filename, O_RDONLY | O_NONBLOCK);
     if (fd < 0) {
         goto out;
index b0f72d6..70c2fb9 100644 (file)
@@ -9,15 +9,82 @@ static int raw_open(BlockDriverState *bs, int flags)
     return 0;
 }
 
+/* check for the user attempting to write something that looks like a
+   block format header to the beginning of the image and fail out.
+*/
+static int check_for_block_signature(BlockDriverState *bs, const uint8_t *buf)
+{
+    static const uint8_t signatures[][4] = {
+        { 'Q', 'F', 'I', 0xfb }, /* qcow/qcow2 */
+        { 'C', 'O', 'W', 'D' }, /* VMDK3 */
+        { 'V', 'M', 'D', 'K' }, /* VMDK4 */
+        { 'O', 'O', 'O', 'M' }, /* UML COW */
+        {}
+    };
+    int i;
+
+    for (i = 0; signatures[i][0] != 0; i++) {
+        if (memcmp(buf, signatures[i], 4) == 0) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int check_write_unsafe(BlockDriverState *bs, int64_t sector_num,
+                              const uint8_t *buf, int nb_sectors)
+{
+    /* assume that if the user specifies the format explicitly, then assume
+       that they will continue to do so and provide no safety net */
+    if (!bs->probed) {
+        return 0;
+    }
+
+    if (sector_num == 0 && nb_sectors > 0) {
+        return check_for_block_signature(bs, buf);
+    }
+
+    return 0;
+}
+
 static int raw_read(BlockDriverState *bs, int64_t sector_num,
                     uint8_t *buf, int nb_sectors)
 {
     return bdrv_read(bs->file, sector_num, buf, nb_sectors);
 }
 
+static int raw_write_scrubbed_bootsect(BlockDriverState *bs,
+                                       const uint8_t *buf)
+{
+    uint8_t bootsect[512];
+
+    /* scrub the dangerous signature */
+    memcpy(bootsect, buf, 512);
+    memset(bootsect, 0, 4);
+
+    return bdrv_write(bs->file, 0, bootsect, 1);
+}
+
 static int raw_write(BlockDriverState *bs, int64_t sector_num,
                      const uint8_t *buf, int nb_sectors)
 {
+    if (check_write_unsafe(bs, sector_num, buf, nb_sectors)) {
+        int ret;
+
+        ret = raw_write_scrubbed_bootsect(bs, buf);
+        if (ret < 0) {
+            return ret;
+        }
+
+        ret = bdrv_write(bs->file, 1, buf + 512, nb_sectors - 1);
+        if (ret < 0) {
+            return ret;
+        }
+
+        return ret + 512;
+    }
+
     return bdrv_write(bs->file, sector_num, buf, nb_sectors);
 }
 
@@ -28,10 +95,73 @@ static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
+typedef struct RawScrubberBounce
+{
+    BlockDriverCompletionFunc *cb;
+    void *opaque;
+    QEMUIOVector qiov;
+} RawScrubberBounce;
+
+static void raw_aio_writev_scrubbed(void *opaque, int ret)
+{
+    RawScrubberBounce *b = opaque;
+
+    if (ret < 0) {
+        b->cb(b->opaque, ret);
+    } else {
+        b->cb(b->opaque, ret + 512);
+    }
+
+    qemu_iovec_destroy(&b->qiov);
+    qemu_free(b);
+}
+
 static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
     BlockDriverCompletionFunc *cb, void *opaque)
 {
+    const uint8_t *first_buf;
+    int first_buf_index = 0, i;
+
+    /* This is probably being paranoid, but handle cases of zero size
+       vectors. */
+    for (i = 0; i < qiov->niov; i++) {
+        if (qiov->iov[i].iov_len) {
+            assert(qiov->iov[i].iov_len >= 512);
+            first_buf_index = i;
+            break;
+        }
+    }
+
+    first_buf = qiov->iov[first_buf_index].iov_base;
+
+    if (check_write_unsafe(bs, sector_num, first_buf, nb_sectors)) {
+        RawScrubberBounce *b;
+        int ret;
+
+        /* write the first sector using sync I/O */
+        ret = raw_write_scrubbed_bootsect(bs, first_buf);
+        if (ret < 0) {
+            return NULL;
+        }
+
+        /* adjust request to be everything but first sector */
+
+        b = qemu_malloc(sizeof(*b));
+        b->cb = cb;
+        b->opaque = opaque;
+
+        qemu_iovec_init(&b->qiov, qiov->nalloc);
+        qemu_iovec_concat(&b->qiov, qiov, qiov->size);
+
+        b->qiov.size -= 512;
+        b->qiov.iov[first_buf_index].iov_base += 512;
+        b->qiov.iov[first_buf_index].iov_len -= 512;
+
+        return bdrv_aio_writev(bs->file, sector_num + 1, &b->qiov,
+                               nb_sectors - 1, raw_aio_writev_scrubbed, b);
+    }
+
     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
index fe568fe..2d28c48 100644 (file)
@@ -817,7 +817,13 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
 
 static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
 {
-    return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+    uint32_t ret;
+
+    if (sector_num < s->faked_sectors)
+        ret = 0;
+    else
+        ret = (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+    return ret;
 }
 
 static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
index 545ad11..b9755f0 100644 (file)
@@ -152,6 +152,7 @@ struct BlockDriverState {
     int encrypted; /* if true, the media is encrypted */
     int valid_key; /* if true, a valid encryption key has been set */
     int sg;        /* if true, the device is a /dev/sg* */
+    int probed;    /* if true, format was probed automatically */
     /* event callback when inserting/removing */
     void (*change_cb)(void *opaque, int reason);
     void *change_opaque;
index fdbe3d9..f315a1b 100755 (executable)
--- a/configure
+++ b/configure
@@ -122,6 +122,7 @@ uuid=""
 vde=""
 vnc_tls=""
 vnc_sasl=""
+jpeg="yes"
 vnc_jpeg=""
 vnc_png=""
 vnc_thread="no"
@@ -172,6 +173,8 @@ blobs="yes"
 pkgversion=""
 check_utests="no"
 user_pie="no"
+gles2="no"
+gles2_static="no"
 zero_malloc=""
 trace_backend="nop"
 trace_file="trace"
@@ -583,7 +586,6 @@ for opt do
   ;;
   --enable-jpeg) jpeg="yes"
   ;;
-
   --disable-vnc-tls) vnc_tls="no"
   ;;
   --enable-vnc-tls) vnc_tls="yes"
@@ -731,6 +733,16 @@ for opt do
   ;;
   --enable-docs) docs="yes"
   ;;
+  --enable-gles2) gles2="yes"; gles2dir="/usr";
+  ;;
+  --disable-gles2) gles2="no";
+  ;;
+  --enable-gles2-static) gles2="yes"; gles2_static="yes" gles2dir="/usr";
+  ;;
+  --disable-gles2-static) gles2_static="no";
+  ;;
+  --gles2dir=*) gles2dir="$optarg"
+  ;;
   --disable-vhost-net) vhost_net="no"
   ;;
   --enable-vhost-net) vhost_net="yes"
@@ -859,6 +871,8 @@ echo "  --disable-werror         disable compilation abort on warning"
 echo "  --disable-sdl            disable SDL"
 echo "  --enable-sdl             enable SDL"
 echo "  --enable-cocoa           enable COCOA (Mac OS X only)"
+echo "  --disable-jpeg           disable JPEG support"
+echo "  --enable-jpeg            enable JPEG support"
 echo "  --audio-drv-list=LIST    set audio drivers list:"
 echo "                           Available drivers: $audio_possible_drivers"
 echo "  --audio-card-list=LIST   set list of emulated audio cards [$audio_card_list]"
@@ -933,6 +947,9 @@ echo "  --disable-blobs          disable installing provided firmware blobs"
 echo "  --kerneldir=PATH         look for kernel includes in PATH"
 echo "  --enable-docs            enable documentation build"
 echo "  --disable-docs           disable documentation build"
+echo "  --enable-gles2           ARM target: Enable OpenGL ES 2.0 hardware accelerator"
+echo "  --enable-gles2-static    ARM target: Link OpenGL ES 2.0 hardware accelerator statically"
+echo "  --gles2dir=PATH          prefix where dgles2 is installed"
 echo "  --disable-vhost-net      disable vhost-net acceleration support"
 echo "  --enable-vhost-net       enable vhost-net acceleration support"
 echo "  --enable-trace-backend=B Set trace backend"
@@ -1342,24 +1359,34 @@ EOF
 fi
 
 ##########################################
-# VNC JPEG detection
-if test "$vnc_jpeg" != "no" ; then
+# JPEG detection
+if test "$jpeg" != "no"; then
 cat > $TMPC <<EOF
 #include <stdio.h>
 #include <jpeglib.h>
 int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
 EOF
-    vnc_jpeg_cflags=""
-    vnc_jpeg_libs="-ljpeg"
-  if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
-    vnc_jpeg=yes
-    libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+    jpeg_cflags=""
+    jpeg_libs="-ljpeg"
+  if compile_prog "$jpeg_cflags" "$jpeg_libs" ; then
+    jpeg=yes
+    if test "$vnc_jpeg" != "no"; then
+      vnc_jpeg=yes
+    fi
+    libs_softmmu="$jpeg_libs $libs_softmmu"
   else
-    if test "$vnc_jpeg" = "yes" ; then
-      feature_not_found "vnc-jpeg"
+    if test "$jpeg" = "yes" ; then
+      feature_not_found "jpeg"
     fi
+    jpeg=no
     vnc_jpeg=no
   fi
+elif test "$vnc_jpeg" != "no"; then
+    echo
+    echo "Error: VNC JPEG was requested but JPEG was disabled"
+    echo "Please either enable JPEG with '--enable-jpeg' or disable VNC JPEG with '--disable-vnc-jpeg'."
+    echo
+    exit 1
 fi
 
 ##########################################
@@ -2525,6 +2552,7 @@ if test "$darwin" = "yes" ; then
     echo "Cocoa support     $cocoa"
 fi
 echo "SDL support       $sdl"
+echo "JPEG support      $jpeg"
 echo "curses support    $curses"
 echo "curl support      $curl"
 echo "check support     $check_utests"
@@ -2560,6 +2588,9 @@ echo "Install blobs     $blobs"
 echo "KVM support       $kvm"
 echo "fdt support       $fdt"
 echo "preadv support    $preadv"
+echo "gles2 support     $gles2"
+echo "gles2 static      $gles2_static"
+echo "dgles2 prefix     $gles2dir"
 echo "fdatasync         $fdatasync"
 echo "madvise           $madvise"
 echo "posix_madvise     $posix_madvise"
@@ -2692,6 +2723,10 @@ echo "CONFIG_BDRV_WHITELIST=$block_drv_whitelist" >> $config_host_mak
 if test "$mixemu" = "yes" ; then
   echo "CONFIG_MIXEMU=y" >> $config_host_mak
 fi
+if test "$jpeg" = "yes" ; then
+  echo "CONFIG_JPEG=y" >> $config_host_mak
+  echo "JPEG_CFLAGS=$jpeg_cflags" >> $config_host_mak
+fi
 if test "$vnc_tls" = "yes" ; then
   echo "CONFIG_VNC_TLS=y" >> $config_host_mak
   echo "VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_host_mak
@@ -2851,10 +2886,16 @@ fi
 if test "$posix_madvise" = "yes" ; then
   echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
 fi
-
 if test "$spice" = "yes" ; then
   echo "CONFIG_SPICE=y" >> $config_host_mak
 fi
+if test "$gles2" = "yes" ; then
+  echo "CONFIG_GLES2=y" >> $config_host_mak
+if test "$gles2_static" = "yes" ; then
+  echo "CONFIG_GLES2_STATIC=y" >> $config_host_mak
+fi
+  echo "DGLES2=$gles2dir" >> $config_host_mak
+fi
 
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
index ed88432..9830bd7 100644 (file)
--- a/console.h
+++ b/console.h
@@ -19,7 +19,8 @@
 /* in ms */
 #define GUI_REFRESH_INTERVAL 30
 
-typedef void QEMUPutKBDEvent(void *opaque, int keycode);
+/* Keyboard events handlers should return zero if they handled the event */
+typedef int  QEMUPutKBDEvent(void *opaque, int keycode);
 typedef void QEMUPutLEDEvent(void *opaque, int ledstate);
 typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state);
 
@@ -41,10 +42,20 @@ typedef struct QEMUPutLEDEntry {
     QTAILQ_ENTRY(QEMUPutLEDEntry) next;
 } QEMUPutLEDEntry;
 
-void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque);
-void qemu_remove_kbd_event_handler(void);
-void qemu_add_ps2kbd_event_handler(QEMUPutKBDEvent *func, void *opaque);
-void qemu_remove_ps2kbd_event_handler(void);
+typedef struct QEMUPutKbdEntry {
+    char *qemu_put_kbd_name;
+    QEMUPutKBDEvent *qemu_put_kbd_event;
+    void *qemu_put_kbd_event_opaque;
+    int index;
+
+    QTAILQ_ENTRY(QEMUPutKbdEntry) node;
+} QEMUPutKbdEntry;
+
+QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func,
+                                            void *opaque,
+                                            const char *name);
+void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry);
+void qemu_activate_keyboard_event_handler(QEMUPutKbdEntry *entry);
 QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
                                                 void *opaque, int absolute,
                                                 const char *name);
@@ -79,6 +90,10 @@ void do_info_mice_print(Monitor *mon, const QObject *data);
 void do_info_mice(Monitor *mon, QObject **ret_data);
 void do_mouse_set(Monitor *mon, const QDict *qdict);
 
+void do_info_keyboard_print(Monitor *mon, const QObject *data);
+void do_info_keyboard(Monitor *mon, QObject **ret_data);
+int do_keyboard_set(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
 /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
    constants) */
 #define QEMU_KEY_ESC1(c) ((c) | 0xe100)
index 8d1174f..ea78f4e 100644 (file)
@@ -2,6 +2,8 @@
 
 include pci.mak
 CONFIG_GDBSTUB_XML=y
+CONFIG_USB_OHCI=y
+CONFIG_USB_EHCI=y
 CONFIG_ISA_MMIO=y
 CONFIG_NAND=y
 CONFIG_ECC=y
index 99b30f6..ab39591 100644 (file)
--- a/hw/adb.c
+++ b/hw/adb.c
@@ -153,7 +153,7 @@ static const uint8_t pc_to_adb_keycode[256] = {
   0,  0,  0,  0,  0, 95,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 };
 
-static void adb_kbd_put_keycode(void *opaque, int keycode)
+static int adb_kbd_put_keycode(void *opaque, int keycode)
 {
     ADBDevice *d = opaque;
     KBDState *s = d->opaque;
@@ -164,6 +164,7 @@ static void adb_kbd_put_keycode(void *opaque, int keycode)
             s->wptr = 0;
         s->count++;
     }
+    return 0;
 }
 
 static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
@@ -304,7 +305,7 @@ void adb_kbd_init(ADBBusState *bus)
     s = qemu_mallocz(sizeof(KBDState));
     d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request,
                             adb_kbd_reset, s);
-    qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
+    qemu_add_kbd_event_handler(adb_kbd_put_keycode, d, "adb");
     register_savevm(NULL, "adb_kbd", -1, 1, adb_kbd_save,
                     adb_kbd_load, s);
 }
diff --git a/hw/ak8973.c b/hw/ak8973.c
new file mode 100644 (file)
index 0000000..bed951a
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * AK8973 Compass Emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#include "i2c-addressable.h"
+#include "ak8973.h"
+
+//#define DEBUG
+
+#define AK8973_ST               0xC0
+#define AK8973_TMPS             0xC1
+#define AK8973_H1X              0xC2
+#define AK8973_H1Y              0xC3
+#define AK8973_H1Z              0xC4
+
+#define AK8973_MS1              0xE0
+#define AK8973_HXDA             0xE1
+#define AK8973_HYDA             0xE2
+#define AK8973_HZDA             0xE3
+#define AK8973_HXGA             0xE4
+#define AK8973_HYGA             0xE5
+#define AK8973_HZGA             0xE6
+
+#define AK8973_ETS              0x62
+#define AK8973_EVIR             0x63
+#define AK8973_EIHE             0x64
+#define AK8973_ETST             0x65
+#define AK8973_EHXGA            0x66
+#define AK8973_EHYGA            0x67
+#define AK8973_EHZGA            0x68
+#define AK8973_WRAL1            0x60
+
+
+typedef struct AK8973State {
+    I2CAddressableState i2c_addressable;
+
+    uint8_t status;
+    uint8_t x;
+    uint8_t y;
+    uint8_t z;
+       uint8_t temp;
+    uint8_t ms1;
+    uint8_t dac[3];
+    uint8_t gain[3];
+    qemu_irq irq;
+} AK8973State;
+
+
+static void ak8973_reset(DeviceState *d)
+{
+    AK8973State *s =
+        FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+    s->status  = 0;
+    /* Random coordinates */
+    s->x       = 10;
+    s->y       = 20;
+    s->z       = 30;
+
+    /* 20 degree Celsius */
+       s->temp    = 0x90;
+
+    /* EEPROM data-write disable / Powerdown mode */
+    s->ms1     = 0x3;
+
+    /* TODO: get the defaults */
+    s->dac[0]  = 0;
+    s->dac[1]  = 0;
+    s->dac[2]  = 0;
+    s->gain[0] = 0;
+    s->gain[1] = 0;
+    s->gain[2] = 0;
+
+    return;
+}
+
+static uint8_t ak8973_read(void *opaque, uint32_t address, uint8_t offset)
+{
+    struct AK8973State *s = (struct AK8973State *)opaque;
+    uint32_t ret = 0, index = address + offset;
+
+    switch (index) {
+    case AK8973_ST:
+        ret = s->status;
+        break;
+    case AK8973_TMPS:
+        /* IRQ is lowered when any of data registers are read */
+        qemu_irq_lower(s->irq);
+        s->status &= ~1;
+        ret = s->temp;
+        break;
+    case AK8973_H1X:
+        qemu_irq_lower(s->irq);
+        s->status &= ~1;
+        ret = s->x;
+        break;
+    case AK8973_H1Y:
+        qemu_irq_lower(s->irq);
+        s->status &= ~1;
+        ret = s->y;
+        break;
+    case AK8973_H1Z:
+        qemu_irq_lower(s->irq);
+        s->status &= ~1;
+        ret = s->z;
+        break;
+    case AK8973_MS1:
+        ret = s->ms1;
+        break;
+    case AK8973_HXDA:
+    case AK8973_HYDA:
+    case AK8973_HZDA:
+        ret = s->dac[index - AK8973_HXDA];
+        break;
+    case AK8973_HXGA:
+    case AK8973_HYGA:
+    case AK8973_HZGA:
+        ret = s->gain[index - AK8973_HXGA];
+        break;
+    case AK8973_ETS:
+    case AK8973_EVIR:
+    case AK8973_EIHE:
+    case AK8973_ETST:
+    case AK8973_EHXGA:
+    case AK8973_EHYGA:
+    case AK8973_EHZGA:
+    case AK8973_WRAL1:
+        if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) != 0xA8) {
+            /* TODO: implement EEPROM reading */
+            ret = 0;
+        } else {
+            ret = 0;
+        }
+        break;
+    default:
+        hw_error("ak8973: bad read offset 0x%x\n", index);
+    }
+
+#ifdef DEBUG
+    printf("ak8973_read IDX = 0x%x, Data = 0x%x\n", index, ret);
+#endif
+
+    return ret;
+}
+
+static void ak8973_write(void *opaque, uint32_t address, uint8_t offset,
+                         uint8_t val)
+{
+    struct AK8973State *s = (struct AK8973State *)opaque;
+    uint32_t index = address + offset;
+
+#ifdef DEBUG
+    printf("ak8973_write IDX = 0x%x, Data = 0x%x\n", index, val);
+#endif
+
+    switch (index) {
+    case AK8973_MS1:
+        if ((val & 3) == 2) {
+            /* EEPROM access mode */
+            /* TODO: implement this mode */
+            s->ms1 = val;
+        } else {
+            /* All other mode setting finishes in power-down mode */
+            s->ms1 |= 3;
+        }
+        if ((val & 3) == 0) {
+            /* Measurement mode */
+            qemu_irq_raise(s->irq);
+            s->status |= 1;
+            /* TODO: change measurement registers accordingly */
+        }
+        break;
+    case AK8973_HXDA:
+    case AK8973_HYDA:
+    case AK8973_HZDA:
+        /* TODO: implement DAC changes influence on reported data */
+        s->dac[index - AK8973_HXDA] = val;
+        break;
+    case AK8973_HXGA:
+    case AK8973_HYGA:
+    case AK8973_HZGA:
+        /* TODO: implement gain changes influence on reported data */
+        s->gain[index - AK8973_HXGA] = val;
+        break;
+    case AK8973_ETS:
+    case AK8973_EVIR:
+    case AK8973_EIHE:
+    case AK8973_ETST:
+    case AK8973_EHXGA:
+    case AK8973_EHYGA:
+    case AK8973_EHZGA:
+    case AK8973_WRAL1:
+        if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) == 0xA8) {
+            /* TODO: implement EEPROM writing */
+        }
+        break;
+    default:
+        hw_error("ak8973: bad write offset 0x%x\n", index);
+    }
+}
+
+static void ak8973_save(QEMUFile *f, void *opaque)
+{
+    struct AK8973State *s = (struct AK8973State *)opaque;
+
+    qemu_put_8s(f, &s->status);
+    qemu_put_8s(f, &s->ms1);
+    qemu_put_buffer(f, s->dac, 3);
+    qemu_put_buffer(f, s->gain, 3);
+}
+
+static int ak8973_load(QEMUFile *f, void *opaque, int version_id)
+{
+    struct AK8973State *s = (struct AK8973State *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_8s(f, &s->status);
+    qemu_get_8s(f, &s->ms1);
+    qemu_get_buffer(f, s->dac, 3);
+    qemu_get_buffer(f, s->gain, 3);
+
+    return 0;
+}
+
+void ak8973_update(DeviceState *dev,
+                   uint8_t x, uint8_t y, uint8_t z, uint8_t temp)
+{
+       AK8973State *s =
+        FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(dev));
+    s->x = x;
+    s->y = y;
+    s->z = z;
+    s->temp = temp;
+#ifdef DEBUG
+    printf("compass update : %d %d %d %d\n", x, y, z, temp);
+#endif
+}
+
+
+static int ak8973_init(I2CAddressableState *s)
+{
+    AK8973State *t = FROM_I2CADDR_SLAVE(AK8973State, s);
+
+    /* Set irq address */
+    qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+
+    ak8973_reset(&s->i2c.qdev);
+
+    register_savevm(&s->i2c.qdev, "ak8973", -1, 1,
+                    ak8973_save, ak8973_load, s);
+
+    return 0;
+}
+
+static I2CAddressableDeviceInfo ak8973_info = {
+    .i2c.qdev.name = "ak8973",
+    .i2c.qdev.size = sizeof(AK8973State),
+    .i2c.qdev.reset = ak8973_reset,
+    .init = ak8973_init,
+    .read = ak8973_read,
+    .write = ak8973_write,
+    .size = 1,
+    .rev = 0
+};
+
+static void ak8973_register_devices(void)
+{
+    i2c_addressable_register_device(&ak8973_info);
+}
+
+device_init(ak8973_register_devices)
diff --git a/hw/ak8973.h b/hw/ak8973.h
new file mode 100644 (file)
index 0000000..366b2a2
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * AK8973 Compass Emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+void ak8973_update(DeviceState *dev,
+                   uint8_t x, uint8_t y, uint8_t z, uint8_t temp);
index 010acb4..e2d8397 100644 (file)
@@ -31,6 +31,7 @@ struct arm_boot_info {
     target_phys_addr_t smp_priv_base;
     int nb_cpus;
     int board_id;
+    int revision;
     int (*atag_board)(struct arm_boot_info *info, void *p);
     /* Used internally by arm_boot.c */
     int is_linux;
index 620550b..b6ec911 100644 (file)
@@ -97,6 +97,12 @@ static void set_kernel_args(struct arm_boot_info *info,
         cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
         p += atag_board_len;
     }
+    if (info->revision) {
+        /* ATAG_REVISION */
+        WRITE_WORD(p, 3);
+        WRITE_WORD(p, 0x54410007);
+        WRITE_WORD(p, info->revision);
+    }
     /* ATAG_END */
     WRITE_WORD(p, 0);
     WRITE_WORD(p, 0);
index 4ee99a1..7ca2b39 100644 (file)
@@ -152,11 +152,12 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level)
         return bitbang_i2c_ret(i2c, data);
 
     case SENDING_ACK:
-        i2c->state = RECEIVING_BIT7;
         if (data != 0) {
             DPRINTF("NACKED\n");
+            i2c->state = STOPPED;
             i2c_nack(i2c->bus);
         } else {
+            i2c->state = RECEIVING_BIT7;
             DPRINTF("ACKED\n");
         }
         return bitbang_i2c_ret(i2c, 1);
index 6f0f0d7..5d6edb1 100644 (file)
@@ -31,6 +31,7 @@ typedef struct QEMUMachine {
     struct QEMUMachine *next;
 } QEMUMachine;
 
+int qemu_arch_is_arm(void);
 int qemu_register_machine(QEMUMachine *m);
 
 extern QEMUMachine *current_machine;
index edf48f6..166526b 100644 (file)
@@ -1656,6 +1656,12 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size
     static const uint8_t broadcast_macaddr[6] =
         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
+    /* TODO: check multiple IA bit. */
+    if (s->configuration[20] & BIT(6)) {
+        missing("Multiple IA bit");
+        return -1;
+    }
+
     if (s->configuration[8] & 0x80) {
         /* CSMA is disabled. */
         logout("%p received while CSMA is disabled\n", s);
index f6fd919..682409d 100644 (file)
--- a/hw/escc.c
+++ b/hw/escc.c
@@ -344,10 +344,9 @@ static void escc_reset(DeviceState *d)
 static inline void set_rxint(ChannelState *s)
 {
     s->rxint = 1;
-    /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
-       than chn_a rx/tx/special_condition service*/
-    s->rxint_under_svc = 1;
-    if (s->chn == chn_a) {
+    if (!s->txint_under_svc) {
+        s->rxint_under_svc = 1;
+        if (s->chn == chn_a) {
         s->rregs[R_INTR] |= INTR_RXINTA;
         if (s->wregs[W_MINTR] & MINTR_STATUSHI)
             s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
@@ -360,6 +359,11 @@ static inline void set_rxint(ChannelState *s)
         else
             s->rregs[R_IVEC] = IVEC_LORXINTB;
     }
+    }
+    if (s->chn == chn_a)
+        s->rregs[R_INTR] |= INTR_RXINTA;
+    else
+        s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
     escc_update_irq(s);
 }
 
@@ -762,7 +766,7 @@ static const uint8_t e0_keycodes[128] = {
     1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
 };
 
-static void sunkbd_event(void *opaque, int ch)
+static int sunkbd_event(void *opaque, int ch)
 {
     ChannelState *s = opaque;
     int release = ch & 0x80;
@@ -773,26 +777,26 @@ static void sunkbd_event(void *opaque, int ch)
     case 58: // Caps lock press
         s->caps_lock_mode ^= 1;
         if (s->caps_lock_mode == 2)
-            return; // Drop second press
+            return 0; // Drop second press
         break;
     case 69: // Num lock press
         s->num_lock_mode ^= 1;
         if (s->num_lock_mode == 2)
-            return; // Drop second press
+            return 0; // Drop second press
         break;
     case 186: // Caps lock release
         s->caps_lock_mode ^= 2;
         if (s->caps_lock_mode == 3)
-            return; // Drop first release
+            return 0; // Drop first release
         break;
     case 197: // Num lock release
         s->num_lock_mode ^= 2;
         if (s->num_lock_mode == 3)
-            return; // Drop first release
+            return 0; // Drop first release
         break;
     case 0xe0:
         s->e0_mode = 1;
-        return;
+        return 0;
     default:
         break;
     }
@@ -804,6 +808,7 @@ static void sunkbd_event(void *opaque, int ch)
     }
     KBD_DPRINTF("Translated keycode %2.2x\n", ch);
     put_queue(s, ch | release);
+    return 0;
 }
 
 static void handle_kbd_command(ChannelState *s, int val)
@@ -928,7 +933,8 @@ static int escc_init1(SysBusDevice *dev)
                                      "QEMU Sun Mouse");
     }
     if (s->chn[1].type == kbd) {
-        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1],
+                "QEMU Sun Keyboard");
     }
 
     return 0;
index d7d103e..ecac39d 100644 (file)
@@ -39,9 +39,19 @@ uint8_t nand_getio(NANDFlashState *s);
 /* onenand.c */
 void onenand_base_update(void *opaque, target_phys_addr_t new);
 void onenand_base_unmap(void *opaque);
-void *onenand_init(uint32_t id, int regshift, qemu_irq irq);
+/* id = XXYYZZ (XX - Manufacturer, YY - device properties, ZZ - version)
+   regshift = 1 in most cases
+   page_size = 10 (for 1kB-page chips), 11 (for 2kB-page chips),
+   12 (for 4kB-page chips)
+   data_buf = 2 in most cases */
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq,
+                   int page_size, int data_buf);
 void *onenand_raw_otp(void *opaque);
 
+#define ONENAND_1KB_PAGE 10
+#define ONENAND_2KB_PAGE 11
+#define ONENAND_4KB_PAGE 12
+
 /* ecc.c */
 typedef struct {
     uint8_t cp;                /* Column parity */
diff --git a/hw/gles1_calls.c b/hw/gles1_calls.c
new file mode 100644 (file)
index 0000000..7bd2cd3
--- /dev/null
@@ -0,0 +1,1396 @@
+/* GLES v1 implementation.
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ *
+ * 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) any later version of the License.
+ *
+ * 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 "gles2.h"
+#include "EGL/degl.h"
+#include "GLES/gl.h"
+
+#include "gles2_types.h"
+
+
+#define gles1_count_glClipPlanef  4
+#define gles1_count_glClipPlanex  4
+#define gles1_count_glLoadMatrixf 16
+#define gles1_count_glLoadMatrixx 16
+#define gles1_count_glMultMatrixf 16
+#define gles1_count_glMultMatrixx 16
+
+
+// Numbers from c->array's end
+#define gles1_num_glColorPointer    1
+#define gles1_num_glNormalPointer   2
+#define gles1_num_glTexCoordPointer 3
+#define gles1_num_glVertexPointer   4
+
+#define gles1_size_glNormalPointer 3
+
+//for debug only
+void checkGLESError(void)
+{
+    GLenum error;
+    if ((error = glGetError()) != 0) {
+        GLES2_PRINT("Error after call 0x%x!\n", error);
+    }
+    else {
+        GLES2_PRINT("Ok after call!\n");
+    }
+}
+
+static void gles1_apply_glColorPointer(gles2_Array *va)
+{
+    glColorPointer(va->size, va->type,
+        va->real_stride, va->ptr);
+    GLenum error;
+
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        GLES2_PRINT("glColorPointer(%d, 0x%x, %d, %p\n)"
+            " failed with 0x%x!\n", va->size, va->type,
+            va->stride, va->ptr, error);
+    }
+}
+
+static void gles1_apply_glNormalPointer(gles2_Array *va)
+{
+    glNormalPointer(va->type, va->real_stride, va->ptr);
+    GLenum error;
+
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        GLES2_PRINT("glNormalPointer(0x%x, %d, %p\n)"
+            " failed with 0x%x!\n", va->type,
+            va->stride, va->ptr, error);
+    }
+}
+
+static void gles1_apply_glTexCoordPointer(gles2_Array *va)
+{
+    glTexCoordPointer(va->size, va->type,
+        va->real_stride, va->ptr);
+    GLenum error;
+
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        GLES2_PRINT("glTexCoordPointer(%d, 0x%x, %d, %p\n)"
+            " failed with 0x%x!\n", va->size, va->type,
+            va->stride, va->ptr, error);
+    }
+}
+
+static void gles1_apply_glVertexPointer(gles2_Array *va)
+{
+    glVertexPointer(va->size, va->type,
+        va->real_stride, va->ptr);
+    GLenum error;
+
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        GLES2_PRINT("glVertexPointer(%d, 0x%x, %d, %p\n)"
+            " failed with 0x%x!\n", va->size, va->type,
+            va->stride, va->ptr, error);
+    }
+}
+
+
+unsigned gles1_glGetCount(TGLenum pname)
+{
+    unsigned count;
+    switch(pname) {
+        case GL_MAX_TEXTURE_UNITS:
+
+        case GL_ALPHA_BITS:
+        case GL_ALPHA_SCALE:
+        case GL_ALPHA_TEST:
+        case GL_ALPHA_TEST_FUNC:
+        case GL_ALPHA_TEST_REF:
+        case GL_BLEND:
+        case GL_BLEND_DST:
+        case GL_BLEND_SRC:
+        case GL_BLUE_BITS:
+        case GL_CLIP_PLANE0:
+        case GL_CLIP_PLANE1:
+        case GL_CLIP_PLANE2:
+        case GL_CLIP_PLANE3:
+        case GL_CLIP_PLANE4:
+        case GL_CLIP_PLANE5:
+        case GL_COLOR_MATERIAL:
+        case GL_CULL_FACE:
+        case GL_CULL_FACE_MODE:
+        case GL_DEPTH_BITS:
+        case GL_DEPTH_CLEAR_VALUE:
+        case GL_DEPTH_FUNC:
+        case GL_DEPTH_TEST:
+        case GL_DEPTH_WRITEMASK:
+        case GL_DITHER:
+        case GL_FOG:
+        case GL_FOG_DENSITY:
+        case GL_FOG_END:
+        case GL_FOG_HINT:
+        case GL_FOG_MODE:
+        case GL_FOG_START:
+        case GL_FRONT_FACE:
+        case GL_GREEN_BITS:
+        case GL_LIGHT0:
+        case GL_LIGHT1:
+        case GL_LIGHT2:
+        case GL_LIGHT3:
+        case GL_LIGHT4:
+        case GL_LIGHT5:
+        case GL_LIGHT6:
+        case GL_LIGHT7:
+        case GL_LIGHTING:
+        case GL_LIGHT_MODEL_TWO_SIDE:
+        case GL_LINE_SMOOTH:
+        case GL_LINE_SMOOTH_HINT:
+        case GL_LINE_WIDTH:
+        case GL_COLOR_LOGIC_OP:
+        case GL_LOGIC_OP_MODE:
+        case GL_MATRIX_MODE:
+        case GL_MAX_CLIP_PLANES:
+        case GL_MAX_LIGHTS:
+        case GL_MAX_MODELVIEW_STACK_DEPTH:
+        case GL_MAX_PROJECTION_STACK_DEPTH:
+        case GL_MAX_TEXTURE_SIZE:
+        case GL_MAX_TEXTURE_STACK_DEPTH:
+        case GL_MODELVIEW_STACK_DEPTH:
+        case GL_NORMALIZE:
+        case GL_PACK_ALIGNMENT:
+        case GL_PERSPECTIVE_CORRECTION_HINT:
+        case GL_POINT_SIZE:
+        case GL_POINT_SMOOTH:
+        case GL_POINT_SMOOTH_HINT:
+        case GL_POLYGON_OFFSET_FACTOR:
+        case GL_POLYGON_OFFSET_UNITS:
+        case GL_POLYGON_OFFSET_FILL:
+        case GL_PROJECTION_STACK_DEPTH:
+        case GL_RED_BITS:
+        case GL_RESCALE_NORMAL:
+        case GL_SCISSOR_TEST:
+        case GL_SHADE_MODEL:
+        case GL_STENCIL_BITS:
+        case GL_STENCIL_CLEAR_VALUE:
+        case GL_STENCIL_FAIL:
+        case GL_STENCIL_FUNC:
+        case GL_STENCIL_PASS_DEPTH_FAIL:
+        case GL_STENCIL_PASS_DEPTH_PASS:
+        case GL_STENCIL_REF:
+        case GL_STENCIL_TEST:
+        case GL_STENCIL_VALUE_MASK:
+        case GL_STENCIL_WRITEMASK:
+        case GL_SUBPIXEL_BITS:
+        case GL_TEXTURE_2D:
+        case GL_TEXTURE_BINDING_2D:
+        case GL_TEXTURE_STACK_DEPTH:
+        case GL_UNPACK_ALIGNMENT:
+        case GL_VERTEX_ARRAY:
+        case GL_VERTEX_ARRAY_SIZE:
+        case GL_VERTEX_ARRAY_TYPE:
+        case GL_VERTEX_ARRAY_STRIDE:
+        case GL_NORMAL_ARRAY:
+        case GL_NORMAL_ARRAY_TYPE:
+        case GL_NORMAL_ARRAY_STRIDE:
+        case GL_COLOR_ARRAY:
+        case GL_COLOR_ARRAY_SIZE:
+        case GL_COLOR_ARRAY_TYPE:
+        case GL_COLOR_ARRAY_STRIDE:
+        case GL_TEXTURE_COORD_ARRAY:
+        case GL_TEXTURE_COORD_ARRAY_SIZE:
+        case GL_TEXTURE_COORD_ARRAY_TYPE:
+        case GL_TEXTURE_COORD_ARRAY_STRIDE:
+        case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
+        case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
+        case GL_SPOT_EXPONENT:
+        case GL_SPOT_CUTOFF:
+        case GL_CONSTANT_ATTENUATION:
+        case GL_LINEAR_ATTENUATION:
+        case GL_QUADRATIC_ATTENUATION:
+        case GL_SHININESS:
+        case GL_TEXTURE_ENV_MODE:
+        case GL_COMBINE_RGB:
+        case GL_COMBINE_ALPHA:
+        case GL_SRC0_RGB:
+        case GL_SRC1_RGB:
+        case GL_SRC2_RGB:
+        case GL_SRC0_ALPHA:
+        case GL_SRC1_ALPHA:
+        case GL_SRC2_ALPHA:
+        case GL_OPERAND0_RGB:
+        case GL_OPERAND1_RGB:
+        case GL_OPERAND2_RGB:
+        case GL_OPERAND0_ALPHA:
+        case GL_OPERAND1_ALPHA:
+        case GL_OPERAND2_ALPHA:
+        case GL_RGB_SCALE:
+        case GL_POINT_SIZE_MIN:
+        case GL_POINT_SIZE_MAX:
+        case GL_POINT_FADE_THRESHOLD_SIZE:
+          count = 1;
+          break;
+
+        case GL_DEPTH_RANGE:
+        case GL_ALIASED_LINE_WIDTH_RANGE:
+        case GL_MAX_VIEWPORT_DIMS:
+        case GL_ALIASED_POINT_SIZE_RANGE:
+          count = 2;
+          break;
+
+        case GL_CURRENT_NORMAL:
+        case GL_SPOT_DIRECTION:
+        case GL_POINT_DISTANCE_ATTENUATION:
+          count = 3;
+          break;
+
+        case GL_COLOR_CLEAR_VALUE:
+        case GL_COLOR_WRITEMASK:
+        case GL_CURRENT_COLOR:
+        case GL_CURRENT_TEXTURE_COORDS:
+        case GL_FOG_COLOR:
+        case GL_LIGHT_MODEL_AMBIENT:
+        case GL_SCISSOR_BOX:
+        case GL_VIEWPORT:
+        case GL_AMBIENT:
+        case GL_DIFFUSE:
+        case GL_SPECULAR:
+        case GL_EMISSION:
+        case GL_TEXTURE_ENV_COLOR:
+        case GL_POSITION:
+        case GL_AMBIENT_AND_DIFFUSE:
+          count = 4;
+          break;
+
+
+        case GL_MODELVIEW_MATRIX:
+        case GL_PROJECTION_MATRIX:
+        case GL_TEXTURE_MATRIX:
+          count = 16;
+          break;
+
+        default:
+            GLES2_PRINT("ERROR: Unknown pname 0x%x in glGet!\n", pname);
+            //exit(0);
+            count = 1;
+            break;
+    }
+
+    GLES2_PRINT("glGet(0x%x) -> %u!\n", pname, count);
+
+    return count;
+}
+
+
+GLES2_CB(glGetPointerv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+
+    Tptr res = 0;
+
+    switch (pname)
+    {
+    case GL_COLOR_ARRAY_POINTER:
+        res = c->arrays[c->narrays - gles1_num_glColorPointer].tptr;
+        break;
+    case GL_NORMAL_ARRAY_POINTER:
+        res = c->arrays[c->narrays - gles1_num_glNormalPointer].tptr;
+        break;
+    case GL_TEXTURE_COORD_ARRAY_POINTER:
+        res = c->arrays[c->narrays - gles1_num_glTexCoordPointer].tptr;
+        break;
+    case GL_VERTEX_ARRAY_POINTER:
+        res = c->arrays[c->narrays - gles1_num_glVertexPointer].tptr;
+        break;
+
+    default:
+        GLES2_PRINT("ERROR: Unknown pname 0x%x in glGetPointerv!\n", pname);
+        //exit(0);
+        break;
+    }
+
+    GLES2_BARRIER_RET;
+    gles2_put_Tptr(s, paramsp, res);
+}
+
+GLES2_CB(glEnableClientState)
+{
+    GLES2_ARG(TGLenum, array);
+    GLES2_BARRIER_ARG_NORET;
+
+    switch (array)
+    {
+    case GL_COLOR_ARRAY:
+        c->arrays[c->narrays - gles1_num_glColorPointer].enabled = 1;
+        break;
+    case GL_NORMAL_ARRAY:
+        c->arrays[c->narrays - gles1_num_glNormalPointer].enabled = 1;
+        break;
+    case GL_TEXTURE_COORD_ARRAY:
+        c->arrays[c->narrays - gles1_num_glTexCoordPointer].enabled = 1;
+        break;
+    case GL_VERTEX_ARRAY:
+        c->arrays[c->narrays - gles1_num_glVertexPointer].enabled = 1;
+        break;
+
+    default:
+        GLES2_PRINT("ERROR: Unknown array 0x%x in glDisableClientState!\n", array);
+        exit(0);
+        break;
+    }
+
+    glEnableClientState(array);
+}
+
+GLES2_CB(glDisableClientState)
+{
+    GLES2_ARG(TGLenum, array);
+    GLES2_BARRIER_ARG_NORET;
+
+    switch (array)
+    {
+    case GL_COLOR_ARRAY:
+        c->arrays[c->narrays - gles1_num_glColorPointer].enabled = 0;
+        break;
+    case GL_NORMAL_ARRAY:
+        c->arrays[c->narrays - gles1_num_glNormalPointer].enabled = 0;
+        break;
+    case GL_TEXTURE_COORD_ARRAY:
+        c->arrays[c->narrays - gles1_num_glTexCoordPointer].enabled = 0;
+        break;
+    case GL_VERTEX_ARRAY:
+        c->arrays[c->narrays - gles1_num_glVertexPointer].enabled = 0;
+        break;
+
+    default:
+        GLES2_PRINT("ERROR: Unknown array 0x%x in glDisableClientState!\n", array);
+        exit(0);
+        break;
+    }
+
+    glDisableClientState(array);
+}
+
+
+//***************** Has been generated! *******************
+
+GLES2_CB(glAlphaFunc)
+{
+    GLES2_ARG(TGLenum, func);
+    GLES2_ARG(TGLclampf, ref);
+    GLES2_BARRIER_ARG_NORET;
+
+    glAlphaFunc(func, ref);
+}
+
+GLES2_CB(glClipPlanef)
+{
+    GLES2_ARG(TGLenum, plane);
+    GLES2_ARG(Tptr, equationp);
+    unsigned count = gles1_count_glClipPlanef;
+    GLfloat equation [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        equation[i] = gles2_get_TGLfloat(s, equationp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glClipPlanef(plane, equation);
+}
+
+GLES2_CB(glColor4f)
+{
+    GLES2_ARG(TGLfloat, red);
+    GLES2_ARG(TGLfloat, green);
+    GLES2_ARG(TGLfloat, blue);
+    GLES2_ARG(TGLfloat, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glColor4f(red, green, blue, alpha);
+}
+
+GLES2_CB(glFogf)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFogf(pname, param);
+}
+
+GLES2_CB(glFogfv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glFogfv(pname, params);
+}
+
+GLES2_CB(glFrustumf)
+{
+    GLES2_ARG(TGLfloat, left);
+    GLES2_ARG(TGLfloat, right);
+    GLES2_ARG(TGLfloat, bottom);
+    GLES2_ARG(TGLfloat, top);
+    GLES2_ARG(TGLfloat, zNear);
+    GLES2_ARG(TGLfloat, zFar);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFrustumf(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glGetClipPlanef)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, eqnp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat eqn [16];
+
+    glGetClipPlanef(pname, eqn);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, eqnp + i*sizeof(TGLfloat), eqn[i]);
+    }
+}
+
+GLES2_CB(glGetLightfv)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+
+    glGetLightfv(light, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetMaterialfv)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+
+    glGetMaterialfv(face, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetTexEnvfv)
+{
+    GLES2_ARG(TGLenum, env);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+
+    glGetTexEnvfv(env, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glLightModelf)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightModelf(pname, param);
+}
+
+GLES2_CB(glLightModelfv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightModelfv(pname, params);
+}
+
+GLES2_CB(glLightf)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightf(light, pname, param);
+}
+
+GLES2_CB(glLightfv)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightfv(light, pname, params);
+}
+
+GLES2_CB(glLoadMatrixf)
+{
+    GLES2_ARG(Tptr, mp);
+    unsigned count = gles1_count_glLoadMatrixf;
+    GLfloat m [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        m[i] = gles2_get_TGLfloat(s, mp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLoadMatrixf(m);
+}
+
+GLES2_CB(glMaterialf)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glMaterialf(face, pname, param);
+}
+
+GLES2_CB(glMaterialfv)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glMaterialfv(face, pname, params);
+}
+
+GLES2_CB(glMultMatrixf)
+{
+    GLES2_ARG(Tptr, mp);
+    unsigned count = gles1_count_glMultMatrixf;
+    GLfloat m [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        m[i] = gles2_get_TGLfloat(s, mp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glMultMatrixf(m);
+}
+
+GLES2_CB(glMultiTexCoord4f)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLfloat, ps);
+    GLES2_ARG(TGLfloat, t);
+    GLES2_ARG(TGLfloat, r);
+    GLES2_ARG(TGLfloat, q);
+    GLES2_BARRIER_ARG_NORET;
+
+    glMultiTexCoord4f(target, ps, t, r, q);
+}
+
+GLES2_CB(glNormal3f)
+{
+    GLES2_ARG(TGLfloat, nx);
+    GLES2_ARG(TGLfloat, ny);
+    GLES2_ARG(TGLfloat, nz);
+    GLES2_BARRIER_ARG_NORET;
+
+    glNormal3f(nx, ny, nz);
+}
+
+GLES2_CB(glOrthof)
+{
+    GLES2_ARG(TGLfloat, left);
+    GLES2_ARG(TGLfloat, right);
+    GLES2_ARG(TGLfloat, bottom);
+    GLES2_ARG(TGLfloat, top);
+    GLES2_ARG(TGLfloat, zNear);
+    GLES2_ARG(TGLfloat, zFar);
+    GLES2_BARRIER_ARG_NORET;
+
+    glOrthof(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glPointParameterf)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointParameterf(pname, param);
+}
+
+GLES2_CB(glPointParameterfv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointParameterfv(pname, params);
+}
+
+GLES2_CB(glPointSize)
+{
+    GLES2_ARG(TGLfloat, size);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointSize(size);
+}
+
+GLES2_CB(glRotatef)
+{
+    GLES2_ARG(TGLfloat, angle);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glRotatef(angle, x, y, z);
+}
+
+GLES2_CB(glScalef)
+{
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glScalef(x, y, z);
+}
+
+GLES2_CB(glTexEnvf)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnvf(target, pname, param);
+}
+
+GLES2_CB(glTexEnvfv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfloat params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnvfv(target, pname, params);
+}
+
+GLES2_CB(glTranslatef)
+{
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTranslatef(x, y, z);
+}
+
+GLES2_CB(glAlphaFuncx)
+{
+    GLES2_ARG(TGLenum, func);
+    GLES2_ARG(TGLclampx, ref);
+    GLES2_BARRIER_ARG_NORET;
+
+    glAlphaFuncx(func, ref);
+}
+
+GLES2_CB(glClearColorx)
+{
+    GLES2_ARG(TGLclampx, red);
+    GLES2_ARG(TGLclampx, green);
+    GLES2_ARG(TGLclampx, blue);
+    GLES2_ARG(TGLclampx, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClearColorx(red, green, blue, alpha);
+}
+
+GLES2_CB(glClearDepthx)
+{
+    GLES2_ARG(TGLclampx, depth);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClearDepthx(depth);
+}
+
+GLES2_CB(glClientActiveTexture)
+{
+    GLES2_ARG(TGLenum, texture);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClientActiveTexture(texture);
+}
+
+GLES2_CB(glClipPlanex)
+{
+    GLES2_ARG(TGLenum, plane);
+    GLES2_ARG(Tptr, equationp);
+    unsigned count = gles1_count_glClipPlanex;
+    GLfixed equation [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        equation[i] = gles2_get_TGLfixed(s, equationp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glClipPlanex(plane, equation);
+}
+
+GLES2_CB(glColor4ub)
+{
+    GLES2_ARG(TGLubyte, red);
+    GLES2_ARG(TGLubyte, green);
+    GLES2_ARG(TGLubyte, blue);
+    GLES2_ARG(TGLubyte, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glColor4ub(red, green, blue, alpha);
+}
+
+GLES2_CB(glColor4x)
+{
+    GLES2_ARG(TGLfixed, red);
+    GLES2_ARG(TGLfixed, green);
+    GLES2_ARG(TGLfixed, blue);
+    GLES2_ARG(TGLfixed, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glColor4x(red, green, blue, alpha);
+}
+
+GLES2_CB(glColorPointer)
+{
+    GLES2_ARG(TGLint, size);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(TGLsizei, stride);
+    GLES2_ARG(Tptr, pointerp);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Array glColorPointer at 0x%x\n", pointerp);
+
+    gles2_Array *va = c->arrays + (c->narrays - gles1_num_glColorPointer);
+    va->size = size;
+    va->type = type;
+    va->stride = stride;
+    va->tptr = pointerp;
+    va->apply = gles1_apply_glColorPointer;
+    va->enabled = 1;
+}
+
+GLES2_CB(glDepthRangex)
+{
+    GLES2_ARG(TGLclampx, zNear);
+    GLES2_ARG(TGLclampx, zFar);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDepthRangex(zNear, zFar);
+}
+
+GLES2_CB(glFogx)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFogx(pname, param);
+}
+
+GLES2_CB(glFogxv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glFogxv(pname, params);
+}
+
+GLES2_CB(glFrustumx)
+{
+    GLES2_ARG(TGLfixed, left);
+    GLES2_ARG(TGLfixed, right);
+    GLES2_ARG(TGLfixed, bottom);
+    GLES2_ARG(TGLfixed, top);
+    GLES2_ARG(TGLfixed, zNear);
+    GLES2_ARG(TGLfixed, zFar);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFrustumx(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glGetClipPlanex)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, eqnp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed eqn [16];
+
+    glGetClipPlanex(pname, eqn);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, eqnp + i*sizeof(TGLfixed), eqn[i]);
+    }
+}
+
+GLES2_CB(glGetFixedv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+
+    glGetFixedv(pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+    }
+}
+
+GLES2_CB(glGetLightxv)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+
+    glGetLightxv(light, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+    }
+}
+
+GLES2_CB(glGetMaterialxv)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+
+    glGetMaterialxv(face, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+    }
+}
+
+GLES2_CB(glGetTexEnviv)
+{
+    GLES2_ARG(TGLenum, env);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLint params [16];
+
+    glGetTexEnviv(env, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+    }
+}
+
+GLES2_CB(glGetTexEnvxv)
+{
+    GLES2_ARG(TGLenum, env);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+
+    glGetTexEnvxv(env, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+    }
+}
+
+GLES2_CB(glGetTexParameterxv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+
+    glGetTexParameterxv(target, pname, params);
+    GLES2_BARRIER_RET;
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+    }
+}
+
+GLES2_CB(glLightModelx)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightModelx(pname, param);
+}
+
+GLES2_CB(glLightModelxv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightModelxv(pname, params);
+}
+
+GLES2_CB(glLightx)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightx(light, pname, param);
+}
+
+GLES2_CB(glLightxv)
+{
+    GLES2_ARG(TGLenum, light);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLightxv(light, pname, params);
+}
+
+GLES2_CB(glLineWidthx)
+{
+    GLES2_ARG(TGLfixed, width);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLineWidthx(width);
+}
+
+GLES2_CB(glLoadIdentity)
+{
+    GLES2_BARRIER_ARG_NORET;
+
+    glLoadIdentity();
+}
+
+GLES2_CB(glLoadMatrixx)
+{
+    GLES2_ARG(Tptr, mp);
+    unsigned count = gles1_count_glLoadMatrixx;
+    GLfixed m [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        m[i] = gles2_get_TGLfixed(s, mp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glLoadMatrixx(m);
+}
+
+GLES2_CB(glLogicOp)
+{
+    GLES2_ARG(TGLenum, opcode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLogicOp(opcode);
+}
+
+GLES2_CB(glMaterialx)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glMaterialx(face, pname, param);
+}
+
+GLES2_CB(glMaterialxv)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glMaterialxv(face, pname, params);
+}
+
+GLES2_CB(glMatrixMode)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glMatrixMode(mode);
+}
+
+GLES2_CB(glMultMatrixx)
+{
+    GLES2_ARG(Tptr, mp);
+    unsigned count = gles1_count_glMultMatrixx;
+    GLfixed m [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        m[i] = gles2_get_TGLfixed(s, mp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glMultMatrixx(m);
+}
+
+GLES2_CB(glMultiTexCoord4x)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLfixed, ps);
+    GLES2_ARG(TGLfixed, t);
+    GLES2_ARG(TGLfixed, r);
+    GLES2_ARG(TGLfixed, q);
+    GLES2_BARRIER_ARG_NORET;
+
+    glMultiTexCoord4x(target, ps, t, r, q);
+}
+
+GLES2_CB(glNormal3x)
+{
+    GLES2_ARG(TGLfixed, nx);
+    GLES2_ARG(TGLfixed, ny);
+    GLES2_ARG(TGLfixed, nz);
+    GLES2_BARRIER_ARG_NORET;
+
+    glNormal3x(nx, ny, nz);
+}
+
+GLES2_CB(glNormalPointer)
+{
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(TGLsizei, stride);
+    GLES2_ARG(Tptr, pointerp);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Array glNormalPointer at 0x%x\n", pointerp);
+
+    gles2_Array *va = c->arrays + (c->narrays - gles1_num_glNormalPointer);
+    va->type = type;
+    va->stride = stride;
+    va->tptr = pointerp;
+    va->size = gles1_size_glNormalPointer;
+    va->apply = gles1_apply_glNormalPointer;
+    va->enabled = 1;
+}
+
+GLES2_CB(glOrthox)
+{
+    GLES2_ARG(TGLfixed, left);
+    GLES2_ARG(TGLfixed, right);
+    GLES2_ARG(TGLfixed, bottom);
+    GLES2_ARG(TGLfixed, top);
+    GLES2_ARG(TGLfixed, zNear);
+    GLES2_ARG(TGLfixed, zFar);
+    GLES2_BARRIER_ARG_NORET;
+
+    glOrthox(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glPointParameterx)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointParameterx(pname, param);
+}
+
+GLES2_CB(glPointParameterxv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointParameterxv(pname, params);
+}
+
+GLES2_CB(glPointSizex)
+{
+    GLES2_ARG(TGLfixed, size);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPointSizex(size);
+}
+
+GLES2_CB(glPolygonOffsetx)
+{
+    GLES2_ARG(TGLfixed, factor);
+    GLES2_ARG(TGLfixed, units);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPolygonOffsetx(factor, units);
+}
+
+GLES2_CB(glPopMatrix)
+{
+    GLES2_BARRIER_ARG_NORET;
+
+    glPopMatrix();
+}
+
+GLES2_CB(glPushMatrix)
+{
+    GLES2_BARRIER_ARG_NORET;
+
+    glPushMatrix();
+}
+
+GLES2_CB(glRotatex)
+{
+    GLES2_ARG(TGLfixed, angle);
+    GLES2_ARG(TGLfixed, x);
+    GLES2_ARG(TGLfixed, y);
+    GLES2_ARG(TGLfixed, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glRotatex(angle, x, y, z);
+}
+
+GLES2_CB(glSampleCoveragex)
+{
+    GLES2_ARG(TGLclampx, value);
+    GLES2_ARG(TGLboolean, invert);
+    GLES2_BARRIER_ARG_NORET;
+
+    glSampleCoveragex(value, invert);
+}
+
+GLES2_CB(glScalex)
+{
+    GLES2_ARG(TGLfixed, x);
+    GLES2_ARG(TGLfixed, y);
+    GLES2_ARG(TGLfixed, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glScalex(x, y, z);
+}
+
+GLES2_CB(glShadeModel)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glShadeModel(mode);
+}
+
+GLES2_CB(glTexCoordPointer)
+{
+    GLES2_ARG(TGLint, size);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(TGLsizei, stride);
+    GLES2_ARG(Tptr, pointerp);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Array glTexCoordPointer at 0x%x\n", pointerp);
+
+    gles2_Array *va = c->arrays + (c->narrays - gles1_num_glTexCoordPointer);
+    va->size = size;
+    va->type = type;
+    va->stride = stride;
+    va->tptr = pointerp;
+    va->apply = gles1_apply_glTexCoordPointer;
+    va->enabled = 1;
+}
+
+GLES2_CB(glTexEnvi)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLint, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnvi(target, pname, param);
+}
+
+GLES2_CB(glTexEnvx)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnvx(target, pname, param);
+}
+
+GLES2_CB(glTexEnviv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLint params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLint(s, paramsp + i*sizeof(TGLint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnviv(target, pname, params);
+}
+
+GLES2_CB(glTexEnvxv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexEnvxv(target, pname, params);
+}
+
+GLES2_CB(glTexParameterx)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfixed, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexParameterx(target, pname, param);
+}
+
+GLES2_CB(glTexParameterxv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    unsigned count = gles1_glGetCount(pname);
+    GLfixed params [16];
+    unsigned i = 0;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexParameterxv(target, pname, params);
+}
+
+GLES2_CB(glTranslatex)
+{
+    GLES2_ARG(TGLfixed, x);
+    GLES2_ARG(TGLfixed, y);
+    GLES2_ARG(TGLfixed, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTranslatex(x, y, z);
+}
+
+GLES2_CB(glVertexPointer)
+{
+    GLES2_ARG(TGLint, size);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(TGLsizei, stride);
+    GLES2_ARG(Tptr, pointerp);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Array glVertexPointer at 0x%x\n", pointerp);
+
+    gles2_Array *va = c->arrays + (c->narrays - gles1_num_glVertexPointer);
+    va->size = size;
+    va->type = type;
+    va->stride = stride;
+    va->tptr = pointerp;
+    va->apply = gles1_apply_glVertexPointer;
+    va->enabled = 1;
+}
diff --git a/hw/gles2.c b/hw/gles2.c
new file mode 100644 (file)
index 0000000..ab4f822
--- /dev/null
@@ -0,0 +1,754 @@
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * 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) any later version of the License.
+ *
+ * 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 "gles2.h"
+#include <pthread.h>
+
+// From target-arm/helper.c.
+extern int get_phys_addr(CPUState *env, uint32_t address,
+                         int access_type, int is_user,
+                         uint32_t *phys_ptr, int *prot,
+                         target_ulong *page_size);
+
+
+// List of calls to used by the kernel module (page 0).
+static gles2_Call const gles2_kcalls[];
+// List of calls used by clients (page 1->15).
+static gles2_Call const gles2_calls[];
+
+// Translate a target virtual address to physical address.
+static target_ulong gles2_pa(gles2_State *s, target_ulong va,
+    int access_type)
+{
+    target_ulong pa, ps;
+    int prot;
+
+    if (get_phys_addr(s->env, va, access_type, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Page fault on guest!\n");
+        return 0;
+    }
+
+    return pa;
+}
+
+int gles2_transfer_compile(gles2_CompiledTransfer* tfr, gles2_State *s,
+    target_ulong va, target_ulong len)
+{
+    tfr->nsections = 0;
+    tfr->sections = 0;
+
+#if (GLES2_DEBUG == 1)
+    target_ulong first_page = TARGET_PAGE(va);
+#endif // GLES2_DEBUG == 1
+
+    target_ulong last_page = TARGET_PAGE(va + len - 1);
+    target_ulong start_addr = va;
+
+    GLES2_PRINT("DEBUG: Compiling transfer of %d bytes at 0x%x (0x%x->0x%x).\n",
+        len, va, first_page, last_page);
+
+    // Loop through the pages.
+    while (len) {
+        target_ulong start_page = TARGET_PAGE(start_addr);
+        target_ulong start_pa = gles2_pa(s, start_page, 0);
+        target_ulong end_pa = start_pa;
+
+        // Solve length of continuous section.
+        target_ulong end_page = start_page;
+        while(end_page < last_page) {
+            target_ulong next_page = end_page + TARGET_PAGE_SIZE;
+            target_ulong next_pa = gles2_pa(s, next_page, 0);
+
+            // If the target pages are not linearly spaced, stop..
+            if((next_pa < start_pa) ||
+                (next_pa - start_pa > next_page - start_page)) {
+                break;
+            }
+
+            end_page = next_page;
+            end_pa = next_pa;
+        }
+
+        unsigned id = tfr->nsections++;
+
+        GLES2_PRINT("\tContinuous from 0x%x to 0x%x (0x%x to 0x%x) #%d.\n",
+                    start_page, end_page, start_pa, end_pa, id);
+        tfr->sections = realloc(tfr->sections,
+            tfr->nsections*sizeof(*(tfr->sections)));
+
+        target_phys_addr_t pages_len = end_page + TARGET_PAGE_SIZE - start_page;
+        void* target_pages = cpu_physical_memory_map(start_pa, &pages_len, 0);
+
+        if (!target_pages || !pages_len) {
+            fprintf(stderr, "ERROR: Failed to map memory to host!\n");
+            return 0;
+        }
+
+        target_ulong section_len = end_page + TARGET_PAGE_SIZE - start_addr;
+
+        if (section_len > len) {
+            section_len = len;
+        }
+
+        target_ulong offset = TARGET_OFFSET(start_addr);
+        void* target_data = target_pages + offset;
+
+        GLES2_PRINT("\tSlice of %d bytes at %p (offset = %x).\n",
+            section_len, target_data, offset);
+
+        tfr->sections[id].base = target_data;
+        tfr->sections[id].len = section_len;
+
+        cpu_physical_memory_unmap(target_pages, pages_len, 0, pages_len);
+        len -= section_len;
+        start_addr += section_len;
+        GLES2_PRINT("\t%d bytes remain...\n", len);
+    }
+    return 1;
+}
+
+void gles2_transfer_exec(gles2_CompiledTransfer* tfr, gles2_State *s,
+    void* data, int access_type)
+{
+    unsigned i;
+
+    for (i = 0; i < tfr->nsections; ++i) {
+        void* target_data = tfr->sections[i].base;
+        target_ulong len = tfr->sections[i].len;
+        if (access_type == 0) {
+            memcpy(data, target_data, len);
+        } else {
+            memcpy(target_data, data, len);
+        }
+        data += len;
+    }
+}
+
+void gles2_transfer_free(gles2_CompiledTransfer* tfr)
+{
+    free(tfr->sections);
+    tfr->sections = 0;
+    tfr->nsections = 0;
+}
+
+int gles2_transfer(gles2_State *s, target_ulong va, target_ulong len,
+    void* data, int access_type)
+{
+#if (GLES2_DEBUG == 1)
+    target_ulong first_page = TARGET_PAGE(va);
+#endif // GLES2_DEBUG == 1
+
+    target_ulong last_page = TARGET_PAGE(va + len - 1);
+    target_ulong start_addr = va;
+
+    GLES2_PRINT("DEBUG: Request transfer of %d bytes at 0x%x (0x%x->0x%x) (access=%d).\n",
+        len, va, first_page, last_page, access_type);
+
+    // Loop through the pages.
+    while (len) {
+        target_ulong start_page = TARGET_PAGE(start_addr);
+        target_ulong start_pa = gles2_pa(s, start_page, access_type);
+        target_ulong end_pa = start_pa;
+
+        // Solve length of continuous section.
+        target_ulong end_page = start_page;
+        while(end_page < last_page) {
+            target_ulong next_page = end_page + TARGET_PAGE_SIZE;
+            target_ulong next_pa = gles2_pa(s, next_page, access_type);
+
+            // If the target pages are not linearly spaced, stop..
+            if ((next_pa < start_pa) ||
+                (next_pa - start_pa > next_page - start_page)) {
+                break;
+            }
+
+            end_page = next_page;
+            end_pa = next_pa;
+        }
+
+        GLES2_PRINT("\tContinuous from 0x%x to 0x%x (0x%x to 0x%x).\n",
+            start_page, end_page, start_pa, end_pa);
+
+        target_phys_addr_t pages_len = end_page + TARGET_PAGE_SIZE - start_page;
+        void* target_pages = cpu_physical_memory_map(start_pa, &pages_len, access_type);
+        if (!target_pages || !pages_len) {
+            GLES2_PRINT("ERROR: Failed to map memory to host!\n");
+            return 0;
+        }
+
+        target_ulong section_len = end_page + TARGET_PAGE_SIZE - start_addr;
+        target_ulong offset = TARGET_OFFSET(start_addr);
+        void* target_data = target_pages + offset;
+
+        if (section_len > len) {
+            section_len = len;
+        }
+
+        GLES2_PRINT("\tTransfering %d bytes at %p (offset = %x).\n",
+            section_len, target_data, offset);
+
+        if (access_type == 0) {
+            memcpy(data, target_data, section_len);
+        } else {
+            memcpy(target_data, data, section_len);
+        }
+
+        cpu_physical_memory_unmap(target_pages, pages_len, access_type, pages_len);
+        len -= section_len;
+        start_addr += section_len;
+        data += section_len;
+
+        GLES2_PRINT("\t%d bytes remain...\n", len);
+    }
+    return 1;
+}
+
+void gles2_put_byte(gles2_State *s, target_ulong va, uint8_t byte)
+{
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return;
+    }
+
+    GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", byte, va, pa);
+    stb_phys(pa, byte);
+}
+
+uint8_t gles2_get_byte(gles2_State *s, target_ulong va)
+{
+    uint8_t byte;
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return 0xDE;
+    }
+
+    byte = ldub_phys(pa);
+
+    //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", byte, va, pa);
+
+    return byte;
+}
+
+void gles2_put_word(gles2_State *s, target_ulong va, uint16_t word)
+{
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return;
+    }
+
+    GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", word, va, pa);
+    stw_phys(pa, word);
+}
+
+uint16_t gles2_get_word(gles2_State *s, target_ulong va)
+{
+    uint16_t word;
+
+    int prot;
+    target_ulong pa, ps;
+    if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return 0xDEAD;
+    }
+
+    word = lduw_phys(pa);
+
+    //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", word, va, pa);
+
+    return word;
+}
+
+uint32_t gles2_get_dword(gles2_State *s, target_ulong va)
+{
+    uint32_t dword;
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return 0xDEAD000;
+    }
+
+    dword = ldl_phys(pa);
+
+    //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", dword, va, pa);
+
+    return dword;
+}
+
+void gles2_put_dword(gles2_State *s, target_ulong va, uint32_t dword)
+{
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return;
+    }
+
+    GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", dword, va, pa);
+    stl_phys(pa, dword);
+}
+
+float gles2_get_float(gles2_State *s, target_ulong va)
+{
+    float flt;
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return -123456789.f;
+    }
+
+    cpu_physical_memory_read(pa, (unsigned char*)&flt, 4);
+    //flt = ldfl_p(pa);
+
+    //GLES2_PRINT("DEBUG: Read %f from 0x%x(0x%x)\n", flt, va, pa);
+
+    return flt;
+}
+
+void gles2_put_float(gles2_State *s, target_ulong va, float flt)
+{
+    int prot;
+    target_ulong pa, ps;
+
+    if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+        GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+        return;
+    }
+
+    GLES2_PRINT("DEBUG: Written %f to 0x%x(0x%x)\n", flt, va, pa);
+    cpu_physical_memory_write(pa, (unsigned char*)&flt, 4);
+    //stfl_p(pa, flt);
+}
+
+uint32_t gles2_handle_create(gles2_State *s, void* data)
+{
+    uint32_t i = 0;
+
+    if (data) {
+        for (i = 0; i < GLES2_NHANDLES; ++i) {
+            if (!s->handles[i]) {
+                break;
+            }
+        }
+
+        if (i == GLES2_NHANDLES) {
+            fprintf(stderr, "ERROR: No free handles!\n");
+            return 0x0;
+        }
+        s->handles[i] = data;
+        i |= GLES2_HANDLE_BASE;
+    }
+
+    GLES2_PRINT("Handle %x created for %p!\n", i, data);
+    return i;
+}
+
+uint32_t gles2_handle_find(gles2_State *s, void* data)
+{
+    uint32_t i = 0;
+
+    if (data) {
+        for(i = 0; i < GLES2_NHANDLES; ++i) {
+            if(s->handles[i] == data) {
+                break;
+            }
+        }
+
+        if (i == GLES2_NHANDLES) {
+//            fprintf(stderr, "ERROR: Handle not found!\n");
+            return 0x0;
+        }
+        i |= GLES2_HANDLE_BASE;
+    }
+
+    GLES2_PRINT("Handle %x found for %p!\n", i, data);
+    return i;
+}
+
+void* gles2_handle_get(gles2_State *s, uint32_t i)
+{
+#if(GLES2_DEBUG == 1)
+    if (i && (i & ~GLES2_HANDLE_MASK) != GLES2_HANDLE_BASE) {
+        GLES2_PRINT("ERROR: Invalid handle %x!\n", i);
+        exit(1);
+    }
+#endif // GLES2_DEBUG == 1
+
+    void* data = i ? s->handles[i & GLES2_HANDLE_MASK] : NULL;
+
+    GLES2_PRINT("Reading handle %x => %p\n", i, data);
+
+    return data;
+}
+
+void* gles2_handle_free(gles2_State *s, uint32_t i)
+{
+    void* data = NULL;
+    if (i) {
+        data = s->handles[i & GLES2_HANDLE_MASK];
+        s->handles[i & GLES2_HANDLE_MASK] = NULL;
+    }
+
+    GLES2_PRINT("Freed handle %i => %p\n", i, data);
+    return data;
+}
+
+// Virtual register area write operation handler.
+static void gles2_write(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    gles2_State *s = (gles2_State*)opaque;
+
+    target_ulong page = addr/(4*TARGET_PAGE_SIZE);
+    target_ulong callnr = ((addr) & (4*TARGET_PAGE_SIZE - 1))/0x04;
+
+    GLES2_PRINT("Page %d (0x%x) write (call nr. %d (0x%x)), addr = 0x%x, value = 0x%x.\n", page, page, callnr, callnr, (int32_t)addr, value);
+
+    if (page) {
+        gles2_Call const *call = gles2_calls + callnr;
+
+        if (page > 1) {
+            gles2_Client* client;
+
+            // Client API calls without active context should be ignored.
+            if ((page - 2 > GLES2_NCLIENTS) ||
+                !(client = s->clients[page - 2])) {
+                return;
+            }
+
+            // Make sure nothing is running.
+            GLES2_PRINT("Syncing with worker...\n");
+            pthread_mutex_lock(&client->mutex_wait);
+            while (client->state != gles2_ClientState_ready) {
+                pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+            }
+            pthread_mutex_lock(&client->mutex_run);
+            pthread_mutex_lock(&client->mutex_xcode);
+            client->call = call;
+            client->state = gles2_ClientState_pending;
+            client->phase_xcode = 0;
+            GLES2_PRINT("Requesting call %s...\n", call->name);
+            pthread_cond_signal(&client->cond_start);
+            pthread_mutex_unlock(&client->mutex_wait);
+
+            GLES2_PRINT("Releasing worker for decoding...\n");
+            pthread_mutex_unlock(&client->mutex_run);
+            do {
+                pthread_cond_wait(&client->cond_xcode, &client->mutex_xcode);
+            } while (client->phase_xcode < 1);
+
+            pthread_mutex_unlock(&client->mutex_xcode);
+            GLES2_PRINT("Decoding finished.\n");
+        } else {
+            gles2_decode_t d = 0;
+            GLES2_PRINT("Calling clientless function %s...\n", call->name);
+            call->callback(s, &d, 0);
+        }
+    } else {
+        gles2_Call const *call = gles2_kcalls + callnr;
+        gles2_decode_t d = 0;
+
+        GLES2_PRINT("Calling kernel function %s...\n", call->name);
+
+        call->callback(s, &d, 0);
+    }
+}
+
+// Virtual register area read operation handler.
+static uint32_t gles2_read(void *opaque, target_phys_addr_t addr)
+{
+    gles2_State *s = (gles2_State*)opaque;
+
+    target_ulong page = addr/(4*TARGET_PAGE_SIZE);
+
+    if (page) {
+        gles2_Client* client;
+
+        // Client API calls without active context should be ignored.
+        if ((page - 2 > GLES2_NCLIENTS) ||
+            !(client = s->clients[page - 2])) {
+            return 0;
+        }
+
+        if(pthread_mutex_trylock(&client->mutex_xcode)) {
+            return 1;
+        }
+
+        if(client->phase_xcode == 2) {
+            while (client->phase_xcode < 4) {
+                client->phase_xcode = 3;
+                pthread_cond_signal(&client->cond_return);
+                pthread_cond_wait(&client->cond_xcode, &client->mutex_xcode);
+            }
+            pthread_mutex_unlock(&client->mutex_xcode);
+            return 0;
+        }
+        int ret = (client->phase_xcode == 4 ? 0 : 1);
+        pthread_mutex_unlock(&client->mutex_xcode);
+        return ret;
+    }
+    return 0;
+}
+
+static CPUReadMemoryFunc *gles2_readfn[] = {
+    gles2_read,
+    gles2_read,
+    gles2_read,
+};
+
+static CPUWriteMemoryFunc *gles2_writefn[] = {
+    gles2_write,
+    gles2_write,
+    gles2_write,
+};
+
+// Initializes a new gles2 device.
+void *gles2_init(CPUState *env)
+{
+    gles2_State *s = qemu_malloc(sizeof(*s));
+    unsigned i;
+
+    s->env = env;
+    s->quality = gles2_quality;
+
+    GLES2_PRINT("GLES2 quality: %d\n", s->quality);
+
+    cpu_register_physical_memory(GLES2_HWBASE,
+        GLES2_HWSIZE,
+        cpu_register_io_memory(gles2_readfn,
+            gles2_writefn, s, DEVICE_NATIVE_ENDIAN));
+
+    for (i = 0; i < GLES2_NCLIENTS; ++i) {
+        s->clients[i] = NULL;
+    }
+
+    for (i = 0; i < GLES2_NHANDLES; ++i) {
+        s->handles[i] = NULL;
+    }
+
+    GLES2_PRINT("Registered IO memory!\n");
+    return s;
+}
+
+/******************************************************************************
+ *
+ * Kernel interface functions.
+ *
+ *****************************************************************************/
+
+static void* gles2_client_worker(void *opaque)
+{
+    gles2_Client *client = opaque;
+    int run = 1;
+
+    GLES2_PRINT("WORKER(%d): Starting!\n", client->nr);
+
+    pthread_mutex_lock(&client->mutex_xcode);
+    do
+    {
+        gles2_decode_t d = 0;
+        GLES2_PRINT("WORKER(%d): Waiting for call...\n", client->nr);
+        pthread_mutex_lock(&client->mutex_wait);
+
+        client->state = gles2_ClientState_ready;
+        pthread_cond_signal(&client->cond_state);
+        client->phase_xcode = 4;
+        pthread_cond_signal(&client->cond_xcode);
+        pthread_mutex_unlock(&client->mutex_xcode);
+        while (client->state != gles2_ClientState_pending) {
+            pthread_cond_wait(&client->cond_start, &client->mutex_wait);
+        }
+
+        GLES2_PRINT("WORKER(%d): Got call, waiting permission to run...\n", client->nr);
+
+        pthread_mutex_lock(&client->mutex_run);
+        GLES2_PRINT("WORKER(%d): Running!\n", client->nr);
+        client->state = gles2_ClientState_running;
+        pthread_mutex_unlock(&client->mutex_wait);
+
+        if (client->call) {
+            GLES2_PRINT("WORKER(%d): Calling function %s (%p)...\n",
+                        client->nr, client->call->name, client->call);
+            client->call->callback(client->s, &d, client);
+
+            GLES2_PRINT("\tWORKER(%d): Done.\n", client->nr);
+            client->state = gles2_ClientState_done;
+        } else {
+            GLES2_PRINT("WORKER(%d): Exit requested!\n", client->nr);
+            run = 0;
+            client->state = gles2_ClientState_exit;
+            pthread_cond_signal(&client->cond_state);
+        }
+        pthread_mutex_unlock(&client->mutex_run);
+    } while (run);
+
+    GLES2_PRINT("WORKER(%d): Exiting!\n", client->nr);
+    return opaque;
+}
+
+// Called by kernel module when a new client connects.
+static void gles2_init_cb(gles2_State *s, gles2_decode_t *d, gles2_Client *c)
+{
+    unsigned i;
+    gles2_Client *client;
+    pthread_attr_t attr;
+
+    for (i = 0; i < GLES2_NCLIENTS; ++i) {
+        if (!s->clients[i]) {
+            break;
+        }
+    }
+
+    if (i == GLES2_NCLIENTS) {
+        GLES2_PRINT("ERROR: No free slots!\n");
+        gles2_ret_dword(s, 0);
+        return;
+    }
+
+    GLES2_PRINT("Initialization!\n");
+
+    client = malloc(sizeof(*client));
+    client->s = s;
+    client->nr = i + 1;
+    pthread_mutex_init(&client->mutex_wait, NULL);
+    pthread_mutex_init(&client->mutex_run, NULL);
+    pthread_mutex_init(&client->mutex_xcode, NULL);
+    pthread_cond_init(&client->cond_start, NULL);
+    pthread_cond_init(&client->cond_state, NULL);
+    pthread_cond_init(&client->cond_xcode, NULL);
+    pthread_cond_init(&client->cond_return, NULL);
+    client->state = gles2_ClientState_init;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    pthread_mutex_lock(&client->mutex_wait);
+
+    GLES2_PRINT("Creating worker...\n");
+    pthread_create(&client->thread, &attr, gles2_client_worker, client);
+
+    do {
+        pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+    } while(client->state != gles2_ClientState_ready);
+    pthread_mutex_unlock(&client->mutex_wait);
+
+    GLES2_PRINT("Worker initialized\n");
+
+    s->clients[i] = client;
+    gles2_ret_dword(s, client->nr);
+}
+
+// Called by kernel module when an existing client disconnects.
+static void gles2_exit_cb(gles2_State *s, gles2_decode_t *d, gles2_Client *c)
+{
+    uint32_t nr = gles2_arg_dword(s, d);
+    gles2_Client *client;
+
+    GLES2_PRINT("Exit called for client %d!\n", nr);
+
+    if ((nr > GLES2_NCLIENTS + 1) ||
+        (nr == 0)) {
+        GLES2_PRINT("Client number out of range!\n");
+        return;
+    }  else {
+        client = s->clients[nr - 1];
+    }
+
+    if (!client) {
+        GLES2_PRINT("Can't exit NULL client!\n");
+        return;
+    }
+
+    GLES2_PRINT("\tRequesting worker to exit.\n");
+
+    // Make sure nothing is running.
+    GLES2_PRINT("Syncing with worker...\n");
+    pthread_mutex_lock(&client->mutex_wait);
+    while (client->state != gles2_ClientState_ready) {
+        pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+    }
+    pthread_mutex_lock(&client->mutex_run);
+    client->call = NULL;
+    client->state = gles2_ClientState_pending;
+    GLES2_PRINT("Requesting exit...\n");
+    pthread_cond_signal(&client->cond_start);
+    pthread_mutex_unlock(&client->mutex_wait);
+
+    GLES2_PRINT("Waiting worker to exit...\n");
+    do {
+        pthread_cond_wait(&client->cond_state, &client->mutex_run);
+    } while (client->state != gles2_ClientState_exit);
+    pthread_mutex_unlock(&client->mutex_run);
+
+    GLES2_PRINT("\tJoining...\n");
+    pthread_join(client->thread, NULL);
+    pthread_mutex_destroy(&client->mutex_wait);
+    pthread_mutex_destroy(&client->mutex_run);
+    pthread_cond_destroy(&client->cond_start);
+    pthread_cond_destroy(&client->cond_state);
+
+    free(client);
+    s->clients[nr - 1] = NULL;
+
+    GLES2_PRINT("\tDone!\n");
+}
+
+/******************************************************************************
+ *
+ * Call tables
+ *
+ *****************************************************************************/
+
+/* Make a weak stub for every dummy function. */
+#define CALL_DUMMY(func) \
+    void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c); \
+    void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c) \
+    { \
+        GLES2_BARRIER_ARG_NORET; \
+        fprintf(stderr, "GLES2: DUMMY " #func "\n"); \
+    }
+
+#define CALL_ENTRY(func) \
+    void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c);
+
+#include "gles2_calls.h"
+
+#undef CALL_ENTRY
+#undef CALL_DUMMY
+
+#define CALL_ENTRY(func) { #func, gles2_##func##_cb },
+#define CALL_DUMMY(func) { #func, gles2_##func##_cb },
+
+static gles2_Call const gles2_kcalls[] =
+{
+    CALL_ENTRY(init)
+    CALL_ENTRY(exit)
+};
+
+static gles2_Call const gles2_calls[] =
+{
+    { "<<none>>", 0 },
+#include "gles2_calls.h"
+};
+
+#undef CALL_ENTRY
+
diff --git a/hw/gles2.h b/hw/gles2.h
new file mode 100644 (file)
index 0000000..21b2cbe
--- /dev/null
@@ -0,0 +1,357 @@
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * 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) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GLES2_H__
+#define GLES2_H__
+
+#include "qemu-common.h"
+#include "cpu.h"
+#include <pthread.h>
+
+#define GLES2_HWBASE 0x6f000000
+#define GLES2_HWSIZE 0x00100000
+#define GLES2_NCLIENTS (GLES2_HWSIZE/TARGET_PAGE_SIZE - 2)
+#define GLES2_NHANDLES GLES2_NCLIENTS * 16
+// Address base for host to guest pointer handles.
+#define GLES2_HANDLE_BASE 0xCAFE0000
+#define GLES2_HANDLE_MASK 0x0000FFFF // Handle to index bitmask.
+
+#define GLES2_BARRIER_ARG \
+    if (c) { \
+        pthread_mutex_lock(&c->mutex_xcode); \
+        c->phase_xcode = 1; \
+        pthread_cond_signal(&c->cond_xcode); \
+        pthread_mutex_unlock(&c->mutex_xcode); \
+        GLES2_PRINT("-- ARG BARRIER --\n"); \
+    }
+#define GLES2_BARRIER_ARG_NORET \
+    if (c) { \
+        pthread_mutex_lock(&c->mutex_xcode); \
+        c->phase_xcode = 3; \
+        GLES2_PRINT("-- ARG & RETURN BARRIER --\n"); \
+    }
+#define GLES2_BARRIER_RET \
+    if (c) { \
+        pthread_mutex_lock(&c->mutex_xcode); \
+        c->phase_xcode = 2; \
+        do { \
+            pthread_cond_wait(&c->cond_return, &c->mutex_xcode); \
+        } while (c->phase_xcode == 2); \
+        GLES2_PRINT("-- RETURN BARRIER --\n"); \
+    }
+
+// Round address to lower page boundary.
+#define TARGET_PAGE(addr) ((addr) & ~(TARGET_PAGE_SIZE - 1))
+// Return the page offset part of address.
+#define TARGET_OFFSET(addr) ((addr) & (TARGET_PAGE_SIZE - 1))
+
+//#define GLES2_DEBUG 1
+#define GLES2_DEBUG 0
+#if(GLES2_DEBUG == 1)
+#   define GLES2_PRINT(format, args...) \
+        fprintf(stderr, "GLES2: " format, ##args)
+#else
+#   define GLES2_PRINT(format, args...) (void)0
+#endif // GLES_DEBUG != 1
+
+struct gles2_Array;
+typedef struct gles2_Array gles2_Array;
+struct gles2_Call;
+typedef struct gles2_Call gles2_Call;
+struct gles2_State;
+typedef struct gles2_State gles2_State;
+
+typedef enum gles2_ClientState
+{
+    gles2_ClientState_init,
+    gles2_ClientState_ready,
+    gles2_ClientState_pending,
+    gles2_ClientState_running,
+    gles2_ClientState_done,
+    gles2_ClientState_exit
+} gles2_ClientState;
+
+// A connected GLES2 client.
+typedef struct gles2_Client
+{
+    gles2_State *s;     // Link to the device state the client was connected to.
+    target_ulong nr;    // The client ID from kernel.
+
+    gles2_Call const *call;     // Next/current call to perform.
+    pthread_t thread;           // The worker thread.
+    pthread_cond_t cond_start;  // To wake worker for a call.
+    pthread_cond_t cond_state;  // Worker signals when state changes.
+    volatile gles2_ClientState state;// Status of the client.
+    pthread_mutex_t mutex_run;  // Locked when thread is running, or is
+                                // prevented from doing so.
+    pthread_mutex_t mutex_wait; // For synchronization of worker and caller.
+
+    pthread_mutex_t mutex_xcode; // For decode/encode synchronization.
+    volatile int phase_xcode;    // Phase of call 0: pending 1: decode done
+                                 // 2: exec done 3: encode done
+    pthread_cond_t cond_xcode;   // --''--
+    pthread_cond_t cond_return;  // --''--
+
+    gles2_Array *arrays;        // Host side vertex pointer arrays.
+    int narrays;                // Number of arrays (the maximum too).
+} gles2_Client;
+
+// The GLES2 device state holder.
+struct gles2_State
+{
+    CPUState *env;                         // The CPU the device is attached to.
+    gles2_Client *clients[GLES2_NCLIENTS]; // Array of clients.
+    void *handles[GLES2_NHANDLES];         // Handles passed from host to guest.
+    int quality;                           // Rendering quality.
+};
+
+typedef unsigned int gles2_decode_t; // Function call decoding state.
+typedef uint32_t gles2_target_arg_t; // Target unit argument type.
+// Callback for register area access.
+typedef void gles2_Callback(gles2_State *s, gles2_decode_t *d,
+    gles2_Client *c);
+
+// Information holder for guest->host call.
+struct gles2_Call
+{
+#ifndef NDEBUG
+    char const* name;
+#endif //!NDEBUG
+    gles2_Callback* callback;
+};
+
+// Create and initialize a GLES2 device and attach to CPU.
+extern void *gles2_init(CPUState *env);
+// Rendering quality option.
+extern int gles2_quality;
+
+/******************************************************************************
+ *
+ * Guest memory continuous access functions
+ *
+ *****************************************************************************/
+
+// Holder for compiled transfer holder.
+typedef struct gles2_CompiledTransfer
+{
+    unsigned nsections;   // Number of physical memory sections in the transfer.
+    struct
+    {
+        char* base;       // Base address of the section.
+        target_ulong len; // Length of the section.
+    } *sections;          // Sections of the transfer.
+} gles2_CompiledTransfer;
+
+// Pre-compile a transfer to or from virtual guest address.
+// NOTE: An assumption is made that the mapping is equal for read and write, for
+//       complicated transfers, use gles2_transfer or Fix It!
+extern int  gles2_transfer_compile(gles2_CompiledTransfer *tfr, gles2_State *s,
+    target_ulong va, target_ulong len);
+// Execute a pre-compiled transfer.
+extern void gles2_transfer_exec(gles2_CompiledTransfer *tfr, gles2_State *s,
+    void* data, int access_type);
+// Free a pre-compiled transfer.
+extern void gles2_transfer_free(gles2_CompiledTransfer *tfr);
+// Perform a non-compiled transfer between guest and host.
+// access_type, read = 0, write = 1, execute = 2
+extern int  gles2_transfer(gles2_State *s, target_ulong va, target_ulong len,
+    void* data, int access_type);
+
+/******************************************************************************
+ *
+ * Guest memory random access functions
+ *
+ *****************************************************************************/
+
+// Read an 8-bit byte from target system memory.
+extern uint8_t  gles2_get_byte(gles2_State *s, target_ulong va);
+// Write an 8-bit byte to target system memory.
+extern void     gles2_put_byte(gles2_State *s, target_ulong va, uint8_t byte);
+// Read a 16-bit word from target system memory.
+extern uint16_t gles2_get_word(gles2_State *s, target_ulong va);
+// Write a 16-bit word to target system memory.
+extern void     gles2_put_word(gles2_State *s, target_ulong va, uint16_t word);
+// Read a 32-bit double word from target system memory.
+extern uint32_t gles2_get_dword(gles2_State *s, target_ulong va);
+// Write a 32-bit double word to target system memory.
+extern void     gles2_put_dword(gles2_State *s, target_ulong va,
+    uint32_t dword);
+// Read a 32-bit float from target system memory.
+extern float    gles2_get_float(gles2_State *s, target_ulong va);
+// Write a 32-bit float to target system memory.
+extern void     gles2_put_float(gles2_State *s, target_ulong va, float flt);
+
+#define gles2_put_handle(s, va, handle) gles2_put_dword(s, va, handle)
+#define gles2_get_handle(s, va) gles2_get_dword(s, va)
+
+/******************************************************************************
+ *
+ * Handle management functions
+ *
+ *****************************************************************************/
+
+// Create a handle from host side pointer to be passed to guest.
+extern uint32_t gles2_handle_create(gles2_State *s, void* data);
+// Find if there is previously created handle for pointer.
+extern uint32_t gles2_handle_find(gles2_State *s, void* data);
+// Get the host pointer by guest handle.
+extern void* gles2_handle_get(gles2_State *s, uint32_t i);
+// Release a handle for reuse.
+extern void* gles2_handle_free(gles2_State *s, uint32_t i);
+
+// Get the smallest function argument according to target CPU ABI.
+static inline gles2_target_arg_t gles2_arg_raw(gles2_State *s, unsigned i);
+static inline gles2_target_arg_t gles2_arg_raw(gles2_State *s, unsigned i)
+{
+    if (i < 4) {
+        return s->env->regs[i];
+    }
+
+    return gles2_get_dword(s, s->env->regs[13] + 2*0x04 + (i - 4)*0x04);
+}
+
+/******************************************************************************
+ *
+ * ABI function argument decoding functions
+ *
+ *****************************************************************************/
+
+#define GLES2_DEBUG_ARGS 1
+static inline uint8_t gles2_arg_byte(gles2_State *s, gles2_decode_t *d);
+static inline uint8_t gles2_arg_byte(gles2_State *s, gles2_decode_t *d)
+{
+    uint8_t byte = gles2_arg_raw(s, (*d)++) & 0xFF;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("byte arg(%d) = %x\n", *d - 1, byte);
+#endif
+    return byte;
+}
+
+static inline uint16_t gles2_arg_word(gles2_State *s, gles2_decode_t *d);
+static inline uint16_t gles2_arg_word(gles2_State *s, gles2_decode_t *d)
+{
+    uint16_t word = gles2_arg_raw(s, (*d)++) & 0xFFFF;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("word arg(%d) = %x\n", *d - 1, word);
+#endif
+    return word;
+}
+
+static inline uint32_t gles2_arg_dword(gles2_State *s, gles2_decode_t *d);
+static inline uint32_t gles2_arg_dword(gles2_State *s, gles2_decode_t *d)
+{
+    uint32_t dword = gles2_arg_raw(s, (*d)++);
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("dword arg(%d) = %x\n", *d - 1, dword);
+#endif
+    return dword;
+}
+
+static inline uint64_t gles2_arg_qword(gles2_State *s, gles2_decode_t *d);
+static inline uint64_t gles2_arg_qword(gles2_State *s, gles2_decode_t *d)
+{
+    uint64_t qword = gles2_arg_raw(s, (*d)++)
+        | ((uint64_t)gles2_arg_raw(s, (*d)++) << 32);
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("qword arg(%d) = %"PRIu64"\n", *d - 2, qword);
+#endif
+    return qword;
+}
+
+static inline uint32_t gles2_arg_handle(gles2_State *s, gles2_decode_t *d);
+static inline uint32_t gles2_arg_handle(gles2_State *s, gles2_decode_t *d)
+{
+    uint32_t handle = gles2_arg_raw(s, (*d)++);
+
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("handle arg(%d) = %x\n", *d - 1, handle);
+#endif
+    return handle;
+}
+
+// This needs to be its own special function, because we must preserve the byteorder.
+static inline float gles2_arg_float(gles2_State *s, gles2_decode_t *d);
+static inline float gles2_arg_float(gles2_State *s, gles2_decode_t *d)
+{
+    unsigned i = (*d)++;
+
+    if (i < 4) {
+        return *((float*)&s->env->regs[i]);
+    }
+
+    return gles2_get_float(s, s->env->regs[13] + 2*0x04 + (i - 4)*0x04);
+}
+
+/******************************************************************************
+ *
+ * ABI return value encoding functions
+ *
+ *****************************************************************************/
+
+static inline void gles2_ret_byte(gles2_State *s, uint8_t byte);
+static inline void gles2_ret_byte(gles2_State *s, uint8_t byte)
+{
+    s->env->regs[0] = byte;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("byte ret = %d\n", byte);
+#endif
+}
+
+static inline void gles2_ret_word(gles2_State *s, uint16_t word);
+static inline void gles2_ret_word(gles2_State *s, uint16_t word)
+{
+    s->env->regs[0] = word;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("word ret = %d\n", word);
+#endif
+}
+
+static inline void gles2_ret_dword(gles2_State *s, uint32_t dword);
+static inline void gles2_ret_dword(gles2_State *s, uint32_t dword)
+{
+    s->env->regs[0] = dword;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("dword ret = %d\n", dword);
+#endif
+}
+
+static inline void gles2_ret_qword(gles2_State *s, uint64_t qword);
+static inline void gles2_ret_qword(gles2_State *s, uint64_t qword)
+{
+    s->env->regs[0] = qword & 0xFFFFFFFF;
+    s->env->regs[1] = qword >> 32;
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("qword ret = %"PRIu64"\n", qword);
+#endif
+}
+
+static inline void gles2_ret_handle(gles2_State *s, uint32_t handle);
+static inline void gles2_ret_handle(gles2_State *s, uint32_t handle)
+{
+    s->env->regs[0] = handle;
+
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("handle ret = %x\n", handle);
+#endif
+}
+
+static inline void gles2_ret_float(gles2_State *s, float flt);
+static inline void gles2_ret_float(gles2_State *s, float flt)
+{
+    s->env->regs[0] = *(uint32_t*)&flt;
+
+#if (GLES2_DEBUG_ARGS == 1)
+    GLES2_PRINT("float ret = %f\n", flt);
+#endif
+}
+
+#endif // GLES2_H__
+
diff --git a/hw/gles2_calls.c b/hw/gles2_calls.c
new file mode 100644 (file)
index 0000000..1cdec75
--- /dev/null
@@ -0,0 +1,2796 @@
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * 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) any later version of the License.
+ *
+ * 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 "gles2.h"
+#include "EGL/degl.h"
+#include "GLES2/gl2.h"
+
+#include "gles2_types.h"
+
+GLES2_CB(eglBindAPI)
+{
+    GLES2_ARG(TEGLenum, api);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, eglBindAPI(api));
+}
+
+GLES2_CB(eglGetDisplay)
+{
+//     GLES2_ARG(TEGLDisplay, dpy);
+//     (void)dpy;
+    GLES2_BARRIER_ARG;
+
+    GLES2_PRINT("Getting display...\n");
+
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    GLES2_PRINT("\tGot host display %p...\n", dpy);
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLDisplay(s, gles2_handle_create(s, dpy));
+}
+
+GLES2_CB(eglInitialize)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(Tptr, majorp);
+    GLES2_ARG(Tptr, minorp);
+
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+    GLES2_PRINT("Request to initialize display %p...\n", dpy);
+
+    EGLint major, minor;
+    if (eglInitialize(dpy, &major, &minor)) {
+        GLES2_PRINT("Display initialized (EGL %d.%d)!\n", major, minor);
+        GLES2_BARRIER_RET;
+        gles2_put_TEGLint(s, majorp, major);
+        gles2_put_TEGLint(s, minorp, minor);
+        gles2_ret_TEGLBoolean(s, EGL_TRUE);
+        return;
+    }
+
+    GLES2_PRINT("Failed to initialize...\n");
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, EGL_FALSE);
+}
+
+GLES2_CB(eglTerminate)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+    GLES2_PRINT("Request to close display %p...\n", dpy);
+
+    EGLBoolean ret = eglTerminate(dpy);
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglGetConfigs)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(Tptr, configsp);
+    GLES2_ARG(TEGLint, config_size);
+    GLES2_ARG(Tptr, num_configp);
+    
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+    EGLConfig* configs = configsp ? malloc(sizeof(EGLConfig)*config_size) : NULL;
+
+    EGLint num_config;
+    EGLBoolean ret = eglGetConfigs(dpy, configs, config_size, &num_config);
+
+    GLES2_BARRIER_RET;
+    if (configs) {
+        EGLint i;
+
+        for (i = 0; i < num_config; ++i) {
+            uint32_t handle;
+            if (!(handle = gles2_handle_find(s, configs[i]))) {
+                handle = gles2_handle_create(s, configs[i]);
+            }
+            gles2_put_TEGLConfig(s, configsp + i*sizeof(TEGLConfig), handle);
+        }
+        
+        free(configs);
+    }
+    gles2_put_TEGLint(s, num_configp, num_config);
+
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglChooseConfig)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(Tptr, attrib_listp);
+    GLES2_ARG(Tptr, configsp);
+    GLES2_ARG(TEGLint, config_size);
+    GLES2_ARG(Tptr, num_configp);
+    (void)config_size;
+    (void)attrib_listp;
+
+    EGLint attrib_list_n = 0;
+    while (gles2_get_TEGLint(s, attrib_listp
+        + attrib_list_n*sizeof(EGLint)) != EGL_NONE) {
+        attrib_list_n += 2;
+    }
+    EGLint* attrib_list = malloc((attrib_list_n + 1)*sizeof(EGLint));
+    EGLint i;
+
+    for (i = 0; i < attrib_list_n; ++i) {
+        attrib_list[i] = gles2_get_TEGLint(s, attrib_listp
+            + i*sizeof(EGLint));
+    }
+    attrib_list[attrib_list_n] = EGL_NONE;
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+    EGLConfig* configs = configsp ? malloc(sizeof(EGLConfig)*config_size) : NULL;
+
+    EGLint num_config;
+    EGLBoolean ret = eglChooseConfig(dpy, attrib_list, configs, config_size, &num_config);
+    free(attrib_list);
+    GLES2_BARRIER_RET;
+    if (configs) {
+        EGLint i;
+
+        for (i = 0; i < num_config; ++i) {
+            uint32_t handle;
+            if (!(handle = gles2_handle_find(s, configs[i]))) {
+                handle = gles2_handle_create(s, configs[i]);
+            }
+            gles2_put_TEGLConfig(s, configsp + i*sizeof(TEGLConfig), handle);
+        }
+        
+        free(configs);
+    }
+    gles2_put_TEGLint(s, num_configp, num_config);
+
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglGetConfigAttrib)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLConfig, config);
+    GLES2_ARG(TEGLint, attribute);
+    GLES2_ARG(Tptr, valuep);
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+    EGLint value;
+    EGLBoolean ret = eglGetConfigAttrib(dpy, gles2_handle_get(s, config), attribute, &value);
+
+    GLES2_BARRIER_RET;
+
+    gles2_put_TEGLint(s, valuep, value);
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+typedef struct gles2_Surface
+{
+    uint32_t ddrawp;    // Pointer to the offscreen drawable in guest memory.
+    DEGLDrawable ddraw; // Offscreen drawable, read from guest memory.
+    EGLSurface surf;    // Pointer to the EGL surface.
+    uint32_t pixelsp;   // Pointer to pixels in guest memory.
+    int pixmap;         // True if surface is pixmap.
+    gles2_CompiledTransfer tfr; // Framebuffer transfer.
+    int valid;          // If the surface is valid.
+    int id; // DEBUG!
+} gles2_Surface;
+
+// See if guest offscreen drawable was changed and if so, update host copy.
+static int gles2_surface_update(gles2_State *s, gles2_Surface *surf)
+{
+    int ret = 0;
+
+    uint32_t width   = gles2_get_dword(s, surf->ddrawp + 0*sizeof(uint32_t));
+    uint32_t height  = gles2_get_dword(s, surf->ddrawp + 1*sizeof(uint32_t));
+    uint32_t depth   = gles2_get_dword(s, surf->ddrawp + 2*sizeof(uint32_t));
+    uint32_t bpp     = gles2_get_dword(s, surf->ddrawp + 3*sizeof(uint32_t));
+    uint32_t pixelsp = gles2_get_dword(s, surf->ddrawp + 4*sizeof(uint32_t));
+
+    if (width != surf->ddraw.width
+         || height != surf->ddraw.height
+         || depth != surf->ddraw.depth) {
+        surf->ddraw.width = width;
+        surf->ddraw.height = height;
+        surf->ddraw.depth = depth;
+        surf->ddraw.bpp = bpp;
+        ret = 1;
+    }
+
+    surf->pixelsp = pixelsp;
+
+    return ret;
+}
+
+// TODO: Support swapping of offscreen surfaces.
+static void gles2_eglSwapCallback(void* userdata)
+{
+    (void)userdata;
+    GLES2_PRINT("Swap called!\n");
+}
+
+static int surf_id = 1;
+
+GLES2_CB(eglCreateWindowSurface)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLConfig, config_);
+    GLES2_ARG(Tptr, winp);
+    GLES2_ARG(Tptr, attrib_listp);
+
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+    (void)attrib_listp;
+
+    gles2_Surface* fsurf;
+
+    if (!(fsurf = malloc(sizeof(*fsurf)))) {
+        GLES2_PRINT("\tFake window creation failed!\n");
+        GLES2_BARRIER_ARG;
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLSurface(s, 0);
+        return;
+    }
+
+    fsurf->id = surf_id++;
+
+    fsurf->ddrawp = winp;
+    fsurf->pixmap = 0;
+    gles2_surface_update(s, fsurf);
+    GLES2_BARRIER_ARG;
+
+    GLES2_PRINT("Host window creation requested, %dx%d@%d(Bpp=%d) at 0x%x, ID = %d...\n",
+        fsurf->ddraw.width, fsurf->ddraw.height,
+        fsurf->ddraw.depth, fsurf->ddraw.bpp, fsurf->pixelsp, fsurf->id);
+
+    unsigned nbytes = fsurf->ddraw.width*fsurf->ddraw.height*fsurf->ddraw.bpp;
+    fsurf->ddraw.pixels = malloc(nbytes);
+    fsurf->ddraw.userdata = fsurf;
+    fsurf->ddraw.swap = gles2_eglSwapCallback;
+
+    if((fsurf->surf = eglCreateWindowSurface(dpy, config,
+           (EGLNativeWindowType)&fsurf->ddraw, NULL)) == EGL_NO_CONTEXT)
+    {
+        GLES2_PRINT("\tHost window creation failed!\n");
+        free(fsurf->ddraw.pixels);
+        free(fsurf);
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLSurface(s, 0);
+        return;
+    }
+
+    GLES2_PRINT("Created at %p!\n", fsurf);
+    GLES2_BARRIER_RET;
+    gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+    gles2_ret_TEGLSurface(s, gles2_handle_create(s, fsurf));
+}
+
+GLES2_CB(eglCreatePixmapSurface)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLConfig, config_);
+    GLES2_ARG(Tptr, pixmapp);
+    GLES2_ARG(Tptr, attrib_listp);
+       
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+    (void)attrib_listp;
+
+    gles2_Surface* fsurf;
+
+    if (!(fsurf = malloc(sizeof(*fsurf)))) {
+        GLES2_PRINT("\tFake pixmap creation failed!\n");
+        GLES2_BARRIER_ARG;
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLSurface(s, 0);
+        return;
+    }
+
+    fsurf->id = surf_id++;
+    fsurf->ddrawp = pixmapp;
+    fsurf->pixmap = 1;
+    gles2_surface_update(s, fsurf);
+    GLES2_BARRIER_ARG;
+
+    GLES2_PRINT("Host pixmap creation requested, %dx%d@%d(Bpp=%d) at 0x%x, ID = %d...\n",
+        fsurf->ddraw.width, fsurf->ddraw.height,
+        fsurf->ddraw.depth, fsurf->ddraw.bpp, fsurf->pixelsp, fsurf->id);
+
+    unsigned nbytes = fsurf->ddraw.width*fsurf->ddraw.height*fsurf->ddraw.bpp;
+    fsurf->ddraw.pixels = malloc(nbytes);
+    fsurf->ddraw.userdata = fsurf;
+    fsurf->ddraw.swap = gles2_eglSwapCallback;
+
+    if((fsurf->surf = eglCreatePixmapSurface(dpy, config,
+        (EGLNativeWindowType)&fsurf->ddraw, NULL)) == EGL_NO_CONTEXT) {
+        GLES2_PRINT("\tHost pixmap creation failed!\n");
+        free(fsurf->ddraw.pixels);
+        free(fsurf);
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLSurface(s, 0);
+        return;
+    }
+
+    GLES2_PRINT("Created at %p!\n", fsurf);
+    GLES2_BARRIER_RET;
+    gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+    gles2_ret_TEGLSurface(s, gles2_handle_create(s, fsurf));
+}
+
+GLES2_CB(eglDestroySurface)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLSurface, surface_);
+    GLES2_BARRIER_ARG_NORET;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    gles2_Surface* fsurf = (EGLSurface)gles2_handle_get(s, surface_);
+    gles2_handle_free(s, surface_);
+    
+    GLES2_PRINT("Destroyed surface ID = %d...\n", fsurf->id);
+    fsurf->id = -1;
+
+    eglDestroySurface(dpy, fsurf->surf);
+    free(fsurf->ddraw.pixels);
+
+//    if(fsurf->pixmap == 0) {
+        gles2_transfer_free(&fsurf->tfr);
+//    }
+    free(fsurf);
+}
+
+GLES2_CB(eglBindTexImage)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLSurface, surface_);
+    GLES2_ARG(TEGLint, buffer);
+    gles2_CompiledTransfer tfr;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    gles2_Surface* fsurf = (gles2_Surface*)gles2_handle_get(s, surface_);
+
+    // FIXME: Not a very clean way..
+    uint32_t pixelsp = gles2_get_dword(s, fsurf->ddrawp + 4*sizeof(uint32_t));
+    if (pixelsp) {
+        unsigned nbytes = fsurf->ddraw.width 
+            * fsurf->ddraw.height*fsurf->ddraw.bpp;
+        gles2_transfer_compile(&tfr, s, pixelsp, nbytes);
+    }
+    GLES2_BARRIER_ARG;
+
+    if (pixelsp) {
+//        gles2_transfer(s, pixelsp, nbytes, fsurf->ddraw.pixels, 0);
+        gles2_transfer_exec(&tfr, s, fsurf->ddraw.pixels, 0);
+    }
+
+    GLES2_PRINT("Binding surface ID = %d!\n", fsurf->id);
+
+    EGLBoolean ret = eglBindTexImage(dpy, &fsurf->ddraw, buffer);
+    if (pixelsp) {
+        gles2_transfer_free(&tfr);
+    }
+    
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglReleaseTexImage)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLSurface, surface_);
+    GLES2_ARG(TEGLint, buffer);
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    gles2_Surface* fsurf = (gles2_Surface*)gles2_handle_get(s, surface_);
+
+    GLES2_PRINT("Unbinding surface ID = %d!\n", fsurf->id);
+
+    EGLBoolean ret = eglReleaseTexImage(dpy, &fsurf->ddraw, buffer);
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglCreateContext)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLConfig, config_);
+    GLES2_ARG(TEGLContext, share_context_);
+    GLES2_ARG(Tptr, attrib_listp);
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+    EGLContext share_context = (EGLContext)gles2_handle_get(s, share_context_);
+
+    GLES2_PRINT("TODO: Handle attribs...\n");
+    (void)attrib_listp;
+
+    GLES2_PRINT("Host context creation requested...\n");
+    EGLContext ctx;
+    if ((ctx = eglCreateContext(dpy, config,
+        share_context, NULL)) == EGL_NO_CONTEXT) {
+        GLES2_PRINT("\tContext creation failed!\n");
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLContext(s, 0);
+        return;
+    }
+    GLES2_PRINT("Created at %p!\n", ctx);
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLContext(s, gles2_handle_create(s, ctx));
+}
+
+GLES2_CB(eglDestroyContext)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLContext, ctx_);
+    GLES2_BARRIER_ARG;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    EGLContext ctx = (EGLContext)gles2_handle_get(s, ctx_);
+    gles2_handle_free(s, ctx_);
+    GLES2_PRINT("Destroyed %p!\n", ctx);
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, eglDestroyContext(dpy, ctx));
+}
+
+
+GLES2_CB(eglMakeCurrent)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLSurface, draw_);
+    GLES2_ARG(TEGLSurface, read_);
+    GLES2_ARG(TEGLContext, ctx_);
+    GLES2_BARRIER_ARG;
+    int i;
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    gles2_Surface* draw = (EGLSurface)gles2_handle_get(s, draw_);
+    gles2_Surface* read = (EGLSurface)gles2_handle_get(s, read_);
+    EGLContext ctx = (EGLContext)gles2_handle_get(s, ctx_);
+
+    GLES2_PRINT("Making host context current...\n");
+
+    if (!eglMakeCurrent(dpy,
+        draw ? draw->surf : NULL,
+        read ? read->surf : NULL,
+        ctx)) {
+        GLES2_PRINT("\tMakeCurrent failed!\n");
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLBoolean(s, EGL_FALSE);
+        return;
+    }
+
+    // Initialize client state.
+    if (ctx) {
+        c->narrays = 0;
+        int vattrib_num = 0;
+
+        glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &vattrib_num);
+        GLES2_PRINT("Maximum number of host vertex arrays: %d.\n", vattrib_num);
+
+        c->narrays += vattrib_num;
+
+        // for gles v1.
+        c->narrays += 4; //glVertexPointer, glNormalPointer, glColorPointer, glTexCoordPointer
+
+        c->arrays = malloc(c->narrays * sizeof(*c->arrays));
+        for (i = 0; i < c->narrays; ++i) {
+            c->arrays[i].type = GL_NONE;
+            c->arrays[i].enabled = 0;
+            c->arrays[i].ptr = 0;
+            c->arrays[i].apply = 0;
+            c->arrays[i].tptr = 0;
+        }
+    }
+
+    GLES2_PRINT("Made %p current (DRAW = %d, READ = %d)!\n", ctx, draw ? draw->id : 0, read ? read->id : 0);
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, EGL_TRUE);
+}
+
+GLES2_CB(eglSwapBuffers)
+{
+    GLES2_ARG(TEGLDisplay, dpy_);
+    GLES2_ARG(TEGLSurface, surface_);
+
+    EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+    gles2_Surface* fsurf = (EGLSurface)gles2_handle_get(s, surface_);
+    if (!fsurf) {
+        fprintf(stderr, "ERROR: Trying to swap NULL surface!\n");
+        GLES2_BARRIER_ARG;
+        GLES2_BARRIER_RET;
+        gles2_ret_TEGLBoolean(s, EGL_TRUE);
+        return;
+    }
+
+    if (gles2_surface_update(s, fsurf)) {
+        GLES2_BARRIER_ARG;
+        GLES2_PRINT("DIMENSIONS CHANGED!\n");
+        glFinish();
+        free(fsurf->ddraw.pixels);
+        unsigned nbytes = fsurf->ddraw.width
+            * fsurf->ddraw.height*fsurf->ddraw.bpp;
+        fsurf->ddraw.pixels = malloc(nbytes);
+
+        gles2_transfer_free(&fsurf->tfr);
+        GLES2_BARRIER_RET;
+        gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+        eglSwapBuffers(dpy, fsurf->surf);
+        gles2_ret_TEGLBoolean(s, EGL_TRUE);
+        return;
+    }
+    GLES2_BARRIER_ARG;
+
+    GLES2_PRINT("Swapping DGLES2 surface ID = %d!\n", fsurf->id);
+    eglSwapBuffers(dpy, fsurf->surf);
+
+    GLES2_PRINT("Transferring frame!\n");
+    gles2_transfer_exec(&fsurf->tfr, s, fsurf->ddraw.pixels, 1);
+    GLES2_PRINT("\tDone!\n");
+    GLES2_BARRIER_RET;
+    gles2_ret_TEGLBoolean(s, EGL_TRUE);
+}
+
+GLES2_CB(glClearColor)
+{
+    GLES2_ARG(TGLclampf, red);
+    GLES2_ARG(TGLclampf, green);
+    GLES2_ARG(TGLclampf, blue);
+    GLES2_ARG(TGLclampf, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClearColor(red, green, blue, alpha);
+}
+
+
+GLES2_CB(glClear)
+{
+    GLES2_ARG(TGLbitfield, mask);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClear(mask);
+}
+
+GLES2_CB(glDisableVertexAttribArray)
+{
+    GLES2_ARG(TGLuint, index);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Disabling array %d\n", index);
+    c->arrays[index].enabled = 0;
+    glDisableVertexAttribArray(index);
+}
+
+static void fglTransferArrays(gles2_State *s, gles2_Client *c,
+    GLint first, GLsizei count)
+{
+    int i;
+
+    for(i = 0; i < c->narrays; ++i) {
+        gles2_Array* va = c->arrays + i;
+        if(!va->enabled) {
+            continue;
+        }
+        unsigned esize = 1;
+        switch (va->type) {
+            case GL_BYTE:
+            case GL_UNSIGNED_BYTE:  esize = 1; break;
+            case GL_SHORT:
+            case GL_UNSIGNED_SHORT: esize = 2; break;
+            case GL_FIXED:
+            case GL_FLOAT:          esize = 4; break;
+
+            default:
+                GLES2_PRINT("ERROR: Unknown type 0x%x in fglTransferArrays!\n", va->type);
+                exit(0);
+                break;
+
+        }                  
+        if (!va->stride) {
+            va->stride = va->size*esize;
+        }
+        va->real_stride = va->size*esize;
+
+        if (va->ptr) {
+            free(va->ptr);
+        }
+        unsigned nbytes = esize*count*va->size; 
+        va->ptr = malloc(nbytes);
+
+        GLsizei j;
+        for (j = 0; j < count; ++j) {
+            signed k;
+            for (k = 0; k < va->size; ++k) {
+                switch (esize) {
+                    case 1:
+                        ((TGLubyte*)va->ptr)[j*va->size + k] =
+                            gles2_get_byte(s, va->tptr + va->stride*(first + j)
+                            + k*sizeof(TGLubyte));
+                        break;
+                    case 2:
+                        ((TGLushort*)va->ptr)[j*va->size + k] =
+                            gles2_get_word(s, va->tptr + va->stride*(first + j)
+                            + k*sizeof(TGLushort));
+                        break;
+                    case 4:
+                        if(va->type == GL_FLOAT) {
+                            ((TGLfloat*)va->ptr)[j*va->size + k] =
+                                gles2_get_float(s, va->tptr
+                                + va->stride*(first + j)
+                                + k*sizeof(TGLfloat));
+                        } else {
+                            ((TGLuint*)va->ptr)[j*va->size + k] =
+                                gles2_get_dword(s, va->tptr
+                                + va->stride*(first + j)
+                                + k*sizeof(TGLuint));
+                        }
+                        break;
+                }
+            }
+        }
+        
+        va->apply(va);
+    }
+}
+
+static void fglApplyVertexAttrib(gles2_Array *va)
+{
+    glVertexAttribPointer(va->indx, va->size, va->type,
+        va->normalized, 0, va->ptr);
+    GLenum error;
+
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        GLES2_PRINT("glVertexAttribPointer(%d, %d, 0x%x, 0, %d, %p\n)"
+            " failed with 0x%x!\n", va->indx, va->size, va->type,
+            va->normalized, va->ptr, error);
+    }
+}
+
+GLES2_CB(glDrawArrays)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_ARG(TGLint, first);
+    GLES2_ARG(TGLsizei, count);
+
+    fglTransferArrays(s, c, first, count);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDrawArrays(mode, 0, count);
+}
+
+GLES2_CB(glDrawElements)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(Tptr, indicesp);
+
+    (void)indicesp;
+    (void)count;
+    (void)mode;
+
+    GLint indice_size;
+    switch (type) {
+        case GL_UNSIGNED_BYTE: indice_size = sizeof(TGLubyte); break;
+        case GL_UNSIGNED_SHORT: indice_size = sizeof(TGLushort); break;
+        default:
+            fprintf(stderr, "ERROR: Invalid type %d!\n", type);
+            return;
+    }
+
+    int i, first = -1, last = -1;
+    void *copied_indices = malloc(indice_size * count);
+    for (i = 0; i < count; i++) {
+        TGLushort idx = 0;
+        switch (type) {
+            case GL_UNSIGNED_BYTE:
+                idx = gles2_get_byte(s, indicesp++);
+                ((TGLubyte *)copied_indices)[i] = (TGLubyte)idx;
+                break;
+            case GL_UNSIGNED_SHORT:
+                idx = gles2_get_word(s, indicesp);
+                ((TGLushort *)copied_indices)[i] = idx;
+                indicesp += 2;
+                break;
+            default:
+                break;
+        }
+        if (first < 0 || idx < first) {
+            first = idx;
+        }
+        if (last < 0 || idx > last) {
+            last = idx;
+        }
+    }
+    fglTransferArrays(s, c, first, last - first + 1);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDrawElements(mode, count, type, copied_indices);
+    free(copied_indices);
+}
+
+GLES2_CB(glEnableVertexAttribArray)
+{
+    GLES2_ARG(TGLuint, index);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Enabling array %d\n", index);
+    c->arrays[index].enabled = 1;
+    c->arrays[index].apply = fglApplyVertexAttrib;
+    glEnableVertexAttribArray(index);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glGetVertexAttribfv(GLuint index, GLenum pname,
+    GLfloat* params)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetVertexAttribiv(GLuint index, GLenum pname,
+    GLint* params)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv(GLuint index,
+    GLenum pname, void** pointer)
+{
+    DUMMY();
+}
+#endif //0
+
+GLES2_CB(glVertexAttrib1f)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib1f(indx, x);
+}
+
+GLES2_CB(glVertexAttrib1fv)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(Tptr, valuesp);
+    GLfloat x = gles2_get_float(s, valuesp);
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib1f(indx, x);
+}
+
+GLES2_CB(glVertexAttrib2f)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib2f(indx, x, y);
+}
+
+GLES2_CB(glVertexAttrib2fv)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(Tptr, valuesp);
+
+    GLfloat x = gles2_get_float(s, valuesp);
+    GLfloat y = gles2_get_float(s, valuesp + sizeof(TGLfloat));
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib2f(indx, x, y);
+}
+
+GLES2_CB(glVertexAttrib3f)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib3f(indx, x, y, z);
+}
+
+GLES2_CB(glVertexAttrib3fv)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(Tptr, valuesp);
+
+    GLfloat x = gles2_get_float(s, valuesp + 0*sizeof(TGLfloat));
+    GLfloat y = gles2_get_float(s, valuesp + 1*sizeof(TGLfloat));
+    GLfloat z = gles2_get_float(s, valuesp + 2*sizeof(TGLfloat));
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib3f(indx, x, y, z);
+}
+
+GLES2_CB(glVertexAttrib4f)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_ARG(TGLfloat, w);
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib4f(indx, x, y, z, w);
+}
+
+GLES2_CB(glVertexAttrib4fv)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(Tptr, valuesp);
+
+    GLfloat x = gles2_get_float(s, valuesp + 0*sizeof(TGLfloat));
+    GLfloat y = gles2_get_float(s, valuesp + 1*sizeof(TGLfloat));
+    GLfloat z = gles2_get_float(s, valuesp + 2*sizeof(TGLfloat));
+    GLfloat w = gles2_get_float(s, valuesp + 3*sizeof(TGLfloat));
+    GLES2_BARRIER_ARG_NORET;
+
+    glVertexAttrib4f(indx, x, y, z, w);
+}
+
+GLES2_CB(glVertexAttribPointer)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLint, size);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(TGLboolean, normalized);
+    GLES2_ARG(TGLsizei, stride);
+    GLES2_ARG(Tptr, tptr);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Array %d at 0x%x (%d elements every %d bytes)\n",
+        indx, tptr, size, stride);
+
+    gles2_Array *va = c->arrays + indx;
+    va->type = type;
+    va->indx = indx;
+    va->size = size;
+    va->normalized = normalized;
+    va->stride = stride;
+    va->tptr = tptr;
+}
+
+GLES2_CB(glGetVertexAttribPointerv)
+{
+    GLES2_ARG(TGLuint, indx);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, tptr);
+
+    Tptr res = 0;
+
+    if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER)
+        GLES2_PRINT("ERROR: Unknown pname 0x%x in glGetVertexAttribPointerv!\n", pname);
+
+    res = c->arrays[indx].tptr;
+
+    GLES2_BARRIER_RET;
+    gles2_put_Tptr(s, tptr, res);
+}
+
+
+static unsigned gles2_glGetCount(TGLenum pname)
+{
+    unsigned count;
+    switch(pname) {
+        case GL_ACTIVE_TEXTURE: count = 1; break;
+        case GL_ALIASED_LINE_WIDTH_RANGE: count = 2; break;
+        case GL_ALIASED_POINT_SIZE_RANGE: count = 2; break;
+        case GL_ALPHA_BITS: count = 1; break;
+        case GL_ARRAY_BUFFER_BINDING: count = 1; break;
+        case GL_BLEND: count = 1; break;
+        case GL_BLEND_COLOR: count = 4; break;
+        case GL_BLEND_DST_ALPHA: count = 1; break;
+        case GL_BLEND_DST_RGB: count = 1; break;
+        case GL_BLEND_EQUATION_ALPHA: count = 1; break;
+        case GL_BLEND_EQUATION_RGB: count = 1; break;
+        case GL_BLEND_SRC_ALPHA: count = 1; break;
+        case GL_BLEND_SRC_RGB: count = 1; break;
+        case GL_BLUE_BITS: count = 1; break;
+        case GL_COLOR_CLEAR_VALUE: count = 4; break;
+        case GL_COLOR_WRITEMASK: count = 4; break;
+        case GL_COMPRESSED_TEXTURE_FORMATS: count = GL_NUM_COMPRESSED_TEXTURE_FORMATS; break;
+        case GL_CULL_FACE: count = 1; break;
+        case GL_CULL_FACE_MODE: count = 1; break;
+        case GL_CURRENT_PROGRAM: count = 1; break;
+        case GL_DEPTH_BITS: count = 1; break;
+        case GL_DEPTH_CLEAR_VALUE: count = 1; break;
+        case GL_DEPTH_FUNC: count = 1; break;
+        case GL_DEPTH_RANGE: count = 2; break;
+        case GL_DEPTH_TEST: count = 1; break;
+        case GL_DEPTH_WRITEMASK: count = 1; break;
+        case GL_DITHER: count = 1; break;
+        case GL_ELEMENT_ARRAY_BUFFER_BINDING: count = 1; break;
+        case GL_FRAMEBUFFER_BINDING: count = 1; break;
+        case GL_FRONT_FACE: count = 1; break;
+        case GL_GENERATE_MIPMAP_HINT: count = 1; break;
+        case GL_GREEN_BITS: count = 1; break;
+        case GL_IMPLEMENTATION_COLOR_READ_FORMAT: count = 1; break;
+        case GL_IMPLEMENTATION_COLOR_READ_TYPE: count = 1; break;
+        case GL_LINE_WIDTH: count = 1; break;
+        case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: count = 1; break;
+        case GL_MAX_CUBE_MAP_TEXTURE_SIZE: count = 1; break;
+        case GL_MAX_FRAGMENT_UNIFORM_VECTORS: count = 1; break;
+        case GL_MAX_RENDERBUFFER_SIZE: count = 1; break;
+        case GL_MAX_TEXTURE_IMAGE_UNITS: count = 1; break;
+        case GL_MAX_TEXTURE_SIZE: count = 1; break;
+        case GL_MAX_VARYING_VECTORS: count = 1; break;
+        case GL_MAX_VERTEX_ATTRIBS: count = 1; break;
+        case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: count = 1; break;
+        case GL_MAX_VERTEX_UNIFORM_VECTORS: count = 1; break;
+        case GL_MAX_VIEWPORT_DIMS: count = 2; break;
+        case GL_NUM_COMPRESSED_TEXTURE_FORMATS: count = 1; break;
+        case GL_NUM_SHADER_BINARY_FORMATS: count = 1; break;
+        case GL_PACK_ALIGNMENT: count = 1; break;
+        case GL_POLYGON_OFFSET_FACTOR: count = 1; break;
+        case GL_POLYGON_OFFSET_FILL: count = 1; break;
+        case GL_POLYGON_OFFSET_UNITS: count = 1; break;
+        case GL_RED_BITS: count = 1; break;
+        case GL_RENDERBUFFER_BINDING: count = 1; break;
+        case GL_SAMPLE_BUFFERS: count = 1; break;
+        case GL_SAMPLE_COVERAGE_INVERT: count = 1; break;
+        case GL_SAMPLE_COVERAGE_VALUE: count = 1; break;
+        case GL_SAMPLES: count = 1; break;
+        case GL_SCISSOR_BOX: count = 4; break;
+        case GL_SCISSOR_TEST: count = 1; break;
+        case GL_SHADER_BINARY_FORMATS: count = GL_NUM_SHADER_BINARY_FORMATS; break;
+        case GL_SHADER_COMPILER: count = 1; break;
+        case GL_STENCIL_BACK_FAIL: count = 1; break;
+        case GL_STENCIL_BACK_FUNC: count = 1; break;
+        case GL_STENCIL_BACK_PASS_DEPTH_FAIL: count = 1; break;
+        case GL_STENCIL_BACK_PASS_DEPTH_PASS: count = 1; break;
+        case GL_STENCIL_BACK_REF: count = 1; break;
+        case GL_STENCIL_BACK_VALUE_MASK: count = 1; break;
+        case GL_STENCIL_BACK_WRITEMASK: count = 1; break;
+        case GL_STENCIL_BITS: count = 1; break;
+        case GL_STENCIL_CLEAR_VALUE: count = 1; break;
+        case GL_STENCIL_FAIL: count = 1; break;
+        case GL_STENCIL_FUNC: count = 1; break;
+        case GL_STENCIL_PASS_DEPTH_FAIL: count = 1; break;
+        case GL_STENCIL_PASS_DEPTH_PASS: count = 1; break;
+        case GL_STENCIL_REF: count = 1; break;
+        case GL_STENCIL_TEST: count = 1; break;
+        case GL_STENCIL_VALUE_MASK: count = 1; break;
+        case GL_STENCIL_WRITEMASK: count = 1; break;
+        case GL_SUBPIXEL_BITS: count = 1; break;
+        case GL_TEXTURE_BINDING_2D: count = 1; break;
+        case GL_TEXTURE_BINDING_CUBE_MAP: count = 1; break;
+        case GL_UNPACK_ALIGNMENT: count = 1; break;
+        case GL_VIEWPORT: count = 4; break;
+        default:
+            count = gles1_glGetCount(pname);
+            //GLES2_PRINT("ERROR: Unknown pname 0x%x in glGet!\n", pname);
+            //count = 1;
+            break;
+    }
+
+    GLES2_PRINT("glGet(0x%x) -> %u!\n", pname, count);
+
+    return count;
+}
+
+GLES2_CB(glGetBooleanv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLboolean params[4];
+    glGetBooleanv(pname, params);
+    unsigned const count = gles2_glGetCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLboolean(s, paramsp + i*sizeof(TGLboolean), params[i]);
+    }
+}
+
+GLES2_CB(glGetError)
+{
+    GLES2_BARRIER_ARG;
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLenum(s, glGetError());
+}
+
+GLES2_CB(eglGetError)
+{
+    GLES2_BARRIER_ARG;
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLint(s, eglGetError());
+}
+
+GLES2_CB(glGetFloatv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLfloat params[4];
+    glGetFloatv(pname, params);
+    unsigned const count = gles2_glGetCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetIntegerv)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLint params[4];
+    glGetIntegerv(pname, params);
+    unsigned const count = gles2_glGetCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+    }
+}
+
+GLES2_CB(glColorMask)
+{
+    GLES2_ARG(TGLboolean, red);
+    GLES2_ARG(TGLboolean, green);
+    GLES2_ARG(TGLboolean, blue);
+    GLES2_ARG(TGLboolean, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glColorMask(red, green, blue, alpha);
+}
+
+GLES2_CB(glCullFace)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glCullFace(mode);
+}
+
+GLES2_CB(glDisable)
+{
+    GLES2_ARG(TGLenum, cap);
+    GLES2_BARRIER_ARG;
+
+    glDisable(cap);
+    GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glEnable)
+{
+    GLES2_ARG(TGLenum, cap);
+    GLES2_BARRIER_ARG;
+
+    glEnable(cap);
+
+    GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glFinish)
+{
+    // Important to do this way, so that we don't return too early.
+    GLES2_BARRIER_ARG;
+    glFinish();
+    GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glFlush)
+{
+    GLES2_BARRIER_ARG_NORET;
+    glFlush();
+}
+
+GLES2_CB(glFrontFace)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFrontFace(mode);
+}
+
+GLES2_CB(glIsEnabled)
+{
+    GLES2_ARG(TGLenum, cap);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLboolean(s, glIsEnabled(cap));
+}
+
+GLES2_CB(glHint)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    if(s->quality <= 75)
+    {
+        switch(target)
+        {
+            default: mode = GL_FASTEST; break; 
+        }
+    }
+    
+    glHint(target, mode);
+}
+
+GLES2_CB(glLineWidth)
+{
+    GLES2_ARG(TGLfloat, width);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLineWidth(width);
+}
+
+GLES2_CB(glPolygonOffset)
+{
+    GLES2_ARG(TGLfloat, factor);
+    GLES2_ARG(TGLfloat, units);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPolygonOffset(factor, units);
+}
+
+GLES2_CB(glSampleCoverage)
+{
+    GLES2_ARG(TGLclampf, value);
+    GLES2_ARG(TGLboolean, invert);
+    GLES2_BARRIER_ARG_NORET;
+
+    glSampleCoverage(value, invert);
+}
+
+GLES2_CB(glScissor)
+{
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_BARRIER_ARG_NORET;
+
+    glScissor(x, y, width, height);
+}
+
+GLES2_CB(glViewport)
+{
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_BARRIER_ARG_NORET;
+
+    glViewport(x, y, width, height);
+}
+
+GLES2_CB(glActiveTexture)
+{
+    GLES2_ARG(TGLenum, texture);
+    GLES2_BARRIER_ARG_NORET;
+
+    glActiveTexture(texture);
+}
+
+GLES2_CB(glBindTexture)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLuint, texture);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBindTexture(target, texture);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glCompressedTexImage2D(GLenum target,
+    GLint level, GLenum internalformat, GLsizei width, GLsizei height,
+    GLint border, GLsizei imageSize, const void* data)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D(GLenum target,
+    GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+    GLenum format, GLsizei imageSize, const void* data)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCopyTexImage2D(GLenum target, GLint level,
+    GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height,
+    GLint border)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCopyTexSubImage2D(GLenum target, GLint level,
+    GLint xoffset, GLint yoffset, GLint x, GLint y,
+    GLsizei width, GLsizei height)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glDeleteTextures)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, texturesp);
+
+    GLsizei i;
+    GLuint* textures = (GLuint*)malloc(sizeof(GLuint)*n);
+    for(i = 0; i < n; ++i) {
+        textures[i] = gles2_get_TGLuint(s, texturesp + i*sizeof(TGLuint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glDeleteTextures(n, textures);
+    free(textures);
+}
+
+GLES2_CB(glGenerateMipmap)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_BARRIER_ARG_NORET;
+
+    glGenerateMipmap(target);
+}
+
+GLES2_CB(glGenTextures)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, texturesp);
+    GLES2_BARRIER_ARG;
+
+    GLsizei i;
+    GLuint* textures = (GLuint*)malloc(sizeof(GLuint)*n);
+    glGenTextures(n, textures);
+    GLES2_BARRIER_RET;
+    for(i = 0; i < n; ++i) {
+        gles2_put_TGLuint(s, texturesp + i*sizeof(TGLuint), textures[i]);
+    }
+    free(textures);
+}
+
+static unsigned gles2_glTexParameterCount(GLenum pname)
+{
+    unsigned count;
+
+    switch(pname) {
+        case GL_TEXTURE_MIN_FILTER: count = 1; break;
+        case GL_TEXTURE_MAG_FILTER: count = 1; break;
+        case GL_TEXTURE_WRAP_S: count = 1; break;
+        case GL_TEXTURE_WRAP_T: count = 1; break;
+        default:
+            GLES2_PRINT("ERROR: Unknown texture parameter 0x%x!\n", pname);
+            count = 1;
+            break;
+    }
+    
+    return count;
+}
+
+GLES2_CB(glGetTexParameterfv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLfloat params[4];
+    glGetTexParameterfv(target, pname, params);
+    unsigned const count = gles2_glTexParameterCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetTexParameteriv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLint params[4];
+    glGetTexParameteriv(target, pname, params);
+    unsigned const count = gles2_glTexParameterCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+    }
+}
+
+GLES2_CB(glGetUniformfv)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    const int MAX_SIZE = 16;
+    GLfloat reserve[MAX_SIZE];
+    GLfloat params[MAX_SIZE];
+    gles2_transfer(s, paramsp, sizeof(GLfloat)*MAX_SIZE, params, 0);
+
+    memcpy(reserve, params, sizeof(GLfloat)*MAX_SIZE);
+
+    glGetUniformfv(program, location, params);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < MAX_SIZE; ++i) {
+        if (params[i] != reserve[i])
+            gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetUniformiv)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    const int MAX_SIZE = 16;
+    GLint reserve[MAX_SIZE];
+    GLint params[MAX_SIZE];
+    gles2_transfer(s, paramsp, sizeof(GLint)*MAX_SIZE, params, 0);
+
+    memcpy(reserve, params, sizeof(GLint)*MAX_SIZE);
+
+    glGetUniformiv(program, location, params);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < MAX_SIZE; ++i) {
+        if (params[i] != reserve[i])
+            gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+    }
+}
+
+static unsigned gles2_glGetVertexAttribCount(GLenum pname)
+{
+    unsigned count;
+
+    switch(pname) {
+        case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: count = 1; break;
+        case GL_VERTEX_ATTRIB_ARRAY_ENABLED: count = 1; break;
+        case GL_VERTEX_ATTRIB_ARRAY_SIZE: count = 1; break;
+        case GL_VERTEX_ATTRIB_ARRAY_STRIDE: count = 1; break;
+        case GL_VERTEX_ATTRIB_ARRAY_TYPE: count = 1; break;
+        case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: count = 1; break;
+        case GL_CURRENT_VERTEX_ATTRIB: count = 4; break;
+        default:
+            GLES2_PRINT("ERROR: Unknown texture parameter 0x%x!\n", pname);
+            count = 1;
+            break;
+    }
+
+    return count;
+}
+
+GLES2_CB(glGetVertexAttribfv)
+{
+    GLES2_ARG(TGLuint, index);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    const int MAX_SIZE = 4;
+    GLfloat params[MAX_SIZE];
+
+    glGetVertexAttribfv(index, pname, params);
+    unsigned count = gles2_glGetVertexAttribCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+    }
+}
+
+GLES2_CB(glGetVertexAttribiv)
+{
+    GLES2_ARG(TGLuint, index);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    const int MAX_SIZE = 4;
+    GLint params[MAX_SIZE];
+
+    glGetVertexAttribiv(index, pname, params);
+    unsigned count = gles2_glGetVertexAttribCount(pname);
+    unsigned i;
+    GLES2_BARRIER_RET;
+    for(i = 0; i < count; ++i) {
+        gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+    }
+}
+
+GLES2_CB(glIsTexture)
+{
+    GLES2_ARG(TGLuint, texture);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLboolean(s, glIsTexture(texture));
+}
+
+
+
+GLES2_CB(glReadPixels)
+{
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLenum, format);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(Tptr, pixelsp);
+    GLES2_BARRIER_ARG;
+
+    unsigned bpp;
+    switch (format) {
+        case GL_ALPHA: bpp = 1; break;
+        case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+        case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+        case GL_LUMINANCE: bpp = 1; break;
+        case GL_LUMINANCE_ALPHA: bpp = 2; break;
+        default:
+            GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+            bpp = 1;
+            break;
+    }
+
+    GLES2_PRINT("Reading %dx%dx%d image at %d,%d...\n",
+        width, height, bpp, x, y);
+    char* pixels = NULL;
+    unsigned nbytes = width*height*bpp;
+    pixels = malloc(nbytes);
+
+    glReadPixels(x, y, width, height, format, type, pixels);
+    GLES2_BARRIER_RET;
+    gles2_transfer(s, pixelsp, nbytes, pixels, 1);
+    free(pixels);
+}
+
+GLES2_CB(glReleaseShaderCompiler)
+{
+    GLES2_BARRIER_ARG_NORET;
+
+    glReleaseShaderCompiler();
+}
+
+GLES2_CB(glTexImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLint, internalformat);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLint, border);
+    GLES2_ARG(TGLenum, format);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(Tptr, pixelsp);
+
+    unsigned bpp;
+
+    switch(format) {
+        case GL_ALPHA: bpp = 1; break;
+        case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+        case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+        case GL_LUMINANCE: bpp = 1; break;
+        case GL_LUMINANCE_ALPHA: bpp = 2; break;
+        default:
+            GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+            bpp = 1;
+            break;
+    }
+
+    GLES2_PRINT("Uploading %dx%dx%d image...\n", width, height, bpp);
+    char* pixels = NULL;
+    if (pixelsp) {
+        unsigned nbytes = width*height*bpp;
+        pixels = malloc(nbytes);
+        gles2_transfer(s, pixelsp, nbytes, pixels, 0);
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+    free(pixels);
+}
+
+GLES2_CB(glTexParameterf)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLfloat, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexParameterf(target, pname, param);
+}
+
+GLES2_CB(glTexParameterfv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+  
+    GLfloat params[4];
+    unsigned const count = gles2_glTexParameterCount(pname);
+    unsigned i;
+    for (i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexParameterfv(target, pname, params);
+}
+
+GLES2_CB(glTexParameteri)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLint, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    if(s->quality <= 50)
+    {
+        switch(pname)
+        {
+            case GL_TEXTURE_MIN_FILTER: param = GL_NEAREST; break;
+            case GL_TEXTURE_MAG_FILTER: param = GL_NEAREST; break;
+            default: break;
+        }
+    }
+    
+    glTexParameterf(target, pname, param);
+}
+
+GLES2_CB(glTexParameteriv)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+
+    GLint params[4];
+    unsigned const count = gles2_glTexParameterCount(pname);
+    unsigned i;
+    for(i = 0; i < count; ++i) {
+        params[i] = gles2_get_TGLint(s, paramsp + i*sizeof(GLint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glTexParameteriv(target, pname, params);
+}
+
+GLES2_CB(glTexSubImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLint, xoffset);
+    GLES2_ARG(TGLint, yoffset);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLenum, format);
+    GLES2_ARG(TGLenum, type);
+    GLES2_ARG(Tptr, pixelsp);
+
+    unsigned bpp;
+    switch (format) {
+        case GL_ALPHA: bpp = 1; break;
+        case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+        case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+        case GL_LUMINANCE: bpp = 1; break;
+        case GL_LUMINANCE_ALPHA: bpp = 2; break;
+        default:
+            GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+            bpp = 1;
+            break;
+    }
+
+    GLES2_PRINT("Uploading partial %dx%dx%d image at %d,%d...\n",
+    width, height, bpp, xoffset, yoffset);
+
+    unsigned nbytes = width*height*bpp;
+    char* pixels = malloc(nbytes);
+    gles2_transfer(s, pixelsp, nbytes, pixels, 0);
+    GLES2_BARRIER_ARG_NORET;
+
+    glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+    free(pixels);
+}
+
+
+GLES2_CB(glCompileShader)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_BARRIER_ARG_NORET;
+
+    glCompileShader(shader);
+}
+
+GLES2_CB(glCompressedTexImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLenum, internalformat);
+    GLES2_ARG(TGLint, border);
+    GLES2_ARG(TGLsizei, imageSize);
+    GLES2_ARG(Tptr, data);
+
+    GLES2_PRINT("Uploading compressed %dx%d image with size 0x%x and border %d...\n",
+    width, height, xoffset, yoffset, imageSize, border);
+
+    char* pixels = malloc(imageSize);
+    gles2_transfer(s, data, imageSize, pixels, 0);
+    GLES2_BARRIER_ARG_NORET;
+
+    glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, pixels);
+    free(pixels);
+}
+
+GLES2_CB(glCopyTexImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLenum, internalformat);
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLint, border);
+
+    GLES2_BARRIER_ARG;
+
+    glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+
+    GLES2_BARRIER_RET;
+}
+
+
+GLES2_CB(glCompressedTexSubImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLint, xoffset);
+    GLES2_ARG(TGLint, yoffset);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_ARG(TGLenum, format);
+    GLES2_ARG(TGLsizei, imageSize);
+    GLES2_ARG(Tptr, data);
+
+    GLES2_PRINT("Uploading compressed partial %dx%d image at %d,%d with size 0x%x...\n",
+    width, height, xoffset, yoffset, imageSize);
+
+    char* pixels = malloc(imageSize);
+    gles2_transfer(s, data, imageSize, pixels, 0);
+    GLES2_BARRIER_ARG_NORET;
+
+    glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, pixels);
+    free(pixels);
+}
+
+
+GLES2_CB(glCreateShader)
+{
+    GLES2_ARG(TGLenum, type);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLuint(s, glCreateShader(type));
+}
+
+GLES2_CB(glDeleteShader)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDeleteShader(shader);
+}
+
+GLES2_CB(glIsShader)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLboolean(s, glIsShader(shader));
+}
+
+GLES2_CB(glGetShaderiv)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLint param;
+    glGetShaderiv(shader, pname, &param);
+    GLES2_BARRIER_RET;
+    gles2_put_TGLint(s, paramsp, param);
+}
+
+GLES2_CB(glGetShaderInfoLog)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_ARG(TGLsizei, bufsize);
+    GLES2_ARG(Tptr, lengthp);
+    GLES2_ARG(Tptr, infologp);
+
+    GLsizei length = gles2_get_TGLsizei(s, lengthp);
+    char* infolog = malloc(bufsize);
+    glGetShaderInfoLog(shader, bufsize, &length, infolog);
+    gles2_transfer(s, infologp, length, infolog, 1);
+    gles2_put_TGLsizei(s, lengthp, length);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("shader %d infolog:\n%.*s\n", shader, length, infolog);
+    free(infolog);
+}
+
+GLES2_CB(glGetShaderPrecisionFormat)
+{
+    GLES2_ARG(TGLenum, shadertype);
+    GLES2_ARG(TGLenum, precisiontype);
+    GLES2_ARG(Tptr, rangep);
+    GLES2_ARG(Tptr, precisionp);
+
+    GLint range[2] = {0};
+    GLint precision = 0;
+
+    glGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision);
+
+    gles2_put_TGLint(s, rangep, range[0]);
+    gles2_put_TGLint(s, rangep + sizeof(TGLint), range[1]);
+    gles2_put_TGLint(s, precisionp, precision);
+
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("shadertype %d precisiontype %d: range = (%d, %d), "
+            "precision = %d\n", shadertype, precisiontype, range[0],
+            range[1], precision);
+}
+
+GLES2_CB(glGetShaderSource)
+{
+    /* If host OpenGL driver doesn't support some of keywords,
+     * before call glShaderSource gles library cut these keywords.
+     * In this case host OpenGL driver returns changed source.
+     * May be it should be fixed.
+     * */
+    GLES2_ARG(TGLuint, shader);
+    GLES2_ARG(TGLsizei, bufsize);
+    GLES2_ARG(Tptr, lengthp);
+    GLES2_ARG(Tptr, sourcep);
+
+    GLsizei length = lengthp ? gles2_get_TGLsizei(s, lengthp) : 0;
+    char* source = malloc(bufsize);
+    glGetShaderSource(shader, bufsize, &length, source);
+    gles2_transfer(s, sourcep, length, source, 1);
+    gles2_put_TGLsizei(s, lengthp, length);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("shader %d source:\n%.*s\n", shader, length, source);
+    free(source);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat(GLenum shadertype,
+    GLenum precisiontype, GLint* range, GLint* precision)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetShaderSource(GLuint shader, GLsizei bufsize,
+    GLsizei* length, char* source)
+{
+    DUMMY();
+}
+
+GL_APICALL GLboolean GL_APIENTRY glIsShader(GLuint shader)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glReleaseShaderCompiler(void)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glShaderBinary(GLsizei n, const GLuint* shaders,
+    GLenum binaryformat, const void* binary, GLsizei length)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glShaderSource)
+{
+    GLES2_ARG(TGLuint, shader);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, stringp);
+    GLES2_ARG(Tptr, lengthp);
+
+    char** string_fgl = malloc(sizeof(char*)*count);
+    GLint* length_fgl = malloc(sizeof(GLint)*count);
+
+    unsigned i;
+    for (i = 0; i < count; ++i) {
+        length_fgl[i] = gles2_get_TGLint(s, lengthp + i*sizeof(TGLint));
+        string_fgl[i] = malloc(length_fgl[i] + 1);
+        gles2_transfer(s, gles2_get_dword(s, stringp + i*sizeof(Tptr)),
+            length_fgl[i], string_fgl[i], 0);
+        string_fgl[i][length_fgl[i]] = 0;
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("shader %d source:\n", shader);
+    #if(GLES2_DEBUG == 1)
+    for(i = 0; i < count; ++i) {
+        fprintf(stderr, "%.*s", length_fgl[i], string_fgl[i]);
+    }
+    #endif // GLES2_DEBUG == 1
+    GLES2_PRINT("\n--END--");
+
+    glShaderSource(shader, (GLsizei)count,
+        (const char**)string_fgl, length_fgl);
+
+    for (i = 0; i < count; ++i) {
+        free(string_fgl[i]);
+    }
+    
+    free(string_fgl);
+    free(length_fgl);
+}
+
+GLES2_CB(glAttachShader)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLuint, shader);
+    GLES2_BARRIER_ARG_NORET;
+
+    glAttachShader(program, shader);
+}
+
+GLES2_CB(glBindAttribLocation)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLuint, index);
+    GLES2_ARG(Tptr, namep);
+
+    char name[120];
+    Tptr i;
+
+    for(i = 0; (name[i] = gles2_get_byte(s, namep + i)) ; ++i);
+    GLES2_BARRIER_ARG_NORET;
+
+    GLES2_PRINT("Binding attribute %s at %d...\n", name, index);
+    glBindAttribLocation(program, index, name);
+}
+
+GLES2_CB(glCreateProgram)//(void)
+{
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLuint(s, glCreateProgram());
+}
+
+GLES2_CB(glCopyTexSubImage2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLint, level);
+    GLES2_ARG(TGLint, xoffset);
+    GLES2_ARG(TGLint, yoffset);
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+
+    GLES2_BARRIER_ARG;
+
+    glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+
+    GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glDeleteProgram)//(GLuint program)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDeleteProgram(program);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glDetachShader(GLuint program, GLuint shader)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index,
+    GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glGetActiveAttrib)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLuint, index);
+    GLES2_ARG(TGLsizei, bufsize);
+    GLES2_ARG(Tptr, lengthp); // GLsizei*
+    GLES2_ARG(Tptr, sizep); // GLint*
+    GLES2_ARG(Tptr, typep); // GLenum*
+    GLES2_ARG(Tptr, namep); // char*
+    GLES2_BARRIER_ARG;
+
+    char* name = malloc(bufsize);
+    GLsizei length = 0;
+    GLint size = 0;
+    GLenum type = 0;
+    Tptr i;
+
+    glGetError();
+    glGetActiveAttrib(program, index, bufsize, &length, &size, &type, name);
+    GLES2_PRINT("Active attrib: %s\n", name);
+
+    GLES2_BARRIER_RET;
+    if(lengthp)
+        gles2_put_TGLsizei(s, lengthp, length);
+    gles2_put_TGLint(s, sizep, size);
+    gles2_put_TGLenum(s, typep, type);
+    (void) (namep + i);
+
+    for (i = 0; i < length; ++i) {
+        gles2_put_byte(s, namep + i, name[i]);
+    }
+    if(i < bufsize)
+        gles2_put_byte(s, namep + i, 0);
+
+    free(name);
+}
+
+GLES2_CB(glGetActiveUniform)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLuint, index);
+    GLES2_ARG(TGLsizei, bufsize);
+    GLES2_ARG(Tptr, lengthp); // GLsizei*
+    GLES2_ARG(Tptr, sizep); // GLint*
+    GLES2_ARG(Tptr, typep); // GLenum*
+    GLES2_ARG(Tptr, namep); // char*
+    GLES2_BARRIER_ARG;
+
+    char* name = malloc(bufsize);
+    GLsizei length = 0;
+    GLint size = 0;
+    GLenum type = 0;
+    Tptr i;
+
+    glGetError();
+    glGetActiveUniform(program, index, bufsize, &length, &size, &type, name);
+    GLES2_PRINT("Active uniform: %s\n", name);
+
+    GLES2_BARRIER_RET;
+    if(lengthp)
+        gles2_put_TGLsizei(s, lengthp, length);
+    gles2_put_TGLint(s, sizep, size);
+    gles2_put_TGLenum(s, typep, type);
+    (void) (namep + i);
+
+    for (i = 0; i < length; ++i) {
+        gles2_put_byte(s, namep + i, name[i]);
+    }
+    if(i < bufsize)
+        gles2_put_byte(s, namep + i, 0);
+
+    free(name);
+}
+
+GLES2_CB(glGetAttachedShaders)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLsizei, maxCount);
+    GLES2_ARG(Tptr, countp); // GLsizei*
+    GLES2_ARG(Tptr, shadersp); // GLuint*
+    GLES2_BARRIER_ARG;
+
+    GLuint* shaders = malloc(maxCount);
+    GLsizei count = 0;
+
+    glGetAttachedShaders(program, maxCount, &count, shaders);
+
+    GLES2_BARRIER_RET;
+    gles2_put_TGLsizei(s, countp, count);
+
+    unsigned i;
+    for (i = 0; i < count; ++i) {
+        gles2_put_TGLuint(s, shadersp + i*sizeof(TGLuint), shaders[i]);
+    }
+
+    free(shaders);
+}
+
+
+#if 0
+GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program,
+    GLsizei maxcount, GLsizei* count, GLuint* shaders)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glGetAttribLocation)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(Tptr, namep);
+
+    char name[120];
+    Tptr i;
+
+    for (i = 0; (name[i] = gles2_get_byte(s, namep + i)) ; ++i);
+
+    GLES2_PRINT("Getting attribute %s location...\n", name);
+
+    gles2_ret_TGLint(s, glGetAttribLocation(program, name));
+    GLES2_BARRIER_ARG_NORET;
+}
+
+GLES2_CB(glGetProgramiv)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(Tptr, paramsp);
+    GLES2_BARRIER_ARG;
+
+    GLint param;
+
+    glGetProgramiv(program, pname, &param);
+    GLES2_BARRIER_RET;
+    gles2_put_TGLint(s, paramsp, param);
+}
+
+GLES2_CB(glGetProgramInfoLog)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLsizei, bufsize);
+    GLES2_ARG(Tptr, lengthp);
+    GLES2_ARG(Tptr, infologp);
+
+    GLsizei length = gles2_get_TGLsizei(s, lengthp);
+    char* infolog = malloc(bufsize);
+    glGetProgramInfoLog(program, bufsize, &length, infolog);
+    gles2_transfer(s, infologp, length, infolog, 1);
+    gles2_put_TGLsizei(s, lengthp, length);
+    GLES2_BARRIER_ARG_NORET;
+    GLES2_PRINT("program %d infolog:\n%.*s\n", program, length, infolog);
+    free(infolog);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glGetUniformfv(GLuint program, GLint location, GLfloat* params)
+{
+    DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetUniformiv(GLuint program, GLint location, GLint* params)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glGetUniformLocation)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(Tptr, namep);
+
+    char name[120];
+    Tptr i;
+
+    for (i = 0; (name[i] = gles2_get_byte(s, namep + i)) ; ++i);
+
+    GLES2_PRINT("Getting uniform %s location...\n", name);
+
+    gles2_ret_TGLint(s, glGetUniformLocation(program, name));
+    GLES2_BARRIER_ARG_NORET;
+}
+
+GLES2_CB(glIsProgram)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLboolean(s, glIsProgram(program));
+}
+
+GLES2_CB(glLinkProgram)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_BARRIER_ARG_NORET;
+
+    glLinkProgram(program);
+}
+
+GLES2_CB(glUniform1f)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform1f(location, x);
+}
+
+GLES2_CB(glUniform1fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+
+    unsigned nvs = count*1;
+    GLfloat* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+
+    for (i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLint(s, vp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform1fv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform1i)//(GLint location, GLint x)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, x);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform1i(location, x);
+}
+
+GLES2_CB(glUniform1iv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+
+    unsigned nvs = count*1;
+    GLint* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for (i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLint(s, vp + i*sizeof(TGLint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform1iv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform2f)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform2f(location, x, y);
+}
+
+GLES2_CB(glUniform2fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+    unsigned nvs = count*2;
+    GLfloat* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for (i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLfloat(s, vp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform2fv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform2i)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform2i(location, x, y);
+}
+
+GLES2_CB(glUniform2iv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+
+    unsigned nvs = count*2;
+    GLint* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for (i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLint(s, vp + i*sizeof(TGLint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glUniform2iv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform3f)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform3f(location, x, y, z);
+}
+
+GLES2_CB(glUniform3fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+    unsigned nvs = count*3;
+    GLfloat* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for(i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLfloat(s, vp + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+   
+    glUniform3fv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform3i)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLint, z);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform3i(location, x, y, z);
+}
+
+GLES2_CB(glUniform3iv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+
+    unsigned nvs = count*3;
+    GLint* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for(i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLint(s, vp + i*sizeof(TGLint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glUniform3iv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform4f)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLfloat, x);
+    GLES2_ARG(TGLfloat, y);
+    GLES2_ARG(TGLfloat, z);
+    GLES2_ARG(TGLfloat, w);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform4f(location, x, y, z, w);
+}
+
+GLES2_CB(glUniform4fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+    unsigned nvs = count*4;
+    GLfloat* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for(i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLfloat(s, vp + i*sizeof(TGLfloat));
+    }
+    
+    GLES2_BARRIER_ARG_NORET;
+    glUniform4fv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniform4i)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, x);
+    GLES2_ARG(TGLint, y);
+    GLES2_ARG(TGLint, z);
+    GLES2_ARG(TGLint, w);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniform4i(location, x, y, z, w);
+}
+
+GLES2_CB(glUniform4iv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLsizei, count);
+    GLES2_ARG(Tptr, vp);
+
+    unsigned nvs = count*4;
+    GLint* v = malloc(nvs*sizeof(*v));
+    unsigned i;
+    for(i = 0; i < nvs; ++i) {
+        v[i] = gles2_get_TGLint(s, vp + i*sizeof(TGLint));
+    }
+    
+    GLES2_BARRIER_ARG_NORET;
+    glUniform4iv(location, count, v);
+    free(v);
+}
+
+GLES2_CB(glUniformMatrix2fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, count);
+    GLES2_ARG(TGLboolean, transpose);
+    GLES2_ARG(Tptr, valuep);
+
+    unsigned nfloats = 2*2*count;
+    GLfloat* value = malloc(nfloats*sizeof(TGLfloat));
+    unsigned i;
+
+    for (i = 0; i < nfloats; ++i) {
+        value[i] = gles2_get_TGLfloat(s, valuep + i*sizeof(TGLfloat));
+       }
+
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniformMatrix2fv(location, count, transpose, value);
+    free(value);
+}
+
+GLES2_CB(glUniformMatrix3fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, count);
+    GLES2_ARG(TGLboolean, transpose);
+    GLES2_ARG(Tptr, valuep);
+
+    unsigned nfloats = 3*3*count;
+    GLfloat* value = malloc(nfloats*sizeof(TGLfloat));
+    unsigned i;
+    for(i = 0; i < nfloats; ++i) {
+        value[i] = gles2_get_TGLfloat(s, valuep + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+
+    glUniformMatrix3fv(location, count, transpose, value);
+    free(value);
+}
+
+GLES2_CB(glUniformMatrix4fv)
+{
+    GLES2_ARG(TGLint, location);
+    GLES2_ARG(TGLint, count);
+    GLES2_ARG(TGLboolean, transpose);
+    GLES2_ARG(Tptr, valuep);
+
+    unsigned nfloats = 4*4*count;
+    GLfloat* value = malloc(nfloats*sizeof(TGLfloat));
+    unsigned i;
+    for(i = 0; i < nfloats; ++i) {
+        value[i] = gles2_get_TGLfloat(s, valuep + i*sizeof(TGLfloat));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glUniformMatrix4fv(location, count, transpose, value);
+    free(value);
+}
+
+GLES2_CB(glUseProgram)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_BARRIER_ARG_NORET;
+
+    glUseProgram(program);
+}
+
+GLES2_CB(glValidateProgram)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_BARRIER_ARG_NORET;
+
+    glValidateProgram(program);
+}
+
+GLES2_CB(glBlendColor)
+{
+    GLES2_ARG(TGLclampf, red);
+    GLES2_ARG(TGLclampf, green);
+    GLES2_ARG(TGLclampf, blue);
+    GLES2_ARG(TGLclampf, alpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBlendColor(red, green, blue, alpha);
+}
+
+GLES2_CB(glBlendEquation)
+{
+    GLES2_ARG(TGLenum, mode);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBlendEquation(mode);
+}
+
+GLES2_CB(glBlendEquationSeparate)
+{
+    GLES2_ARG(TGLenum, modeRGB);
+    GLES2_ARG(TGLenum, modeAlpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBlendEquationSeparate(modeRGB, modeAlpha);
+}
+
+GLES2_CB(glBlendFunc)
+{
+    GLES2_ARG(TGLenum, sfactor);
+    GLES2_ARG(TGLenum, dfactor);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBlendFunc(sfactor, dfactor);
+}
+
+GLES2_CB(glBlendFuncSeparate)
+{
+    GLES2_ARG(TGLenum, srcRGB);
+    GLES2_ARG(TGLenum, dstRGB);
+    GLES2_ARG(TGLenum, srcAlpha);
+    GLES2_ARG(TGLenum, dstAlpha);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+
+GLES2_CB(glPixelStorei)
+{
+    GLES2_ARG(TGLenum, pname);
+    GLES2_ARG(TGLint, param);
+    GLES2_BARRIER_ARG_NORET;
+
+    glPixelStorei(pname, param);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program)
+{
+    DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glClearStencil)
+{
+    GLES2_ARG(TGLint, s_);
+    GLES2_BARRIER_ARG_NORET;
+
+    glClearStencil(s_);
+}
+
+GLES2_CB(glStencilFunc)
+{
+    GLES2_ARG(TGLenum, func);
+    GLES2_ARG(TGLint, ref);
+    GLES2_ARG(TGLuint, mask);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilFunc(func, ref, mask);
+}
+
+GLES2_CB(glStencilFuncSeparate)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, func);
+    GLES2_ARG(TGLint, ref);
+    GLES2_ARG(TGLuint, mask);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilFuncSeparate(face, func, ref, mask);
+}
+
+GLES2_CB(glStencilMask)
+{
+    GLES2_ARG(TGLuint, mask);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilMask(mask);
+}
+
+GLES2_CB(glStencilMaskSeparate)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLuint, mask);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilMaskSeparate(face, mask);
+}
+
+GLES2_CB(glStencilOp)
+{
+    GLES2_ARG(TGLenum, fail);
+    GLES2_ARG(TGLenum, zfail);
+    GLES2_ARG(TGLenum, zpass);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilOp(fail, zfail, zpass);
+}
+
+GLES2_CB(glStencilOpSeparate)
+{
+    GLES2_ARG(TGLenum, face);
+    GLES2_ARG(TGLenum, fail);
+    GLES2_ARG(TGLenum, zfail);
+    GLES2_ARG(TGLenum, zpass);
+    GLES2_BARRIER_ARG_NORET;
+
+    glStencilOpSeparate(face, fail, zfail, zpass);
+}
+
+GLES2_CB(glBindFramebuffer)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLuint, framebuffer);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBindFramebuffer(target, framebuffer);
+}
+
+GLES2_CB(glBindRenderbuffer)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLuint, renderbuffer);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBindRenderbuffer(target, renderbuffer);
+}
+
+GLES2_CB(glCheckFramebufferStatus)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_BARRIER_ARG;
+
+    GLES2_BARRIER_RET;
+    gles2_ret_TGLenum(s, glCheckFramebufferStatus(target));
+}
+
+GLES2_CB(glDeleteFramebuffers)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, framebuffersp);
+
+    GLsizei i;
+    GLuint* framebuffers = (GLuint*)malloc(sizeof(GLuint)*n);
+
+    for (i = 0; i < n; ++i) {
+        framebuffers[i] = gles2_get_TGLuint(s,
+            framebuffersp + i*sizeof(TGLuint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glDeleteFramebuffers(n, framebuffers);
+    free(framebuffers);
+}
+
+GLES2_CB(glDeleteRenderbuffers)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, renderbuffersp);
+
+    GLsizei i;
+    GLuint* renderbuffers = (GLuint*)malloc(sizeof(GLuint)*n);
+
+    for (i = 0; i < n; ++i) {
+        renderbuffers[i] = gles2_get_TGLuint(s,
+            renderbuffersp + i*sizeof(TGLuint));
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    glDeleteRenderbuffers(n, renderbuffers);
+    free(renderbuffers);
+}
+
+GLES2_CB(glFramebufferRenderbuffer)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, attachment);
+    GLES2_ARG(TGLenum, renderbuffertarget);
+    GLES2_ARG(TGLuint, renderbuffer);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFramebufferRenderbuffer(target, attachment,
+        renderbuffertarget, renderbuffer);
+}
+
+GLES2_CB(glFramebufferTexture2D)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, attachment);
+    GLES2_ARG(TGLenum, textarget);
+    GLES2_ARG(TGLuint, texture);
+    GLES2_ARG(TGLint, level);
+    GLES2_BARRIER_ARG_NORET;
+
+    glFramebufferTexture2D(target, attachment, textarget, texture, level);
+}
+
+GLES2_CB(glGenFramebuffers)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, framebuffersp);
+
+    GLsizei i;
+    GLuint* framebuffers = (GLuint*)malloc(sizeof(GLuint)*n);
+
+    glGenFramebuffers(n, framebuffers);
+    for(i = 0; i < n; ++i) {
+        gles2_put_TGLuint(s, framebuffersp + i*sizeof(TGLuint),
+            framebuffers[i]);
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    free(framebuffers);
+}
+
+GLES2_CB(glGenRenderbuffers)//(GLsizei n, GLuint* renderbuffers)
+{
+    GLES2_ARG(TGLsizei, n);
+    GLES2_ARG(Tptr, renderbuffersp);
+
+    GLsizei i;
+    GLuint* renderbuffers = (GLuint*)malloc(sizeof(GLuint)*n);
+
+    glGenRenderbuffers(n, renderbuffers);
+    for(i = 0; i < n; ++i) {
+        gles2_put_TGLuint(s, renderbuffersp + i*sizeof(TGLuint),
+            renderbuffers[i]);
+    }
+    GLES2_BARRIER_ARG_NORET;
+    
+    free(renderbuffers);
+}
+
+#if 0
+GLES2_CB(glGetFramebufferAttachmentParameteriv)//(GLenum target,
+    GLenum attachment, GLenum pname, GLint* params)
+{
+       DUMMY();
+}
+
+GLES2_CB(glGetRenderbufferParameteriv)//(GLenum target, GLenum pname,
+    GLint* params)
+{
+       DUMMY();
+}
+
+GLES2_CB(glIsFramebuffer)//(GLuint framebuffer)
+{
+       DUMMY();
+}
+
+GLES2_CB(glIsRenderbuffer)//(GLuint renderbuffer)
+{
+       DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glBindBuffer)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLuint, buffer);
+    GLES2_BARRIER_ARG_NORET;
+
+    glBindTexture(target, buffer);
+}
+
+
+GLES2_CB(glRenderbufferStorage)
+{
+    GLES2_ARG(TGLenum, target);
+    GLES2_ARG(TGLenum, internalformat);
+    GLES2_ARG(TGLsizei, width);
+    GLES2_ARG(TGLsizei, height);
+    GLES2_BARRIER_ARG_NORET;
+
+    glRenderbufferStorage(target, internalformat, width, height);
+}
+
+GLES2_CB(glDepthFunc)
+{
+       GLES2_ARG(TGLenum, func);
+       GLES2_BARRIER_ARG_NORET;
+
+       glDepthFunc(func);
+}
+
+GLES2_CB(glDepthMask)
+{
+       GLES2_ARG(TGLboolean, flag);
+       GLES2_BARRIER_ARG_NORET;
+
+       glDepthMask(flag);
+}
+
+GLES2_CB(glDepthRangef)
+{
+       GLES2_ARG(TGLclampf, zNear);
+       GLES2_ARG(TGLclampf, zFar);
+       GLES2_BARRIER_ARG_NORET;
+
+       glDepthRangef(zNear, zFar);
+}
+
+GLES2_CB(glDetachShader)
+{
+    GLES2_ARG(TGLuint, program);
+    GLES2_ARG(TGLuint, shader);
+    GLES2_BARRIER_ARG_NORET;
+
+    glDetachShader(program, shader);
+}
+
+GLES2_CB(glClearDepthf)
+{
+       GLES2_ARG(TGLclampf, depth);
+       GLES2_BARRIER_ARG_NORET;
+
+       glClearDepthf(depth);
+}
+
diff --git a/hw/gles2_calls.h b/hw/gles2_calls.h
new file mode 100644 (file)
index 0000000..446858f
--- /dev/null
@@ -0,0 +1,278 @@
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * 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) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*egl*/
+CALL_ENTRY(eglGetError)
+CALL_ENTRY(eglGetDisplay)
+CALL_ENTRY(eglInitialize)
+CALL_ENTRY(eglTerminate)
+CALL_DUMMY(eglQueryString)
+CALL_ENTRY(eglGetConfigs)
+CALL_ENTRY(eglChooseConfig)
+CALL_ENTRY(eglGetConfigAttrib)
+CALL_ENTRY(eglCreateWindowSurface)
+CALL_DUMMY(eglCreatePbufferSurface)
+CALL_ENTRY(eglCreatePixmapSurface)
+CALL_ENTRY(eglDestroySurface)
+CALL_DUMMY(eglQuerySurface)
+CALL_ENTRY(eglBindAPI)
+CALL_DUMMY(eglQueryAPI)
+CALL_DUMMY(eglWaitClient)
+CALL_DUMMY(eglReleaseThread)
+CALL_DUMMY(eglCreatePbufferFromClientBuffer)
+CALL_DUMMY(eglSurfaceAttrib)
+CALL_ENTRY(eglBindTexImage)
+CALL_ENTRY(eglReleaseTexImage)
+CALL_DUMMY(eglSwapInterval)
+CALL_ENTRY(eglCreateContext)
+CALL_ENTRY(eglDestroyContext)
+CALL_ENTRY(eglMakeCurrent)
+CALL_DUMMY(eglGetCurrentContext)
+CALL_DUMMY(eglGetCurrentSurface)
+CALL_DUMMY(eglGetCurrentDisplay)
+CALL_DUMMY(eglQueryContext)
+CALL_DUMMY(eglWaitGL)
+CALL_DUMMY(eglWaitNative)
+CALL_ENTRY(eglSwapBuffers)
+CALL_DUMMY(eglCopyBuffers)
+CALL_DUMMY(eglGetProcAddress)
+
+/* gles v2*/
+CALL_ENTRY(glActiveTexture)
+CALL_ENTRY(glAttachShader)
+CALL_ENTRY(glBindAttribLocation)
+CALL_ENTRY(glBindBuffer)
+CALL_ENTRY(glBindFramebuffer)
+CALL_ENTRY(glBindRenderbuffer)
+CALL_ENTRY(glBindTexture)
+CALL_ENTRY(glBlendColor)
+CALL_ENTRY(glBlendEquation)
+CALL_ENTRY(glBlendEquationSeparate)
+CALL_ENTRY(glBlendFunc)
+CALL_ENTRY(glBlendFuncSeparate)
+CALL_DUMMY(glBufferData)
+CALL_DUMMY(glBufferSubData)
+CALL_ENTRY(glCheckFramebufferStatus)
+CALL_ENTRY(glClear)
+CALL_ENTRY(glClearColor)
+CALL_ENTRY(glClearDepthf)
+CALL_ENTRY(glClearStencil)
+CALL_ENTRY(glColorMask)
+CALL_ENTRY(glCompileShader)
+CALL_ENTRY(glCompressedTexImage2D)
+CALL_ENTRY(glCompressedTexSubImage2D)
+CALL_ENTRY(glCopyTexImage2D)
+CALL_ENTRY(glCopyTexSubImage2D)
+CALL_ENTRY(glCreateProgram)
+CALL_ENTRY(glCreateShader)
+CALL_ENTRY(glCullFace)
+CALL_DUMMY(glDeleteBuffers)
+CALL_ENTRY(glDeleteFramebuffers)
+CALL_ENTRY(glDeleteProgram)
+CALL_ENTRY(glDeleteRenderbuffers)
+CALL_ENTRY(glDeleteShader)
+CALL_ENTRY(glDeleteTextures)
+CALL_ENTRY(glDepthFunc)
+CALL_ENTRY(glDepthMask)
+CALL_ENTRY(glDepthRangef)
+CALL_ENTRY(glDetachShader)
+CALL_ENTRY(glDisable)
+CALL_ENTRY(glDisableVertexAttribArray)
+CALL_ENTRY(glDrawArrays)
+CALL_ENTRY(glDrawElements)
+CALL_ENTRY(glEnable)
+CALL_ENTRY(glEnableVertexAttribArray)
+CALL_ENTRY(glFinish)
+CALL_ENTRY(glFlush)
+CALL_ENTRY(glFramebufferRenderbuffer)
+CALL_ENTRY(glFramebufferTexture2D)
+CALL_ENTRY(glFrontFace)
+CALL_DUMMY(glGenBuffers)
+CALL_ENTRY(glGenerateMipmap)
+CALL_ENTRY(glGenFramebuffers)
+CALL_ENTRY(glGenRenderbuffers)
+CALL_ENTRY(glGenTextures)
+CALL_ENTRY(glGetActiveAttrib)
+CALL_ENTRY(glGetActiveUniform)
+CALL_ENTRY(glGetAttachedShaders)
+CALL_ENTRY(glGetAttribLocation)
+CALL_ENTRY(glGetBooleanv)
+CALL_DUMMY(glGetBufferParameteriv)
+CALL_ENTRY(glGetError)
+CALL_ENTRY(glGetFloatv)
+CALL_DUMMY(glGetFramebufferAttachmentParameteriv)
+CALL_ENTRY(glGetIntegerv)
+CALL_ENTRY(glGetProgramiv)
+CALL_ENTRY(glGetProgramInfoLog)
+CALL_DUMMY(glGetRenderbufferParameteriv)
+CALL_ENTRY(glGetShaderiv)
+CALL_ENTRY(glGetShaderInfoLog)
+CALL_ENTRY(glGetShaderPrecisionFormat)
+CALL_ENTRY(glGetShaderSource)
+CALL_DUMMY(glGetString)
+CALL_ENTRY(glGetTexParameterfv)
+CALL_ENTRY(glGetTexParameteriv)
+CALL_ENTRY(glGetUniformfv)
+CALL_ENTRY(glGetUniformiv)
+CALL_ENTRY(glGetUniformLocation)
+CALL_ENTRY(glGetVertexAttribfv)
+CALL_ENTRY(glGetVertexAttribiv)
+CALL_ENTRY(glGetVertexAttribPointerv)
+CALL_ENTRY(glHint)
+CALL_DUMMY(glIsBuffer)
+CALL_ENTRY(glIsEnabled)
+CALL_DUMMY(glIsFramebuffer)
+CALL_ENTRY(glIsProgram)
+CALL_DUMMY(glIsRenderbuffer)
+CALL_ENTRY(glIsShader)
+CALL_ENTRY(glIsTexture)
+CALL_ENTRY(glLineWidth)
+CALL_ENTRY(glLinkProgram)
+CALL_ENTRY(glPixelStorei)
+CALL_ENTRY(glPolygonOffset)
+CALL_ENTRY(glReadPixels)
+CALL_ENTRY(glReleaseShaderCompiler)
+CALL_ENTRY(glRenderbufferStorage)
+CALL_ENTRY(glSampleCoverage)
+CALL_ENTRY(glScissor)
+CALL_DUMMY(glShaderBinary)
+CALL_ENTRY(glShaderSource)
+CALL_ENTRY(glStencilFunc)
+CALL_ENTRY(glStencilFuncSeparate)
+CALL_ENTRY(glStencilMask)
+CALL_ENTRY(glStencilMaskSeparate)
+CALL_ENTRY(glStencilOp)
+CALL_ENTRY(glStencilOpSeparate)
+CALL_ENTRY(glTexImage2D)
+CALL_ENTRY(glTexParameterf)
+CALL_ENTRY(glTexParameterfv)
+CALL_ENTRY(glTexParameteri)
+CALL_ENTRY(glTexParameteriv)
+CALL_ENTRY(glTexSubImage2D)
+CALL_ENTRY(glUniform1f)
+CALL_ENTRY(glUniform1fv)
+CALL_ENTRY(glUniform1i)
+CALL_ENTRY(glUniform1iv)
+CALL_ENTRY(glUniform2f)
+CALL_ENTRY(glUniform2fv)
+CALL_ENTRY(glUniform2i)
+CALL_ENTRY(glUniform2iv)
+CALL_ENTRY(glUniform3f)
+CALL_ENTRY(glUniform3fv)
+CALL_ENTRY(glUniform3i)
+CALL_ENTRY(glUniform3iv)
+CALL_ENTRY(glUniform4f)
+CALL_ENTRY(glUniform4fv)
+CALL_ENTRY(glUniform4i)
+CALL_ENTRY(glUniform4iv)
+CALL_ENTRY(glUniformMatrix2fv)
+CALL_ENTRY(glUniformMatrix3fv)
+CALL_ENTRY(glUniformMatrix4fv)
+CALL_ENTRY(glUseProgram)
+CALL_ENTRY(glValidateProgram)
+CALL_ENTRY(glVertexAttrib1f)
+CALL_ENTRY(glVertexAttrib1fv)
+CALL_ENTRY(glVertexAttrib2f)
+CALL_ENTRY(glVertexAttrib2fv)
+CALL_ENTRY(glVertexAttrib3f)
+CALL_ENTRY(glVertexAttrib3fv)
+CALL_ENTRY(glVertexAttrib4f)
+CALL_ENTRY(glVertexAttrib4fv)
+CALL_ENTRY(glVertexAttribPointer)
+CALL_ENTRY(glViewport)
+
+/* gles v1 without gles v2*/
+CALL_ENTRY(glAlphaFunc)
+CALL_ENTRY(glClipPlanef)
+CALL_ENTRY(glColor4f)
+CALL_ENTRY(glFogf)
+CALL_ENTRY(glFogfv)
+CALL_ENTRY(glFrustumf)
+CALL_ENTRY(glGetClipPlanef)
+CALL_ENTRY(glGetLightfv)
+CALL_ENTRY(glGetMaterialfv)
+CALL_ENTRY(glGetTexEnvfv)
+CALL_ENTRY(glLightModelf)
+CALL_ENTRY(glLightModelfv)
+CALL_ENTRY(glLightf)
+CALL_ENTRY(glLightfv)
+CALL_ENTRY(glLoadMatrixf)
+CALL_ENTRY(glMaterialf)
+CALL_ENTRY(glMaterialfv)
+CALL_ENTRY(glMultMatrixf)
+CALL_ENTRY(glMultiTexCoord4f)
+CALL_ENTRY(glNormal3f)
+CALL_ENTRY(glOrthof)
+CALL_ENTRY(glPointParameterf)
+CALL_ENTRY(glPointParameterfv)
+CALL_ENTRY(glPointSize)
+CALL_ENTRY(glRotatef)
+CALL_ENTRY(glScalef)
+CALL_ENTRY(glTexEnvf)
+CALL_ENTRY(glTexEnvfv)
+CALL_ENTRY(glTranslatef)
+CALL_ENTRY(glAlphaFuncx)
+CALL_ENTRY(glClearColorx)
+CALL_ENTRY(glClearDepthx)
+CALL_ENTRY(glClientActiveTexture)
+CALL_ENTRY(glClipPlanex)
+CALL_ENTRY(glColor4ub)
+CALL_ENTRY(glColor4x)
+CALL_ENTRY(glColorPointer)
+CALL_ENTRY(glDepthRangex)
+CALL_ENTRY(glDisableClientState)
+CALL_ENTRY(glEnableClientState)
+CALL_ENTRY(glFogx)
+CALL_ENTRY(glFogxv)
+CALL_ENTRY(glFrustumx)
+CALL_ENTRY(glGetClipPlanex)
+CALL_ENTRY(glGetFixedv)
+CALL_ENTRY(glGetLightxv)
+CALL_ENTRY(glGetMaterialxv)
+CALL_ENTRY(glGetPointerv)
+CALL_ENTRY(glGetTexEnviv)
+CALL_ENTRY(glGetTexEnvxv)
+CALL_ENTRY(glGetTexParameterxv)
+CALL_ENTRY(glLightModelx)
+CALL_ENTRY(glLightModelxv)
+CALL_ENTRY(glLightx)
+CALL_ENTRY(glLightxv)
+CALL_ENTRY(glLineWidthx)
+CALL_ENTRY(glLoadIdentity)
+CALL_ENTRY(glLoadMatrixx)
+CALL_ENTRY(glLogicOp)
+CALL_ENTRY(glMaterialx)
+CALL_ENTRY(glMaterialxv)
+CALL_ENTRY(glMatrixMode)
+CALL_ENTRY(glMultMatrixx)
+CALL_ENTRY(glMultiTexCoord4x)
+CALL_ENTRY(glNormal3x)
+CALL_ENTRY(glNormalPointer)
+CALL_ENTRY(glOrthox)
+CALL_ENTRY(glPointParameterx)
+CALL_ENTRY(glPointParameterxv)
+CALL_ENTRY(glPointSizex)
+CALL_ENTRY(glPolygonOffsetx)
+CALL_ENTRY(glPopMatrix)
+CALL_ENTRY(glPushMatrix)
+CALL_ENTRY(glRotatex)
+CALL_ENTRY(glSampleCoveragex)
+CALL_ENTRY(glScalex)
+CALL_ENTRY(glShadeModel)
+CALL_ENTRY(glTexCoordPointer)
+CALL_ENTRY(glTexEnvi)
+CALL_ENTRY(glTexEnvx)
+CALL_ENTRY(glTexEnviv)
+CALL_ENTRY(glTexEnvxv)
+CALL_ENTRY(glTexParameterx)
+CALL_ENTRY(glTexParameterxv)
+CALL_ENTRY(glTranslatex)
+CALL_ENTRY(glVertexPointer)
diff --git a/hw/gles2_types.h b/hw/gles2_types.h
new file mode 100644 (file)
index 0000000..5506509
--- /dev/null
@@ -0,0 +1,92 @@
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * 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) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GLES2_TYPES_H_
+#define GLES2_TYPES_H_
+
+// Automatically create the prototype and function definition.
+#define GLES2_CB(FUNC) \
+    void gles2_##FUNC##_cb(gles2_State *s, \
+        gles2_decode_t *d, gles2_Client *c); \
+    void gles2_##FUNC##_cb(gles2_State *s, \
+        gles2_decode_t *d, gles2_Client *c)
+
+// Sizes of primitive types in the ABI.
+#define GLES2_HTYPE_byte uint8_t
+#define GLES2_HTYPE_word uint16_t
+#define GLES2_HTYPE_dword uint32_t
+#define GLES2_HTYPE_float float
+#define GLES2_HTYPE_handle uint32_t
+
+// Defines shorthands for handling types.
+#define GLES2_TYPE(TYPE, SIZE) \
+    typedef GLES2_HTYPE_##SIZE TYPE; \
+    static inline void gles2_ret_##TYPE(gles2_State *s, TYPE value) \
+    { gles2_ret_##SIZE(s, value); } \
+    static inline void gles2_put_##TYPE(gles2_State *s, target_ulong va, TYPE value) \
+    { gles2_put_##SIZE(s, va, value); } \
+    static inline TYPE gles2_get_##TYPE(gles2_State *s, target_ulong va) \
+    { return (TYPE)gles2_get_##SIZE(s, va); } \
+    static inline TYPE gles2_arg_##TYPE(gles2_State *s, gles2_decode_t *d) \
+    { return (TYPE)gles2_arg_##SIZE(s, d); }
+
+// Bunch of expansions of previous macro to ease things up.
+GLES2_TYPE(Tptr, dword)
+GLES2_TYPE(TEGLBoolean, dword)
+GLES2_TYPE(TEGLenum, dword)
+GLES2_TYPE(TEGLint, dword)
+GLES2_TYPE(TEGLDisplay, handle)
+GLES2_TYPE(TEGLConfig, handle)
+GLES2_TYPE(TEGLContext, handle)
+GLES2_TYPE(TEGLSurface, handle)
+
+GLES2_TYPE(TGLclampf, float)
+GLES2_TYPE(TGLbitfield, dword)
+GLES2_TYPE(TGLboolean, byte)
+GLES2_TYPE(TGLint, dword)
+GLES2_TYPE(TGLuint, dword)
+GLES2_TYPE(TGLushort, word)
+GLES2_TYPE(TGLubyte, byte)
+GLES2_TYPE(TGLenum, dword)
+GLES2_TYPE(TGLsizei, dword)
+GLES2_TYPE(TGLfloat, float)
+GLES2_TYPE(TGLfixed, dword)
+GLES2_TYPE(TGLclampx, dword)
+
+
+// Just one more macro for even less typing.
+#define GLES2_ARG(TYPE, NAME) \
+    TYPE NAME = gles2_arg_##TYPE(s, d)
+
+//        pthread_cond_signal(&s->cond_xcode);
+
+
+// Host to guest vertex array copy.
+struct gles2_Array
+{
+    GLuint indx;          // Parameter of the call.
+    GLint size;           // --''--
+    GLenum type;          // --''--
+    GLboolean normalized; // --''--
+    GLsizei stride;       // --''--
+    GLsizei real_stride;       // --''--
+    Tptr tptr;            // Pointer in the guest memory.
+    void* ptr;            // Pointer in the host memory.
+
+    void (*apply) (struct gles2_Array *va);
+
+    GLboolean enabled;    // State.
+};
+
+unsigned gles1_glGetCount(TGLenum pname);
+void checkGLESError(void);
+
+#endif /* GLES2_TYPES_H_ */
diff --git a/hw/i2c-addressable.c b/hw/i2c-addressable.c
new file mode 100644 (file)
index 0000000..eeb35a3
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Interface to support I2C devices with control registers accessed by I2C
+ *
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vmonakhov@ispras.ru>
+ */
+
+#include "i2c-addressable.h"
+
+
+static int i2c_addressable_rx(i2c_slave *s)
+{
+    I2CAddressableDeviceInfo *t =
+        DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+    I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+    /* Get next byte of the value and transfer it */
+    if (t->read) {
+        return t->read(dev, dev->addr, dev->num++);
+    }
+    return 0;
+}
+
+static int i2c_addressable_tx(i2c_slave *s, uint8_t data)
+{
+    I2CAddressableDeviceInfo *t =
+        DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+    I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+    if (dev->num < t->size) {
+        /* First fully get control register address byte after byte */
+        if (!t->rev) {
+            /* Less significant bytes come first */
+            dev->addr |= data << (dev->num * 8);
+        } else {
+            /* More significant bytes come first */
+            dev->addr |= data << ((t->size - dev->num - 1) * 8);
+        }
+    } else {
+        /* Then get corresponding data byte after byte */
+        if (t->write) {
+            t->write(dev, dev->addr, dev->num - t->size, data);
+        }
+    }
+
+    dev->num++;
+    return 1;
+}
+
+static void i2c_addressable_event(i2c_slave *s, enum i2c_event event)
+{
+    I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+    switch (event) {
+    case I2C_START_SEND:
+        dev->addr = 0;
+        /* fallthrough */
+    case I2C_START_RECV:
+        /* Save address from the previous send */
+        dev->num = 0;
+        break;
+    case I2C_FINISH:
+    default:
+        break;
+    }
+}
+
+static int i2c_addressable_device_init(i2c_slave *s)
+{
+    I2CAddressableDeviceInfo *t =
+        DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+    I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+    return t->init(dev);
+}
+
+void i2c_addressable_register_device(I2CAddressableDeviceInfo *info)
+{
+    assert(info->i2c.qdev.size >= sizeof(I2CAddressableState));
+    info->i2c.init  = i2c_addressable_device_init;
+    info->i2c.event = i2c_addressable_event;
+    info->i2c.recv  = i2c_addressable_rx;
+    info->i2c.send  = i2c_addressable_tx;
+    i2c_register_slave(&info->i2c);
+}
diff --git a/hw/i2c-addressable.h b/hw/i2c-addressable.h
new file mode 100644 (file)
index 0000000..4a2a2f1
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef QEMU_I2C_ADDRESSABLE_H
+#define QEMU_I2C_ADDRESSABLE_H
+
+/*
+ * Interface to support I2C devices with control registers accessed by I2C
+ *
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * Many I2C devices have common logic which closely corresponds to control
+ * registers.  These devices are first passed with an ID of the needed command
+ * to be performed.  This ID may be considered as control register address.
+ * Then the device is either passed with some data or is awaited to return
+ * some data.  This may be considered as control register write and read
+ * respectively.  This interface provides a generic way to implement such
+ * I2C devices emulation by writing two functions for reading and writing of
+ * control registers similar to iomem interface.  One difference is that
+ * control registers may not have fixed sizes so along with the register
+ * address these functions get another parameter - offset inside this register
+ * data.
+ */
+
+#include "i2c.h"
+
+#define I2CADDR_SLAVE_FROM_QDEV(dev) DO_UPCAST(I2CAddressableState, i2c.qdev, dev)
+#define FROM_I2CADDR_SLAVE(type, dev) DO_UPCAST(type, i2c_addressable, dev)
+
+typedef struct I2CAddressableState {
+    /* I2C slave for the device */
+    i2c_slave i2c;
+    /* Address of currently processing data */
+    uint32_t addr;
+    /* Number of transferred bytes */
+    uint8_t num;
+} I2CAddressableState;
+
+typedef uint8_t (*i2c_addressable_read)  (void *opaque, uint32_t address,
+                                          uint8_t offset);
+typedef void    (*i2c_addressable_write) (void *opaque, uint32_t address,
+                                          uint8_t offset, uint8_t val);
+typedef int     (*i2c_addressable_init)  (I2CAddressableState *i2c);
+
+typedef struct I2CAddressableDeviceInfo {
+    I2CSlaveInfo i2c;
+
+    /* Read, write and init handlers */
+    i2c_addressable_read  read;
+    i2c_addressable_write write;
+    i2c_addressable_init  init;
+
+    /* Size of passed addresses in bytes */
+    int size;
+    /* Byte order: reversed is big-endian (more significant bytes come before
+     * less significant ones) */
+    int rev;
+} I2CAddressableDeviceInfo;
+
+void i2c_addressable_register_device(I2CAddressableDeviceInfo *info);
+
+#endif
index 83fd917..71ee662 100644 (file)
--- a/hw/i2c.h
+++ b/hw/i2c.h
@@ -79,4 +79,11 @@ void tmp105_set(i2c_slave *i2c, int temp);
 /* lm832x.c */
 void lm832x_key_event(i2c_slave *i2c, int key, int state);
 
+/* wm8994.c */
+void wm8994_data_req_set(DeviceState *dev, void (*data_req)(void *, int),
+                         void *opaque);
+void *wm8994_dac_buffer(DeviceState *dev, int samples);
+void wm8994_dac_dat(DeviceState *dev, uint32_t sample);
+void wm8994_dac_commit(DeviceState *dev);
+
 #endif
index 82b24b6..3d7da82 100644 (file)
@@ -39,6 +39,7 @@
 typedef struct {
     IDEBus bus;
     int shift;
+    int offset;
 } MMIOState;
 
 static void mmio_ide_reset(void *opaque)
@@ -51,7 +52,7 @@ static void mmio_ide_reset(void *opaque)
 static uint32_t mmio_ide_read (void *opaque, target_phys_addr_t addr)
 {
     MMIOState *s = opaque;
-    addr >>= s->shift;
+    addr = (addr - s->offset) >> s->shift;
     if (addr & 7)
         return ide_ioport_read(&s->bus, addr);
     else
@@ -62,7 +63,7 @@ static void mmio_ide_write (void *opaque, target_phys_addr_t addr,
        uint32_t val)
 {
     MMIOState *s = opaque;
-    addr >>= s->shift;
+    addr = (addr - s->offset) >> s->shift;
     if (addr & 7)
         ide_ioport_write(&s->bus, addr, val);
     else
@@ -128,6 +129,7 @@ void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2,
     ide_init2_with_non_qdev_drives(&s->bus, hd0, hd1, irq);
 
     s->shift = shift;
+    s->offset = membase & ~TARGET_PAGE_MASK;
 
     mem1 = cpu_register_io_memory(mmio_ide_reads, mmio_ide_writes, s,
                                   DEVICE_NATIVE_ENDIAN);
diff --git a/hw/iommu.c b/hw/iommu.c
new file mode 100644 (file)
index 0000000..4151022
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * QEMU SPARC iommu emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sun4m.h"
+#include "sysbus.h"
+
+/* debug iommu */
+//#define DEBUG_IOMMU
+
+#ifdef DEBUG_IOMMU
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define IOMMU_NREGS         (4*4096/4)
+#define IOMMU_CTRL          (0x0000 >> 2)
+#define IOMMU_CTRL_IMPL     0xf0000000 /* Implementation */
+#define IOMMU_CTRL_VERS     0x0f000000 /* Version */
+#define IOMMU_CTRL_RNGE     0x0000001c /* Mapping RANGE */
+#define IOMMU_RNGE_16MB     0x00000000 /* 0xff000000 -> 0xffffffff */
+#define IOMMU_RNGE_32MB     0x00000004 /* 0xfe000000 -> 0xffffffff */
+#define IOMMU_RNGE_64MB     0x00000008 /* 0xfc000000 -> 0xffffffff */
+#define IOMMU_RNGE_128MB    0x0000000c /* 0xf8000000 -> 0xffffffff */
+#define IOMMU_RNGE_256MB    0x00000010 /* 0xf0000000 -> 0xffffffff */
+#define IOMMU_RNGE_512MB    0x00000014 /* 0xe0000000 -> 0xffffffff */
+#define IOMMU_RNGE_1GB      0x00000018 /* 0xc0000000 -> 0xffffffff */
+#define IOMMU_RNGE_2GB      0x0000001c /* 0x80000000 -> 0xffffffff */
+#define IOMMU_CTRL_ENAB     0x00000001 /* IOMMU Enable */
+#define IOMMU_CTRL_MASK     0x0000001d
+
+#define IOMMU_BASE          (0x0004 >> 2)
+#define IOMMU_BASE_MASK     0x07fffc00
+
+#define IOMMU_TLBFLUSH      (0x0014 >> 2)
+#define IOMMU_TLBFLUSH_MASK 0xffffffff
+
+#define IOMMU_PGFLUSH       (0x0018 >> 2)
+#define IOMMU_PGFLUSH_MASK  0xffffffff
+
+#define IOMMU_AFSR          (0x1000 >> 2)
+#define IOMMU_AFSR_ERR      0x80000000 /* LE, TO, or BE asserted */
+#define IOMMU_AFSR_LE       0x40000000 /* SBUS reports error after
+                                          transaction */
+#define IOMMU_AFSR_TO       0x20000000 /* Write access took more than
+                                          12.8 us. */
+#define IOMMU_AFSR_BE       0x10000000 /* Write access received error
+                                          acknowledge */
+#define IOMMU_AFSR_SIZE     0x0e000000 /* Size of transaction causing error */
+#define IOMMU_AFSR_S        0x01000000 /* Sparc was in supervisor mode */
+#define IOMMU_AFSR_RESV     0x00800000 /* Reserved, forced to 0x8 by
+                                          hardware */
+#define IOMMU_AFSR_ME       0x00080000 /* Multiple errors occurred */
+#define IOMMU_AFSR_RD       0x00040000 /* A read operation was in progress */
+#define IOMMU_AFSR_FAV      0x00020000 /* IOMMU afar has valid contents */
+#define IOMMU_AFSR_MASK     0xff0fffff
+
+#define IOMMU_AFAR          (0x1004 >> 2)
+
+#define IOMMU_AER           (0x1008 >> 2) /* Arbiter Enable Register */
+#define IOMMU_AER_EN_P0_ARB 0x00000001    /* MBus master 0x8 (Always 1) */
+#define IOMMU_AER_EN_P1_ARB 0x00000002    /* MBus master 0x9 */
+#define IOMMU_AER_EN_P2_ARB 0x00000004    /* MBus master 0xa */
+#define IOMMU_AER_EN_P3_ARB 0x00000008    /* MBus master 0xb */
+#define IOMMU_AER_EN_0      0x00010000    /* SBus slot 0 */
+#define IOMMU_AER_EN_1      0x00020000    /* SBus slot 1 */
+#define IOMMU_AER_EN_2      0x00040000    /* SBus slot 2 */
+#define IOMMU_AER_EN_3      0x00080000    /* SBus slot 3 */
+#define IOMMU_AER_EN_F      0x00100000    /* SBus on-board */
+#define IOMMU_AER_SBW       0x80000000    /* S-to-M asynchronous writes */
+#define IOMMU_AER_MASK      0x801f000f
+
+#define IOMMU_SBCFG0        (0x1010 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG1        (0x1014 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG2        (0x1018 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG3        (0x101c >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG_SAB30   0x00010000 /* Phys-address bit 30 when
+                                          bypass enabled */
+#define IOMMU_SBCFG_BA16    0x00000004 /* Slave supports 16 byte bursts */
+#define IOMMU_SBCFG_BA8     0x00000002 /* Slave supports 8 byte bursts */
+#define IOMMU_SBCFG_BYPASS  0x00000001 /* Bypass IOMMU, treat all addresses
+                                          produced by this device as pure
+                                          physical. */
+#define IOMMU_SBCFG_MASK    0x00010003
+
+#define IOMMU_ARBEN         (0x2000 >> 2) /* SBUS arbitration enable */
+#define IOMMU_ARBEN_MASK    0x001f0000
+#define IOMMU_MID           0x00000008
+
+#define IOMMU_MASK_ID       (0x3018 >> 2) /* Mask ID */
+#define IOMMU_MASK_ID_MASK  0x00ffffff
+
+#define IOMMU_MSII_MASK     0x26000000 /* microSPARC II mask number */
+#define IOMMU_TS_MASK       0x23000000 /* turboSPARC mask number */
+
+/* The format of an iopte in the page tables */
+#define IOPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
+#define IOPTE_CACHE         0x00000080 /* Cached (in vme IOCACHE or
+                                          Viking/MXCC) */
+#define IOPTE_WRITE         0x00000004 /* Writeable */
+#define IOPTE_VALID         0x00000002 /* IOPTE is valid */
+#define IOPTE_WAZ           0x00000001 /* Write as zeros */
+
+#define IOMMU_PAGE_SHIFT    12
+#define IOMMU_PAGE_SIZE     (1 << IOMMU_PAGE_SHIFT)
+#define IOMMU_PAGE_MASK     ~(IOMMU_PAGE_SIZE - 1)
+
+typedef struct IOMMUState {
+    SysBusDevice busdev;
+    uint32_t regs[IOMMU_NREGS];
+    target_phys_addr_t iostart;
+    uint32_t version;
+    qemu_irq irq;
+} IOMMUState;
+
+static uint32_t iommu_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    IOMMUState *s = opaque;
+    target_phys_addr_t saddr;
+    uint32_t ret;
+
+    saddr = addr >> 2;
+    switch (saddr) {
+    default:
+        ret = s->regs[saddr];
+        break;
+    case IOMMU_AFAR:
+    case IOMMU_AFSR:
+        ret = s->regs[saddr];
+        qemu_irq_lower(s->irq);
+        break;
+    }
+    DPRINTF("read reg[%d] = %x\n", (int)saddr, ret);
+    return ret;
+}
+
+static void iommu_mem_writel(void *opaque, target_phys_addr_t addr,
+                             uint32_t val)
+{
+    IOMMUState *s = opaque;
+    target_phys_addr_t saddr;
+
+    saddr = addr >> 2;
+    DPRINTF("write reg[%d] = %x\n", (int)saddr, val);
+    switch (saddr) {
+    case IOMMU_CTRL:
+        switch (val & IOMMU_CTRL_RNGE) {
+        case IOMMU_RNGE_16MB:
+            s->iostart = 0xffffffffff000000ULL;
+            break;
+        case IOMMU_RNGE_32MB:
+            s->iostart = 0xfffffffffe000000ULL;
+            break;
+        case IOMMU_RNGE_64MB:
+            s->iostart = 0xfffffffffc000000ULL;
+            break;
+        case IOMMU_RNGE_128MB:
+            s->iostart = 0xfffffffff8000000ULL;
+            break;
+        case IOMMU_RNGE_256MB:
+            s->iostart = 0xfffffffff0000000ULL;
+            break;
+        case IOMMU_RNGE_512MB:
+            s->iostart = 0xffffffffe0000000ULL;
+            break;
+        case IOMMU_RNGE_1GB:
+            s->iostart = 0xffffffffc0000000ULL;
+            break;
+        default:
+        case IOMMU_RNGE_2GB:
+            s->iostart = 0xffffffff80000000ULL;
+            break;
+        }
+        DPRINTF("iostart = " TARGET_FMT_plx "\n", s->iostart);
+        s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version);
+        break;
+    case IOMMU_BASE:
+        s->regs[saddr] = val & IOMMU_BASE_MASK;
+        break;
+    case IOMMU_TLBFLUSH:
+        DPRINTF("tlb flush %x\n", val);
+        s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK;
+        break;
+    case IOMMU_PGFLUSH:
+        DPRINTF("page flush %x\n", val);
+        s->regs[saddr] = val & IOMMU_PGFLUSH_MASK;
+        break;
+    case IOMMU_AFAR:
+        s->regs[saddr] = val;
+        qemu_irq_lower(s->irq);
+        break;
+    case IOMMU_AER:
+        s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB;
+        break;
+    case IOMMU_AFSR:
+        s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV;
+        qemu_irq_lower(s->irq);
+        break;
+    case IOMMU_SBCFG0:
+    case IOMMU_SBCFG1:
+    case IOMMU_SBCFG2:
+    case IOMMU_SBCFG3:
+        s->regs[saddr] = val & IOMMU_SBCFG_MASK;
+        break;
+    case IOMMU_ARBEN:
+        // XXX implement SBus probing: fault when reading unmapped
+        // addresses, fault cause and address stored to MMU/IOMMU
+        s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
+        break;
+    case IOMMU_MASK_ID:
+        s->regs[saddr] |= val & IOMMU_MASK_ID_MASK;
+        break;
+    default:
+        s->regs[saddr] = val;
+        break;
+    }
+}
+
+static CPUReadMemoryFunc * const iommu_mem_read[3] = {
+    NULL,
+    NULL,
+    iommu_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const iommu_mem_write[3] = {
+    NULL,
+    NULL,
+    iommu_mem_writel,
+};
+
+static uint32_t iommu_page_get_flags(IOMMUState *s, target_phys_addr_t addr)
+{
+    uint32_t ret;
+    target_phys_addr_t iopte;
+#ifdef DEBUG_IOMMU
+    target_phys_addr_t pa = addr;
+#endif
+
+    iopte = s->regs[IOMMU_BASE] << 4;
+    addr &= ~s->iostart;
+    iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3;
+    cpu_physical_memory_read(iopte, (uint8_t *)&ret, 4);
+    tswap32s(&ret);
+    DPRINTF("get flags addr " TARGET_FMT_plx " => pte " TARGET_FMT_plx
+            ", *pte = %x\n", pa, iopte, ret);
+
+    return ret;
+}
+
+static target_phys_addr_t iommu_translate_pa(target_phys_addr_t addr,
+                                             uint32_t pte)
+{
+    uint32_t tmppte;
+    target_phys_addr_t pa;
+
+    tmppte = pte;
+    pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK);
+    DPRINTF("xlate dva " TARGET_FMT_plx " => pa " TARGET_FMT_plx
+            " (iopte = %x)\n", addr, pa, tmppte);
+
+    return pa;
+}
+
+static void iommu_bad_addr(IOMMUState *s, target_phys_addr_t addr,
+                           int is_write)
+{
+    DPRINTF("bad addr " TARGET_FMT_plx "\n", addr);
+    s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV |
+        IOMMU_AFSR_FAV;
+    if (!is_write)
+        s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD;
+    s->regs[IOMMU_AFAR] = addr;
+    qemu_irq_raise(s->irq);
+}
+
+void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr,
+                           uint8_t *buf, int len, int is_write)
+{
+    int l;
+    uint32_t flags;
+    target_phys_addr_t page, phys_addr;
+
+    while (len > 0) {
+        page = addr & IOMMU_PAGE_MASK;
+        l = (page + IOMMU_PAGE_SIZE) - addr;
+        if (l > len)
+            l = len;
+        flags = iommu_page_get_flags(opaque, page);
+        if (!(flags & IOPTE_VALID)) {
+            iommu_bad_addr(opaque, page, is_write);
+            return;
+        }
+        phys_addr = iommu_translate_pa(addr, flags);
+        if (is_write) {
+            if (!(flags & IOPTE_WRITE)) {
+                iommu_bad_addr(opaque, page, is_write);
+                return;
+            }
+            cpu_physical_memory_write(phys_addr, buf, l);
+        } else {
+            cpu_physical_memory_read(phys_addr, buf, l);
+        }
+        len -= l;
+        buf += l;
+        addr += l;
+    }
+}
+
+static const VMStateDescription vmstate_iommu = {
+    .name ="iommu",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS),
+        VMSTATE_UINT64(iostart, IOMMUState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void iommu_reset(DeviceState *d)
+{
+    IOMMUState *s = container_of(d, IOMMUState, busdev.qdev);
+
+    memset(s->regs, 0, IOMMU_NREGS * 4);
+    s->iostart = 0;
+    s->regs[IOMMU_CTRL] = s->version;
+    s->regs[IOMMU_ARBEN] = IOMMU_MID;
+    s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV;
+    s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB;
+    s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK;
+}
+
+static int iommu_init1(SysBusDevice *dev)
+{
+    IOMMUState *s = FROM_SYSBUS(IOMMUState, dev);
+    int io;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    io = cpu_register_io_memory(iommu_mem_read, iommu_mem_write, s);
+    sysbus_init_mmio(dev, IOMMU_NREGS * sizeof(uint32_t), io);
+
+    return 0;
+}
+
+static SysBusDeviceInfo iommu_info = {
+    .init = iommu_init1,
+    .qdev.name  = "iommu",
+    .qdev.size  = sizeof(IOMMUState),
+    .qdev.vmsd  = &vmstate_iommu,
+    .qdev.reset = iommu_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_HEX32("version", IOMMUState, version, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void iommu_register_devices(void)
+{
+    sysbus_register_withprop(&iommu_info);
+}
+
+device_init(iommu_register_devices)
diff --git a/hw/mcs5000_tk.c b/hw/mcs5000_tk.c
new file mode 100644 (file)
index 0000000..c24f1a2
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Melfas MCS-5000 Touchkey
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * NB: Only features used in the kernel driver is implemented currently.
+ * NB2: This device may also be used in touchscreen mode.  Not supported now.
+ */
+
+#include "console.h"
+#include "i2c-addressable.h"
+
+
+#define MCS5000_TK_LED_ONOFF            0x01
+#define MCS5000_TK_LED_DIMMING          0x02
+#define MCS5000_TK_RESERVED             0x03
+#define MCS5000_TK_VALUE_STATUS         0x04
+#define MCS5000_TK_RESERVED1            0x05
+#define MCS5000_TK_HW_VERSION           0x06
+#define MCS5000_TK_FW_VERSION           0x0A
+#define MCS5000_TK_MI_VERSION           0x0B
+
+#define MCS5000_TK_FW_ADDR              0x7e
+
+
+typedef struct MCS5000State {
+
+    I2CAddressableState i2c_addressable;
+
+    uint8_t hw_version;
+    uint8_t fw_version;
+    uint8_t mi_version;
+
+    uint8_t status;
+
+    int32_t download_fw_state;
+    int32_t download_fw_size;
+    qemu_irq irq;
+} MCS5000State;
+
+
+static int map[0x80];
+
+static int universal_tk_keymap[0x80] = {
+    [0x61] = 100, /* 100 is KEY_PHONE / KEY_SEND */
+    [0x62] = 101, /* 101 is KEY_FRONT / KEY_HOME */
+    [0x63] = 102, /* 102 is KEY_EXIT  / KEY_END  */
+};
+
+
+static void mcs5000_reset(DeviceState *d)
+{
+    MCS5000State *s =
+        FROM_I2CADDR_SLAVE(MCS5000State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+    s->hw_version = 0x9;
+    s->fw_version = 0x9;
+    s->mi_version = 0x0;
+
+    s->status = 0;
+
+    s->download_fw_state = 0;
+    s->download_fw_size = 0;
+}
+
+static uint8_t mcs5000_read(void *opaque, uint32_t address, uint8_t offset)
+{
+    MCS5000State *s = (MCS5000State *)opaque;
+
+    if (offset > 0) {
+        hw_error("mcs5000: bad read size\n");
+    }
+
+    switch (address) {
+    case MCS5000_TK_VALUE_STATUS:
+        return s->status;
+    case MCS5000_TK_HW_VERSION:
+        return s->hw_version;
+    case MCS5000_TK_FW_VERSION:
+        return s->fw_version;
+    case MCS5000_TK_MI_VERSION:
+        return s->mi_version;
+    case MCS5000_TK_FW_ADDR:
+        s->download_fw_state++;
+        switch (s->download_fw_state - 1) {
+        case 0: /* Request for firmware download */
+            return 0x55;
+        case 1: /* Prepare erase done */
+            return 0x8F;
+        case 2: /* Erase done */
+            return 0x82;
+        case 3: /* Read flash */
+            return 0x84;
+        case 4: /* Check firmware */
+            if (s->download_fw_size > 0) {
+                s->download_fw_size--;
+                s->download_fw_state = 4;
+            }
+            return 0xFF;
+        case 5: /* Prepare program */
+            return 0x8F;
+        case 6: /* Program flash */
+            return 0x83;
+        case 7: /* Read flash */
+            return 0x84;
+        case 8: /* Verify data */
+            /* TODO */
+            if (s->download_fw_size > 0) {
+                s->download_fw_size--;
+                s->download_fw_state = 8;
+            }
+            return 0xFF;
+        default:
+            s->download_fw_state = 0;
+            return 0;
+        }
+    default:
+        hw_error("mcs5000: bad read offset 0x%x\n", address);
+    }
+}
+
+
+static void mcs5000_write(void *opaque, uint32_t address, uint8_t offset,
+                          uint8_t val)
+{
+    MCS5000State *s = (MCS5000State *)opaque;
+
+    if (offset > 0) {
+        hw_error("mcs5000: bad write size\n");
+    }
+
+    switch (address) {
+    case MCS5000_TK_LED_ONOFF:
+        break;
+    case MCS5000_TK_LED_DIMMING:
+        break;
+    case MCS5000_TK_FW_ADDR:
+        if (s->download_fw_state == 3 || s->download_fw_state == 7) {
+            s->download_fw_size = val;
+        } else if (s->download_fw_state == 6) {
+            /* TODO */
+            /* Get firmware image */
+        }
+        break;
+    default:
+        hw_error("mcs5000: bad write offset 0x%x\n", address);
+    }
+}
+
+static int mcs5000_tk_event(void *opaque, int keycode)
+{
+    MCS5000State *s = (MCS5000State *)opaque;
+    int k, push = 0;
+
+    push = (keycode & 0x80) ? 0 : 1; /* Key push from qemu */
+    keycode &= ~(0x80); /* Strip qemu key release bit */
+
+    k = map[keycode];
+
+    /* Don't report unknown keypress */
+    if (k < 0) {
+        return -1;
+    }
+
+    s->status = k | (push << 7);
+    qemu_irq_raise(s->irq);
+    return 0;
+}
+
+static void mcs5000_init_keymap(int keycodes[])
+{
+    int i;
+
+    for (i = 0; i < 0x80; i++) {
+        map[i] = -1;
+    }
+    for (i = 0; i < 0x80; i++) {
+        map[keycodes[i]] = i;
+    }
+}
+
+static void mcs5000_save(QEMUFile *f, void *opaque)
+{
+    MCS5000State *s = (MCS5000State *)opaque;
+
+    qemu_put_8s(f, &s->status);
+
+    qemu_put_sbe32s(f, &s->download_fw_state);
+    qemu_put_sbe32s(f, &s->download_fw_size);
+}
+
+static int mcs5000_load(QEMUFile *f, void *opaque, int version_id)
+{
+    MCS5000State *s = (MCS5000State *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_8s(f, &s->status);
+
+    qemu_get_sbe32s(f, &s->download_fw_state);
+    qemu_get_sbe32s(f, &s->download_fw_size);
+
+    return 0;
+}
+
+static int mcs5000_init(I2CAddressableState *s, int keycodes[])
+{
+    MCS5000State *t = FROM_I2CADDR_SLAVE(MCS5000State, s);
+
+    qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+    qemu_add_kbd_event_handler(mcs5000_tk_event, t, "MCS5000 Touchkey");
+
+    mcs5000_reset(&s->i2c.qdev);
+    mcs5000_init_keymap(keycodes);
+
+    register_savevm(&s->i2c.qdev, "mcs5000", -1, 1,
+                    mcs5000_save, mcs5000_load, s);
+
+    return 0;
+}
+
+static int mcs5000_universal_init(I2CAddressableState *i2c)
+{
+    return mcs5000_init(i2c, universal_tk_keymap);
+}
+
+static I2CAddressableDeviceInfo mcs5000_universal_info = {
+    .i2c.qdev.name  = "mcs5000.universal",
+    .i2c.qdev.size  = sizeof(MCS5000State),
+    .i2c.qdev.reset = mcs5000_reset,
+    .init  = mcs5000_universal_init,
+    .read  = mcs5000_read,
+    .write = mcs5000_write,
+    .size  = 1,
+    .rev   = 0
+};
+
+static void mcs5000_register_devices(void)
+{
+    i2c_addressable_register_device(&mcs5000_universal_info);
+}
+
+device_init(mcs5000_register_devices)
index d98aa8d..aef8ba5 100644 (file)
@@ -1366,7 +1366,7 @@ typedef struct musicpal_key_state {
     qemu_irq out[8];
 } musicpal_key_state;
 
-static void musicpal_key_event(void *opaque, int keycode)
+static int musicpal_key_event(void *opaque, int keycode)
 {
     musicpal_key_state *s = opaque;
     uint32_t event = 0;
@@ -1374,7 +1374,7 @@ static void musicpal_key_event(void *opaque, int keycode)
 
     if (keycode == KEYCODE_EXTENDED) {
         s->kbd_extended = 1;
-        return;
+        return 0;
     }
 
     if (s->kbd_extended) {
@@ -1441,6 +1441,7 @@ static void musicpal_key_event(void *opaque, int keycode)
     }
 
     s->kbd_extended = 0;
+    return 0;
 }
 
 static int musicpal_key_init(SysBusDevice *dev)
@@ -1454,7 +1455,7 @@ static int musicpal_key_init(SysBusDevice *dev)
 
     qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out));
 
-    qemu_add_kbd_event_handler(musicpal_key_event, s);
+    qemu_add_kbd_event_handler(musicpal_key_event, s, "Musicpal");
 
     return 0;
 }
index 2f6f473..ddada06 100644 (file)
@@ -169,7 +169,8 @@ static void n8x0_nand_setup(struct n800_s *s)
                     onenand_base_unmap,
                     (s->nand = onenand_init(0xec4800, 1,
                                             omap2_gpio_in_get(s->cpu->gpif,
-                                                    N8X0_ONENAND_GPIO)[0])));
+                                                    N8X0_ONENAND_GPIO)[0],
+                                            ONENAND_2KB_PAGE, 2)));
     otp_region = onenand_raw_otp(s->nand);
 
     memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac));
@@ -209,7 +210,7 @@ static MouseTransformInfo n810_pointercal = {
 
 #define RETU_KEYCODE   61      /* F3 */
 
-static void n800_key_event(void *opaque, int keycode)
+static int n800_key_event(void *opaque, int keycode)
 {
     struct n800_s *s = (struct n800_s *) opaque;
     int code = s->keymap[keycode & 0x7f];
@@ -217,10 +218,11 @@ static void n800_key_event(void *opaque, int keycode)
     if (code == -1) {
         if ((keycode & 0x7f) == RETU_KEYCODE)
             retu_key_event(s->retu, !(keycode & 0x80));
-        return;
+        return 0;
     }
 
     tsc210x_key_event(s->ts.chip, code, !(keycode & 0x80));
+    return 0;
 }
 
 static const int n800_keys[16] = {
@@ -262,7 +264,7 @@ static void n800_tsc_kbd_setup(struct n800_s *s)
         if (n800_keys[i] >= 0)
             s->keymap[n800_keys[i]] = i;
 
-    qemu_add_kbd_event_handler(n800_key_event, s);
+    qemu_add_kbd_event_handler(n800_key_event, s, "Nokia n800");
 
     tsc210x_set_transform(s->ts.chip, &n800_pointercal);
 }
@@ -278,7 +280,7 @@ static void n810_tsc_setup(struct n800_s *s)
 }
 
 /* N810 Keyboard controller */
-static void n810_key_event(void *opaque, int keycode)
+static int n810_key_event(void *opaque, int keycode)
 {
     struct n800_s *s = (struct n800_s *) opaque;
     int code = s->keymap[keycode & 0x7f];
@@ -286,10 +288,11 @@ static void n810_key_event(void *opaque, int keycode)
     if (code == -1) {
         if ((keycode & 0x7f) == RETU_KEYCODE)
             retu_key_event(s->retu, !(keycode & 0x80));
-        return;
+        return 0;
     }
 
     lm832x_key_event(s->kbd, code, !(keycode & 0x80));
+    return 0;
 }
 
 #define M      0
@@ -371,7 +374,7 @@ static void n810_kbd_setup(struct n800_s *s)
         if (n810_keys[i] > 0)
             s->keymap[n810_keys[i]] = i;
 
-    qemu_add_kbd_event_handler(n810_key_event, s);
+    qemu_add_kbd_event_handler(n810_key_event, s, "Nokia n810");
 
     /* Attach the LM8322 keyboard to the I2C bus,
      * should happen in n8x0_i2c_setup and s->kbd be initialised here.  */
index 71c1ab4..dd6d96c 100644 (file)
 #include "irq.h"
 #include "blockdev.h"
 
-/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */
-#define PAGE_SHIFT     11
-
-/* Fixed */
-#define BLOCK_SHIFT    (PAGE_SHIFT + 6)
-
 typedef struct {
     uint32_t id;
     int shift;
@@ -65,6 +59,12 @@ typedef struct {
     int secs_cur;
     int blocks;
     uint8_t *blockwp;
+
+    int page_shift;
+    int block_shift;
+    int dbuf_num;
+
+    int superload;
 } OneNANDState;
 
 enum {
@@ -99,20 +99,33 @@ enum {
 void onenand_base_update(void *opaque, target_phys_addr_t new)
 {
     OneNANDState *s = (OneNANDState *) opaque;
+    int buf_data_size = (0x7e50 << s->shift);
+    int buf_boot_size = (0x0200 << s->shift);
+    int slow_buf_size = buf_data_size % TARGET_PAGE_SIZE ?
+                           buf_data_size % TARGET_PAGE_SIZE :
+                           TARGET_PAGE_SIZE;
+    int fast_buf_size = buf_data_size - slow_buf_size;
 
     s->base = new;
 
     /* XXX: We should use IO_MEM_ROMD but we broke it earlier...
      * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to
      * write boot commands.  Also take note of the BWPS bit.  */
-    cpu_register_physical_memory(s->base + (0x0000 << s->shift),
-                    0x0200 << s->shift, s->iomemtype);
-    cpu_register_physical_memory(s->base + (0x0200 << s->shift),
-                    0xbe00 << s->shift,
-                    (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM);
-    if (s->iomemtype)
+    cpu_register_physical_memory(s->base, buf_boot_size, s->iomemtype);
+
+    cpu_register_physical_memory(s->base + buf_boot_size,
+                    fast_buf_size,
+                    (s->ram + buf_boot_size) | IO_MEM_RAM);
+
+    cpu_register_physical_memory_offset(s->base + buf_boot_size + fast_buf_size,
+                                        slow_buf_size, s->iomemtype,
+                                        buf_boot_size + fast_buf_size);
+
+    if (s->iomemtype) {
         cpu_register_physical_memory_offset(s->base + (0xc000 << s->shift),
-                    0x4000 << s->shift, s->iomemtype, (0xc000 << s->shift));
+                                            0x4000 << s->shift, s->iomemtype,
+                                            (0xc000 << s->shift));
+    }
 }
 
 void onenand_base_unmap(void *opaque)
@@ -135,7 +148,7 @@ static void onenand_reset(OneNANDState *s, int cold)
     s->command = 0;
     s->count = 1;
     s->bufaddr = 0;
-    s->config[0] = 0x40c0;
+    s->config[0] = 0xc0c0;
     s->config[1] = 0x0000;
     onenand_intr_update(s);
     qemu_irq_raise(s->rdy);
@@ -149,6 +162,7 @@ static void onenand_reset(OneNANDState *s, int cold)
     s->bdrv_cur = s->bdrv;
     s->current = s->image;
     s->secs_cur = s->secs;
+    s->superload = 0;
 
     if (cold) {
         /* Lock the whole flash */
@@ -194,11 +208,12 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
         if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
             return 1;
         memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
-    } else if (sec + secn > s->secs_cur)
+    } else if (sec + secn > s->secs_cur) {
         return 1;
-    else
+    } else {
         memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
+    }
+
     return 0;
 }
 
@@ -212,11 +227,12 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
             return 1;
         memcpy(buf + ((sec & 31) << 4), src, secn << 4);
         return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0;
-    } else if (sec + secn > s->secs_cur)
+    } else if (sec + secn > s->secs_cur) {
         return 1;
+    }
 
     memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
+
     return 0;
 }
 
@@ -226,7 +242,7 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num)
     uint8_t buf[512];
 
     memset(buf, 0xff, sizeof(buf));
-    for (; num > 0; num --, sec ++) {
+    for (; num > 0; num--, sec++) {
         if (onenand_prog_main(s, sec, 1, buf))
             return 1;
         if (onenand_prog_spare(s, sec, 1, buf))
@@ -246,7 +262,7 @@ static void onenand_command(OneNANDState *s, int cmd)
             ((((s->addr[page] >> 2) & 0x3f) +  \
               (((s->addr[block] & 0xfff) |     \
                 (s->addr[block] >> 15 ?                \
-                 s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
+                 s->density_mask : 0)) << 6)) << (s->page_shift - 9));
 #define SETBUF_M()                             \
     buf = (s->bufaddr & 8) ?                   \
             s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0];    \
@@ -255,6 +271,7 @@ static void onenand_command(OneNANDState *s, int cmd)
     buf = (s->bufaddr & 8) ?                   \
             s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1];    \
     buf += (s->bufaddr & 3) << 4;
+#define IS_SAMSUNG_ONENAND() (((s->id >> 16) & 0xff) == 0xEC)
 
     switch (cmd) {
     case 0x00: /* Load single/multiple sector data unit into buffer */
@@ -264,11 +281,11 @@ static void onenand_command(OneNANDState *s, int cmd)
         if (onenand_load_main(s, sec, s->count, buf))
             s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
 
-#if 0
-        SETBUF_S()
-        if (onenand_load_spare(s, sec, s->count, buf))
-            s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
-#endif
+        if (IS_SAMSUNG_ONENAND()) {
+            SETBUF_S()
+            if (onenand_load_spare(s, sec, s->count, buf))
+                s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+        }
 
         /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
          * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
@@ -276,6 +293,9 @@ static void onenand_command(OneNANDState *s, int cmd)
          */
         s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
         break;
+    case 0x03:  /* Superload */
+        s->superload = 1;
+        break;
     case 0x13: /* Load single/multiple spare sector into buffer */
         SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
 
@@ -296,11 +316,11 @@ static void onenand_command(OneNANDState *s, int cmd)
         if (onenand_prog_main(s, sec, s->count, buf))
             s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
 
-#if 0
-        SETBUF_S()
-        if (onenand_prog_spare(s, sec, s->count, buf))
-            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
-#endif
+        if (IS_SAMSUNG_ONENAND()) {
+            SETBUF_S()
+            if (onenand_prog_spare(s, sec, s->count, buf))
+                s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+        }
 
         /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
          * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
@@ -405,8 +425,8 @@ static void onenand_command(OneNANDState *s, int cmd)
     case 0x94: /* Block erase */
         sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) |
                         (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0))
-                << (BLOCK_SHIFT - 9);
-        if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9)))
+                << (s->block_shift - 9);
+        if (onenand_erase(s, sec, 1 << (s->block_shift - 9)))
             s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;
 
         s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
@@ -428,7 +448,7 @@ static void onenand_command(OneNANDState *s, int cmd)
         s->intstatus |= ONEN_INT;
         s->bdrv_cur = NULL;
         s->current = s->otp;
-        s->secs_cur = 1 << (BLOCK_SHIFT - 9);
+        s->secs_cur = 1 << (s->block_shift - 9);
         s->addr[ONEN_BUF_BLOCK] = 0;
         s->otpmode = 1;
         break;
@@ -447,11 +467,18 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
 {
     OneNANDState *s = (OneNANDState *) opaque;
     int offset = addr >> s->shift;
+    int res = 0;
 
     switch (offset) {
-    case 0x0000 ... 0xc000:
+    case 0x0000 ... 0x804e:
         return lduw_le_p(s->boot[0] + addr);
-
+    case 0x804f:
+        res = lduw_le_p(s->boot[0] + addr);
+        if (s->superload) {
+            onenand_command(s, 0x00); /* Read */
+        }
+        s->superload = 0;
+        return res;
     case 0xf000:       /* Manufacturer ID */
         return (s->id >> 16) & 0xff;
     case 0xf001:       /* Device ID */
@@ -460,11 +487,12 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
     case 0xf002:       /* Version ID */
         return (s->id >>  0) & 0xff;
     case 0xf003:       /* Data Buffer size */
-        return 1 << PAGE_SHIFT;
+        /* Number of buffers each of one page size measured in words */
+        return s->dbuf_num << (s->page_shift - 1);
     case 0xf004:       /* Boot Buffer size */
         return 0x200;
     case 0xf005:       /* Amount of buffers */
-        return 1 | (2 << 8);
+        return 1 | (s->dbuf_num << 8);
     case 0xf006:       /* Technology */
         return 0;
 
@@ -472,7 +500,8 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
         return s->addr[offset - 0xf100];
 
     case 0xf200:       /* Start buffer */
-        return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10)));
+        return (s->bufaddr << 8) |
+               (s->count & (1 << (s->page_shift - 9)) ? 0 : s->count);
 
     case 0xf220:       /* Command */
         return s->command;
@@ -498,7 +527,9 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
     case 0xff02:       /* ECC Result of spare area data */
     case 0xff03:       /* ECC Result of main area data */
     case 0xff04:       /* ECC Result of spare area data */
-        hw_error("%s: imeplement ECC\n", __FUNCTION__);
+        /* Why we do ever need to care about ECC?  There can't be any errors.
+         * So return zero as a success status.  */
+        /* hw_error("%s: implement ECC\n", __FUNCTION__); */
         return 0x0000;
     }
 
@@ -507,8 +538,14 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
     return 0;
 }
 
-static void onenand_write(void *opaque, target_phys_addr_t addr,
-                uint32_t value)
+static uint32_t onenand_read_32(void *opaque, target_phys_addr_t addr)
+{
+    uint32_t res = onenand_read(opaque, addr) |
+                   (onenand_read(opaque, addr + 2) << 16);
+    return res;
+}
+
+static void onenand_write(void *opaque, target_phys_addr_t addr, uint32_t value)
 {
     OneNANDState *s = (OneNANDState *) opaque;
     int offset = addr >> s->shift;
@@ -523,7 +560,7 @@ static void onenand_write(void *opaque, target_phys_addr_t addr,
             if (value == 0x0000) {
                 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
                 onenand_load_main(s, sec,
-                                1 << (PAGE_SHIFT - 9), s->data[0][0]);
+                                1 << (s->page_shift - 9), s->data[0][0]);
                 s->addr[ONEN_BUF_PAGE] += 4;
                 s->addr[ONEN_BUF_PAGE] &= 0xff;
             }
@@ -552,15 +589,22 @@ static void onenand_write(void *opaque, target_phys_addr_t addr,
         }
         break;
 
+    case 0x0200 ... 0x7fff:
+    case 0x8010 ... 0x804f:
+        stw_le_p(s->boot[0] + addr, (uint16_t)value);
+        break;
+
     case 0xf100 ... 0xf107:    /* Start addresses */
         s->addr[offset - 0xf100] = value;
         break;
 
     case 0xf200:       /* Start buffer */
         s->bufaddr = (value >> 8) & 0xf;
-        if (PAGE_SHIFT == 11)
+        if (s->page_shift == 12)
+            s->count = (value & 7) ?: 8;
+        else if (s->page_shift == 11)
             s->count = (value & 3) ?: 4;
-        else if (PAGE_SHIFT == 10)
+        else if (s->page_shift == 10)
             s->count = (value & 1) ?: 2;
         break;
 
@@ -603,30 +647,45 @@ static void onenand_write(void *opaque, target_phys_addr_t addr,
     }
 }
 
+static void onenand_write_32(void *opaque, target_phys_addr_t addr,
+                             uint32_t value)
+{
+    onenand_write(opaque, addr, value & 0xffff);
+    onenand_write(opaque, addr + 2, value >> 16);
+}
+
 static CPUReadMemoryFunc * const onenand_readfn[] = {
     onenand_read,      /* TODO */
     onenand_read,
-    onenand_read,
+    onenand_read_32,
 };
 
 static CPUWriteMemoryFunc * const onenand_writefn[] = {
     onenand_write,     /* TODO */
     onenand_write,
-    onenand_write,
+    onenand_write_32,
 };
 
-void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq,
+                   int page_size, int data_buf)
 {
     OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s));
     DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
     uint32_t size = 1 << (24 + ((id >> 12) & 7));
     void *ram;
 
+    /* 12 for 4kB-page OneNAND, 11 for 2kB-page OneNAND ("2nd generation") and
+       10 for 1kB-page chips */
+    s->page_shift = page_size;
+    s->dbuf_num = data_buf;
+
+    /* Fixed */
+    s->block_shift = s->page_shift + 6;
     s->shift = regshift;
     s->intr = irq;
     s->rdy = NULL;
     s->id = id;
-    s->blocks = size >> BLOCK_SHIFT;
+    s->blocks = size >> s->block_shift;
     s->secs = size >> 9;
     s->blockwp = qemu_malloc(s->blocks);
     s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0;
@@ -637,16 +696,21 @@ void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
                         0xff, size + (size >> 5));
     else
         s->bdrv = dinfo->bdrv;
-    s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
-                    0xff, (64 + 2) << PAGE_SHIFT);
+    s->otp = memset(qemu_malloc((64 + 2) << s->page_shift),
+                    0xff, (64 + 2) << s->page_shift);
     s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift);
     ram = qemu_get_ram_ptr(s->ram);
     s->boot[0] = ram + (0x0000 << s->shift);
     s->boot[1] = ram + (0x8000 << s->shift);
-    s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift);
-    s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift);
-    s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift);
-    s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift);
+    s->data[0][0] = ram + ((0x0200 + (0 << (s->page_shift - 1))) << s->shift);
+    s->data[0][1] = ram + ((0x8010 + (0 << (s->page_shift - 6))) << s->shift);
+    if (s->page_shift < 12) {
+        /* FIXME: what is here when page_shift IS 12? */
+        s->data[1][0] =
+            ram + ((0x0200 + (1 << (s->page_shift - 1))) << s->shift);
+        s->data[1][1] =
+            ram + ((0x8010 + (1 << (s->page_shift - 6))) << s->shift);
+    }
 
     onenand_reset(s, 1);
 
index f22d777..d8e2857 100644 (file)
--- a/hw/palm.c
+++ b/hw/palm.c
@@ -120,7 +120,7 @@ static struct {
     [0x39] = { 4, 1 }, /* Spc  -> Centre */
 };
 
-static void palmte_button_event(void *opaque, int keycode)
+static int palmte_button_event(void *opaque, int keycode)
 {
     struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque;
 
@@ -129,6 +129,7 @@ static void palmte_button_event(void *opaque, int keycode)
                         palmte_keymap[keycode & 0x7f].row,
                         palmte_keymap[keycode & 0x7f].column,
                         !(keycode & 0x80));
+    return 0;
 }
 
 static void palmte_onoff_gpios(void *opaque, int line, int level)
@@ -232,7 +233,7 @@ static void palmte_init(ram_addr_t ram_size,
 
     palmte_microwire_setup(cpu);
 
-    qemu_add_kbd_event_handler(palmte_button_event, cpu);
+    qemu_add_kbd_event_handler(palmte_button_event, cpu, "Palm Keyboard");
 
     palmte_gpio_setup(cpu);
 
diff --git a/hw/pci_host_template.h b/hw/pci_host_template.h
new file mode 100644 (file)
index 0000000..11e6c88
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * QEMU Common PCI Host bridge configuration data space access routines.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Worker routines for a PCI host controller that uses an {address,data}
+   register pair to access PCI configuration space.  */
+
+static void glue(pci_host_data_writeb, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+
+    PCI_DPRINTF("writeb addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1);
+}
+
+static void glue(pci_host_data_writew, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    PCI_DPRINTF("writew addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2);
+}
+
+static void glue(pci_host_data_writel, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
+    PCI_DPRINTF("writel addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg, val, 4);
+}
+
+static uint32_t glue(pci_host_data_readb, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr)
+{
+    PCIHostState *s = opaque;
+    uint32_t val;
+
+    if (!(s->config_reg & (1 << 31)))
+        return 0xff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 1);
+    PCI_DPRINTF("readb addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+    return val;
+}
+
+static uint32_t glue(pci_host_data_readw, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr)
+{
+    PCIHostState *s = opaque;
+    uint32_t val;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xffff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2);
+    PCI_DPRINTF("readw addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    return val;
+}
+
+static uint32_t glue(pci_host_data_readl, PCI_HOST_SUFFIX)(
+    void* opaque, PCI_ADDR_T addr)
+{
+    PCIHostState *s = opaque;
+    uint32_t val;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xffffffff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4);
+    PCI_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n",
+                (target_phys_addr_t)addr, val);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
+    return val;
+}
index 68561fa..8b4cfb8 100644 (file)
 #define PCI_VENDOR_ID_INTEL              0x8086
 #define PCI_DEVICE_ID_INTEL_82441        0x1237
 #define PCI_DEVICE_ID_INTEL_82801AA_5    0x2415
+#define PCI_DEVICE_ID_INTEL_82801D       0x24CD
 #define PCI_DEVICE_ID_INTEL_ESB_9        0x25ab
 #define PCI_DEVICE_ID_INTEL_82371SB_0    0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1    0x7010
diff --git a/hw/pl192.c b/hw/pl192.c
new file mode 100644 (file)
index 0000000..8a3c51d
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * ARM PrimeCell PL192 Vector Interrupt Controller
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "primecell.h"
+
+
+#define PL192_INT_SOURCES   32
+#define PL192_DAISY_IRQ     PL192_INT_SOURCES
+#define PL192_NO_IRQ        PL192_INT_SOURCES+1
+#define PL192_PRIO_LEVELS   16
+
+#define PL192_IRQSTATUS         0x00
+#define PL192_FIQSTATUS         0x04
+#define PL192_RAWINTR           0x08
+#define PL192_INTSELECT         0x0C
+#define PL192_INTENABLE         0x10
+#define PL192_INTENCLEAR        0x14
+#define PL192_SOFTINT           0x18
+#define PL192_SOFTINTCLEAR      0x1C
+#define PL192_PROTECTION        0x20
+#define PL192_SWPRIORITYMASK    0x24
+#define PL192_PRIORITYDAISY     0x28
+#define PL192_VECTADDR          0xF00
+#define PL192_IOMEM_SIZE        0x1000
+
+#define PL190_ITCR              0x300
+#define PL190_VECTADDR          0x30
+#define PL190_DEFVECTADDR       0x34
+
+#define PL192_IOMEM_SIZE    0x1000
+
+
+typedef struct pl192_state_s {
+    SysBusDevice busdev;
+    uint32_t instance;
+
+    /* Control registers */
+    uint32_t irq_status;
+    uint32_t fiq_status;
+    uint32_t rawintr;
+    uint32_t intselect;
+    uint32_t intenable;
+    uint32_t softint;
+    uint32_t protection;
+    uint32_t sw_priority_mask;
+    uint32_t vect_addr[PL192_INT_SOURCES];
+    uint32_t vect_priority[PL192_INT_SOURCES];
+    uint32_t address;
+
+    /* Currently processed interrupt and
+       highest priority interrupt */
+    uint32_t current;
+    uint32_t current_highest;
+
+    /* Priority masking logic */
+    int32_t stack_i;
+    uint32_t priority_stack[PL192_PRIO_LEVELS+1];
+    uint8_t irq_stack[PL192_PRIO_LEVELS+1];
+    uint32_t priority;
+
+    /* Daisy-chain interface */
+    uint32_t daisy_vectaddr;
+    uint32_t daisy_priority;
+    struct pl192_state_s *daisy_callback;
+    uint8_t  daisy_input;
+
+    /* Parent interrupts */
+    qemu_irq irq;
+    qemu_irq fiq;
+
+    /* Next controller in chain */
+    struct pl192_state_s *daisy;
+} pl192_state;
+
+const unsigned char pl192_id[] =
+{ 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
+
+
+static void pl192_update(pl192_state *);
+
+static void pl192_raise(pl192_state *s, int is_fiq)
+{
+    if (is_fiq) {
+        if (s->fiq) {
+            /* Raise parent FIQ */
+            qemu_irq_raise(s->fiq);
+        } else {
+            if (s->daisy) {
+                /* FIQ is directly propagated through daisy chain */
+                pl192_raise(s->daisy, is_fiq);
+            } else {
+                hw_error("pl192: cannot raise FIQ. This usually means that "
+                         "initialization was done incorrectly.\n");
+            }
+        }
+    } else {
+        if (s->irq) {
+            /* Raise parent IRQ */
+            qemu_irq_raise(s->irq);
+        } else {
+            if (s->daisy) {
+                /* Setup daisy input of the next chained contorller and force
+                   it to update it's state */
+                s->daisy->daisy_vectaddr = s->address;
+                s->daisy->daisy_callback = s;
+                s->daisy->daisy_input = 1;
+                pl192_update(s->daisy);
+            } else {
+                hw_error("pl192: cannot raise IRQ. This usually means that "
+                         "initialization was done incorrectly.\n");
+            }
+        }
+    }
+}
+
+static void pl192_lower(pl192_state *s, int is_fiq)
+{
+    /* Lower parrent interrupt if there is one */
+    if (is_fiq && s->fiq) {
+        qemu_irq_lower(s->fiq);
+    }
+    if (!is_fiq && s->irq) {
+        qemu_irq_lower(s->irq);
+    }
+    /* Propagate to the previous controller in chain if needed */
+    if (s->daisy) {
+        if (!is_fiq) {
+            s->daisy->daisy_input = 0;
+            pl192_update(s->daisy);
+        } else {
+            pl192_lower(s->daisy, is_fiq);
+        }
+    }
+}
+
+/* Find interrupt of the highest priority */
+static uint32_t pl192_priority_sorter(pl192_state *s)
+{
+    int i;
+    uint32_t prio_irq[PL192_PRIO_LEVELS];
+
+    for (i = 0; i < PL192_PRIO_LEVELS; i++) {
+        prio_irq[i] = PL192_NO_IRQ;
+    }
+    if (s->daisy_input) {
+        prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
+    }
+    for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
+        if (s->irq_status & (1 << i)) {
+            prio_irq[s->vect_priority[i]] = i;
+        }
+    }
+    for (i = 0; i < PL192_PRIO_LEVELS; i++) {
+        if ((s->sw_priority_mask & (1 << i)) &&
+            prio_irq[i] <= PL192_DAISY_IRQ) {
+            return prio_irq[i];
+        }
+    }
+    return PL192_NO_IRQ;
+}
+
+static void pl192_update(pl192_state *s)
+{
+    /* TODO: does SOFTINT affects IRQ_STATUS??? */
+    s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
+    s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
+    if (s->fiq_status) {
+        pl192_raise(s, 1);
+    } else {
+        pl192_lower(s, 1);
+    }
+    if (s->irq_status || s->daisy_input) {
+        s->current_highest = pl192_priority_sorter(s);
+        if (s->current_highest < PL192_INT_SOURCES) {
+            s->address = s->vect_addr[s->current_highest];
+        } else {
+            s->address = s->daisy_vectaddr;
+        }
+        if (s->current_highest != s->current) {
+            if (s->current_highest < PL192_INT_SOURCES) {
+                if (s->vect_priority[s->current_highest] >= s->priority) {
+                    return ;
+                }
+            }
+            if (s->current_highest == PL192_DAISY_IRQ) {
+                if (s->daisy_priority >= s->priority) {
+                    return ;
+                }
+            }
+            if (s->current_highest <= PL192_DAISY_IRQ) {
+                pl192_raise(s, 0);
+            } else {
+                pl192_lower(s, 0);
+            }
+        }
+    } else {
+        s->current_highest = PL192_NO_IRQ;
+        pl192_lower(s, 0);
+    }
+}
+
+/* Set priority level when an interrupt have been acknoledged by CPU.
+   Also save interrupt id and priority to stack so it can be restored
+   lately. */
+static inline void pl192_mask_priority(pl192_state *s)
+{
+    if (s->stack_i >= PL192_INT_SOURCES) {
+        hw_error("pl192: internal error\n");
+    }
+    s->stack_i++;
+    if (s->current == PL192_DAISY_IRQ) {
+        s->priority = s->daisy_priority;
+    } else {
+        s->priority = s->vect_priority[s->current];
+    }
+    s->priority_stack[s->stack_i] = s->priority;
+    s->irq_stack[s->stack_i] = s->current;
+}
+
+/* Set priority level when interrupt have been successfully processed by CPU.
+   Also restore previous interrupt id and priority level. */
+static inline void pl192_unmask_priority(pl192_state *s)
+{
+    if (s->stack_i < 1) {
+        hw_error("pl192: internal error\n");
+    }
+    s->stack_i--;
+    s->priority = s->priority_stack[s->stack_i];
+    s->current = s->irq_stack[s->stack_i];
+}
+
+/* IRQ was acknoledged by CPU. Update controller state accordingly */
+static uint32_t pl192_irq_ack(pl192_state *s)
+{
+    int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
+    uint32_t res = s->address;
+
+    s->current = s->current_highest;
+    pl192_mask_priority(s);
+    if (is_daisy) {
+        pl192_mask_priority(s->daisy_callback);
+    }
+    pl192_update(s);
+    return res;
+}
+
+/* IRQ was processed by CPU. Update controller state accrodingly */
+static void pl192_irq_fin(pl192_state *s)
+{
+    int is_daisy = (s->current == PL192_DAISY_IRQ);
+
+    pl192_unmask_priority(s);
+    if (is_daisy) {
+        pl192_unmask_priority(s->daisy_callback);
+    }
+    pl192_update(s);
+    if (s->current == PL192_NO_IRQ) {
+        pl192_lower(s, 0);
+    }
+}
+
+static uint32_t pl192_read(void *opaque, target_phys_addr_t offset)
+{
+    pl192_state *s = (pl192_state *) opaque;
+
+    if (offset & 3) {
+        hw_error("pl192: bad read offset " TARGET_FMT_plx "\n", offset);
+        return 0;
+    }
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return pl192_id[(offset - 0xfe0) >> 2];
+    }
+    if (offset >= 0x100 && offset < 0x180) {
+        return s->vect_addr[(offset - 0x100) >> 2];
+    }
+    if (offset >= 0x200 && offset < 0x280) {
+        return s->vect_priority[(offset - 0x200) >> 2];
+    }
+
+    switch (offset) {
+        case PL192_IRQSTATUS:
+            return s->irq_status;
+        case PL192_FIQSTATUS:
+            return s->fiq_status;
+        case PL192_RAWINTR:
+            return s->rawintr;
+        case PL192_INTSELECT:
+            return s->intselect;
+        case PL192_INTENABLE:
+            return s->intenable;
+        case PL192_SOFTINT:
+            return s->softint;
+        case PL192_PROTECTION:
+            return s->protection;
+        case PL192_SWPRIORITYMASK:
+            return s->sw_priority_mask;
+        case PL192_PRIORITYDAISY:
+            return s->daisy_priority;
+        case PL192_INTENCLEAR:
+        case PL192_SOFTINTCLEAR:
+            hw_error("pl192: attempt to read write-only register (offset = "
+                     TARGET_FMT_plx ")\n", offset);
+        case PL192_VECTADDR:
+            return pl192_irq_ack(s);
+        /* Workaround for kernel code using PL190 */
+        case PL190_ITCR:
+        case PL190_VECTADDR:
+        case PL190_DEFVECTADDR:
+            return 0;
+        default:
+            hw_error("pl192: bad read offset " TARGET_FMT_plx "\n", offset);
+            return 0;
+    }
+}
+
+static void pl192_write(void *opaque, target_phys_addr_t offset,
+                        uint32_t value)
+{
+    pl192_state *s = (pl192_state *) opaque;
+
+    if (offset & 3) {
+        hw_error("pl192: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        hw_error("pl192: attempt to write to a read-only register (offset = "
+                 TARGET_FMT_plx ")\n", offset);
+    }
+    if (offset >= 0x100 && offset < 0x180) {
+        s->vect_addr[(offset - 0x100) >> 2] = value;
+        pl192_update(s);
+        return;
+    }
+    if (offset >= 0x200 && offset < 0x280) {
+        s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
+        pl192_update(s);
+        return;
+    }
+
+    switch (offset) {
+        case PL192_IRQSTATUS:
+            /* This is a readonly register, but linux tries to write to it
+               anyway.  Ignore the write.  */
+            return;
+        case PL192_FIQSTATUS:
+        case PL192_RAWINTR:
+            hw_error("pl192: attempt to write to a read-only register (offset = "
+                     TARGET_FMT_plx ")\n", offset);
+            break;
+        case PL192_INTSELECT:
+            s->intselect = value;
+            break;
+        case PL192_INTENABLE:
+            s->intenable |= value;
+            break;
+        case PL192_INTENCLEAR:
+            s->intenable &= ~value;
+            break;
+        case PL192_SOFTINT:
+            s->softint |= value;
+            break;
+        case PL192_SOFTINTCLEAR:
+            s->softint &= ~value;
+            break;
+        case PL192_PROTECTION:
+            /* TODO: implement protection */
+            s->protection = value & 1;
+            break;
+        case PL192_SWPRIORITYMASK:
+            s->sw_priority_mask = value & 0xffff;
+            break;
+        case PL192_PRIORITYDAISY:
+            s->daisy_priority = value & 0xf;
+            break;
+        case PL192_VECTADDR:
+            pl192_irq_fin(s);
+            return;
+        case PL190_ITCR:
+        case PL190_VECTADDR:
+        case PL190_DEFVECTADDR:
+            /* NB: This thing is not present here, but linux wants to write it */
+            /* Ignore written value */
+            return;
+        default:
+            hw_error("pl192: bad write offset " TARGET_FMT_plx "\n", offset);
+            return;
+    }
+
+    pl192_update(s);
+}
+
+static void pl192_irq_handler(void *opaque, int irq, int level)
+{
+    pl192_state *s = (pl192_state *) opaque;
+
+    if (level) {
+        s->rawintr |= 1 << irq;
+    } else {
+        s->rawintr &= ~(1 << irq);
+    }
+    pl192_update(opaque);
+}
+
+static void pl192_reset(DeviceState *d)
+{
+    pl192_state *s = FROM_SYSBUS(pl192_state, sysbus_from_qdev(d));
+    int i;
+
+    for (i = 0; i < PL192_INT_SOURCES; i++) {
+        s->vect_priority[i] = 0xf;
+    }
+    s->sw_priority_mask = 0xffff;
+    s->daisy_priority = 0xf;
+    s->current = PL192_NO_IRQ;
+    s->current_highest = PL192_NO_IRQ;
+    s->stack_i = 0;
+    s->priority_stack[0] = 0x10;
+    s->irq_stack[0] = PL192_NO_IRQ;
+    s->priority = 0x10;
+}
+
+static CPUReadMemoryFunc * const pl192_readfn[] = {
+    pl192_read,
+    pl192_read,
+    pl192_read
+};
+
+static CPUWriteMemoryFunc * const pl192_writefn[] = {
+    pl192_write,
+    pl192_write,
+    pl192_write
+};
+
+static void pl192_save(QEMUFile *f, void *opaque)
+{
+    pl192_state *s = (pl192_state *) opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->irq_status);
+    qemu_put_be32s(f, &s->fiq_status);
+    qemu_put_be32s(f, &s->rawintr);
+    qemu_put_be32s(f, &s->intselect);
+    qemu_put_be32s(f, &s->intenable);
+    qemu_put_be32s(f, &s->softint);
+    qemu_put_be32s(f, &s->protection);
+    qemu_put_be32s(f, &s->sw_priority_mask);
+
+    for (i = 0; i < PL192_INT_SOURCES; i++) {
+        qemu_put_be32s(f, &s->vect_addr[i]);
+        qemu_put_be32s(f, &s->vect_priority[i]);
+    }
+
+    qemu_put_be32s(f, &s->address);
+    qemu_put_be32s(f, &s->current);
+    qemu_put_be32s(f, &s->current_highest);
+    qemu_put_sbe32s(f, &s->stack_i);
+
+    for (i = 0; i <= PL192_PRIO_LEVELS; i++) {
+        qemu_put_be32s(f, &s->priority_stack[i]);
+        qemu_put_8s   (f, &s->irq_stack[i]);
+    }
+
+    qemu_put_be32s(f, &s->priority);
+    qemu_put_be32s(f, &s->daisy_vectaddr);
+    qemu_put_be32s(f, &s->daisy_priority);
+    qemu_put_8s   (f, &s->daisy_input);
+}
+
+static int pl192_load(QEMUFile *f, void *opaque, int version_id)
+{
+    pl192_state *s = (pl192_state *) opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->irq_status);
+    qemu_get_be32s(f, &s->fiq_status);
+    qemu_get_be32s(f, &s->rawintr);
+    qemu_get_be32s(f, &s->intselect);
+    qemu_get_be32s(f, &s->intenable);
+    qemu_get_be32s(f, &s->softint);
+    qemu_get_be32s(f, &s->protection);
+    qemu_get_be32s(f, &s->sw_priority_mask);
+
+    for (i = 0; i < PL192_INT_SOURCES; i++) {
+        qemu_get_be32s(f, &s->vect_addr[i]);
+        qemu_get_be32s(f, &s->vect_priority[i]);
+    }
+
+    qemu_get_be32s(f, &s->address);
+    qemu_get_be32s(f, &s->current);
+    qemu_get_be32s(f, &s->current_highest);
+    qemu_get_sbe32s(f, &s->stack_i);
+
+    for (i = 0; i <= PL192_PRIO_LEVELS; i++) {
+        qemu_get_be32s(f, &s->priority_stack[i]);
+        qemu_get_8s   (f, &s->irq_stack[i]);
+    }
+
+    qemu_get_be32s(f, &s->priority);
+    qemu_get_be32s(f, &s->daisy_vectaddr);
+    qemu_get_be32s(f, &s->daisy_priority);
+    qemu_get_8s   (f, &s->daisy_input);
+
+    return 0;
+}
+
+DeviceState *pl192_init(target_phys_addr_t base, int instance, ...)
+{
+    va_list va;
+    qemu_irq irq;
+    int n;
+
+    DeviceState *dev = qdev_create(NULL, "pl192");
+    SysBusDevice *s  = sysbus_from_qdev(dev);
+    qdev_prop_set_uint32(dev, "instance", instance);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(s, 0, base);
+
+    va_start(va, instance);
+    n = 0;
+    while (1) {
+        irq = va_arg(va, qemu_irq);
+        if (!irq)
+            break;
+        sysbus_connect_irq(s, n, irq);
+        n++;
+    }
+    return dev;
+}
+
+static int pl192_init1(SysBusDevice *dev)
+{
+    pl192_state *s = FROM_SYSBUS(pl192_state, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->irq);
+    sysbus_init_irq(dev, &s->fiq);
+
+    /* Allocate IRQs */
+    qdev_init_gpio_in(&dev->qdev, pl192_irq_handler, PL192_INT_SOURCES);
+
+    /* Map Interrupt Controller registers to memory */
+    iomemtype = cpu_register_io_memory(pl192_readfn, pl192_writefn, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, PL192_IOMEM_SIZE, iomemtype);
+
+    /* TODO: Interrupt Controller coprocessor??? */
+    pl192_reset(&s->busdev.qdev);
+
+    register_savevm(&dev->qdev, "pl192", s->instance, 1,
+                    pl192_save, pl192_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo pl192_info = {
+    .init       = pl192_init1,
+    .qdev.name  = "pl192",
+    .qdev.size  = sizeof(pl192_state),
+    .qdev.reset = pl192_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("instance", pl192_state, instance, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void pl192_register_devices(void)
+{
+    sysbus_register_withprop(&pl192_info);
+}
+
+void pl192_chain(void *first, void *next)
+{
+    pl192_state *s1 = FROM_SYSBUS(pl192_state, (SysBusDevice *)first);
+    pl192_state *s2 = FROM_SYSBUS(pl192_state, (SysBusDevice *)next);
+
+    s2->daisy = s1;
+}
+
+device_init(pl192_register_devices)
diff --git a/hw/pl330.c b/hw/pl330.c
new file mode 100644 (file)
index 0000000..b72d2c4
--- /dev/null
@@ -0,0 +1,1571 @@
+/*
+ * ARM PrimeCell PL330 DMA Controller
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "primecell.h"
+#include "qemu-timer.h"
+
+
+#define PL330_CHAN_NUM              8
+#define PL330_PERIPH_NUM            32
+#define PL330_MAX_BURST_LEN         128
+#define PL330_INSN_MAXSIZE          6
+
+#define PL330_FIFO_OK               0
+#define PL330_FIFO_STALL            1
+#define PL330_FIFO_ERR              (-1)
+
+#define PL330_FAULT_UNDEF_INSTR     (1 <<  0)
+#define PL330_FAULT_OPERAND_INVALID (1 <<  1)
+#define PL330_FAULT_DMAGO_ER        (1 <<  4)
+#define PL330_FAULT_EVENT_ER        (1 <<  5)
+#define PL330_FAULT_CH_PERIPH_ER    (1 <<  6)
+#define PL330_FAULT_CH_RDWR_ER      (1 <<  7)
+#define PL330_FAULT_MFIFO_ER        (1 << 12)
+#define PL330_FAULT_INSTR_FETCH_ER  (1 << 16)
+#define PL330_FAULT_DATA_WRITE_ER   (1 << 17)
+#define PL330_FAULT_DATA_READ_ER    (1 << 18)
+#define PL330_FAULT_DBG_INSTR       (1 << 30)
+#define PL330_FAULT_LOCKUP_ER       (1 << 31)
+
+#define PL330_UNTAGGED              0xff
+
+#define PL330_SINGLE                0x0
+#define PL330_BURST                 0x1
+
+#define PL330_WATCHDOG_LIMIT        1024
+
+/* IOMEM mapped registers */
+#define PL330_REG_DS        0x000
+#define PL330_REG_DPC       0x004
+#define PL330_REG_INTEN     0x020
+#define PL330_REG_ES        0x024
+#define PL330_REG_INTSTATUS 0x028
+#define PL330_REG_INTCLR    0x02C
+#define PL330_REG_FSM       0x030
+#define PL330_REG_FSC       0x034
+#define PL330_REG_FTM       0x038
+#define PL330_REG_FTC_BASE  0x040
+#define PL330_REG_CS_BASE   0x100
+#define PL330_REG_CPC_BASE  0x104
+#define PL330_REG_CHANCTRL  0x400
+#define PL330_REG_DBGSTATUS 0xD00
+#define PL330_REG_DBGCMD    0xD04
+#define PL330_REG_DBGINST0  0xD08
+#define PL330_REG_DBGINST1  0xD0C
+#define PL330_REG_CONFIG    0xE00
+#define PL330_REG_ID        0xFE0
+
+#define PL330_IOMEM_SIZE    0x1000
+
+
+static const uint32_t pl330_id[] =
+{ 0x30, 0x13, 0x04, 0x00, 0xB1, 0x05, 0xF0, 0x0D };
+
+
+/* DMA chanel states as they are described in PL330 Technical Reference Manual
+   Most of them will not be used in emulation. */
+enum pl330_chan_enum {
+    pl330_chan_stopped = 0,
+    pl330_chan_executing = 1,
+    pl330_chan_completing,
+    pl330_chan_waiting_periph,
+    pl330_chan_at_barrier,
+    pl330_chan_waiting_event = 4,
+    pl330_chan_updating_pc = 3,
+    pl330_chan_cache_miss,
+    pl330_chan_fault_completing,
+    pl330_chan_fault = 15,
+    pl330_chan_killing
+};
+
+struct pl330_chan_state;
+struct pl330_fifo;
+struct pl330_queue_entry;
+struct pl330_queue;
+struct pl330_state;
+struct pl330_insn_desc;
+
+typedef struct pl330_chan_state pl330_chan_state;
+typedef struct pl330_fifo pl330_fifo;
+typedef struct pl330_queue_entry pl330_queue_entry;
+typedef struct pl330_queue pl330_queue;
+typedef struct pl330_state pl330_state;
+typedef struct pl330_insn_desc pl330_insn_desc;
+
+
+struct pl330_chan_state {
+    uint32_t src;
+    uint32_t dst;
+    uint32_t pc;
+    uint32_t control;
+    uint32_t status;
+    uint32_t lc[2];
+    uint32_t fault_type;
+
+    uint8_t ns;
+    uint8_t is_manager;
+    uint8_t request_flag;
+    uint8_t wakeup;
+    uint8_t wfp_sbp;
+
+    enum pl330_chan_enum state;
+    uint8_t stall;
+
+    pl330_state *parent;
+    uint8_t tag;
+    uint32_t watchdog_timer;
+};
+
+struct pl330_fifo {
+    uint8_t *buf;
+    uint8_t *tag;
+    int32_t s, t;
+    int32_t buf_size;
+};
+
+struct pl330_queue_entry {
+    uint32_t addr;
+    int32_t len;
+    int32_t n;
+    int32_t inc;
+    int32_t z;
+    uint8_t tag;
+    uint8_t seqn;
+};
+
+struct pl330_queue {
+    pl330_queue_entry *queue;
+    int32_t queue_size;
+    uint8_t *lo_seqn;
+    uint8_t *hi_seqn;
+};
+
+struct pl330_state {
+    SysBusDevice busdev;
+    uint32_t instance;
+
+    pl330_chan_state manager;
+    pl330_chan_state *chan;
+    pl330_fifo fifo;
+    pl330_queue read_queue;
+    pl330_queue write_queue;
+
+    /* Config registers. cfg[5] = CfgDn. */
+    uint32_t inten;
+    uint32_t int_status;
+    uint32_t ev_status;
+    uint32_t cfg[6];
+    uint32_t dbg[2];
+    uint8_t debug_status;
+
+    qemu_irq irq_abort;
+    qemu_irq *irq;
+
+    uint8_t num_faulting;
+
+    int32_t chan_num;
+    int32_t periph_num;
+    uint32_t event_num;
+
+    QEMUTimer *timer; /* is used for restore dma. */
+    int8_t periph_busy[PL330_PERIPH_NUM];
+};
+
+struct pl330_insn_desc {
+    /* OPCODE of the instruction */
+    uint8_t opcode;
+    /* Mask so we can select several sibling instructions, such as
+       DMALD, DMALDS and DMALDB */
+    uint8_t opmask;
+    /* Size of instruction in bytes */
+    uint8_t size;
+    /* Interpreter */
+    void (*exec)(pl330_chan_state *, uint8_t opcode, uint8_t *args, int len);
+};
+
+
+/* MFIFO Implementation */
+
+/*
+ * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are
+ * stored in this buffer. Data is stored in BUF field, tags - in the
+ * corresponding array elemnets of TAG field.
+ */
+
+/* Initialize queue. */
+static void pl330_fifo_init(pl330_fifo *s, uint32_t size)
+{
+    s->buf = qemu_mallocz(size * sizeof(uint8_t));
+    s->tag = qemu_mallocz(size * sizeof(uint8_t));
+    s->buf_size = size;
+}
+
+/* Cyclic increment */
+static inline int pl330_fifo_inc(int x, int size)
+{
+    return (x + 1) % size;
+}
+
+/* Number of empty bytes in MFIFO */
+static inline int pl330_fifo_num_free(pl330_fifo *s)
+{
+    if (s->t < s->s) {
+        return s->s - s->t;
+    } else {
+        return s->buf_size - s->t + s->s;
+    }
+}
+
+/* Number of bytes in MFIFO */
+static inline int pl330_fifo_num_used(pl330_fifo *s)
+{
+    if (s->t >= s->s) {
+        return s->t - s->s;
+    } else {
+        return s->buf_size - s->s + s->t;
+    }
+}
+
+/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG.
+   Zero returned on success, PL330_FIFO_STALL if there is no enough free
+   space in MFIFO to store requested amount of data. If push was unsaccessful
+   no data is stored to MFIFO. */
+static int pl330_fifo_push(pl330_fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+    int i;
+    int old_s, old_t;
+
+    old_s = s->s;
+    old_t = s->t;
+    for (i = 0; i < len; i++) {
+        if (pl330_fifo_inc(s->t, s->buf_size) != s->s) {
+            s->buf[s->t] = buf[i];
+            s->tag[s->t] = tag;
+            s->t = pl330_fifo_inc(s->t, s->buf_size);
+        } else {
+            /* Rollback transaction */
+            s->s = old_s;
+            s->t = old_t;
+            return PL330_FIFO_STALL;
+        }
+    }
+    return PL330_FIFO_OK;
+}
+
+/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each
+   byte is veryfied. Zero returned on success, PL330_FIFO_ERR on tag missmatch
+   and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was
+   unsaccessful no data is removed from MFIFO. */
+static int pl330_fifo_get(pl330_fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+    int i, ret;
+    int old_s, old_t;
+
+    old_s = s->s;
+    old_t = s->t;
+    for (i = 0; i < len; i++) {
+        if (s->t != s->s && s->tag[s->s] == tag) {
+            buf[i] = s->buf[s->s];
+            s->s = pl330_fifo_inc(s->s, s->buf_size);
+        } else {
+            /* Rollback transaction */
+            if (s->t == s->s)
+                ret = PL330_FIFO_STALL;
+            else
+                ret = PL330_FIFO_ERR;
+            s->s = old_s;
+            s->t = old_t;
+            return ret;
+        }
+    }
+    return PL330_FIFO_OK;
+}
+
+/* Reset MFIFO. This completely erases all data in it. */
+static inline void pl330_fifo_reset(pl330_fifo *s)
+{
+    s->s = 0;
+    s->t = 0;
+}
+
+/* Return tag of the first byte stored in MFIFO. If MFIFO is empty
+   PL330_UNTAGGED is returned. */
+static inline uint8_t pl330_fifo_tag(pl330_fifo *s)
+{
+    if (s->t == s->s)
+        return PL330_UNTAGGED;
+    return s->tag[s->s];
+}
+
+/* Returns non-zero if tag TAG is present in fifo or zero otherwise */
+static int pl330_fifo_has_tag(pl330_fifo *s, uint8_t tag)
+{
+    int i;
+
+    for (i = s->s; i != s->t; i = pl330_fifo_inc(i, s->buf_size)) {
+        if (s->tag[i] == tag)
+            return 1;
+    }
+    return 0;
+}
+
+/* Remove all entry tagged with TAG from MFIFO */
+static void pl330_fifo_tagged_remove(pl330_fifo *s, uint8_t tag)
+{
+    int i, t;
+
+    t = s->s;
+    for (i = s->s; i != s->t; i = pl330_fifo_inc(i, s->buf_size)) {
+        if (s->tag[i] != tag) {
+            s->buf[t] = s->buf[i];
+            s->tag[t] = s->tag[i];
+            t++;
+        }
+    }
+    s->t = t;
+}
+
+
+/* Read-Write Queue implementation */
+
+/*
+ * Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores).
+ * Each instructions is described by source (for loads) or destination (for
+ * stores) address ADDR, width of data to be loaded/stored LEN, number of
+ * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel
+ * this instruction belongs to. Queue does not store any information about
+ * nature of the instruction: is it load or store. PL330 has different queues
+ * for loads and stores so this is already known at the top level where it matters.
+ *
+ * Queue works as FIFO for instructions with equivalent tags, but can issue
+ * instructions with different tags in arbitrary order. SEQN field attached to
+ * each instruction helps to achieve this. For each TAG queue contains
+ * instructions with consecutive SEQN values ranged from LO_SEQN[TAG] to
+ * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is
+ * followed by SEQN=0.
+ *
+ * Z bit indicates that zeroes should be stored. Thus no MFIFO fetches
+ * are performed in this case.
+ */
+
+static void pl330_queue_reset(pl330_queue *s)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++)
+        s->queue[i].tag = PL330_UNTAGGED;
+}
+
+/* Initialize queue */
+static void pl330_queue_init(pl330_queue *s, int size, int channum)
+{
+    s->queue = (pl330_queue_entry *)
+        qemu_mallocz(size * sizeof(pl330_queue_entry));
+    s->lo_seqn = (uint8_t *)qemu_mallocz(channum * sizeof(uint8_t));
+    s->hi_seqn = (uint8_t *)qemu_mallocz(channum * sizeof(uint8_t));
+    s->queue_size = size;
+}
+
+/* Returns pointer to an empty slot or NULL if queue is full */
+static pl330_queue_entry *pl330_queue_find_empty(pl330_queue *s)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++)
+        if (s->queue[i].tag == PL330_UNTAGGED)
+            return &s->queue[i];
+    return NULL;
+}
+
+/* Puts instruction to queue.
+   Return value:
+     - zero - OK
+     - non-zero - queue is full */
+static int pl330_insn_to_queue(pl330_queue *s, uint32_t addr,
+                               int len, int n, int inc, int z, uint8_t tag)
+{
+    pl330_queue_entry *entry = pl330_queue_find_empty(s);
+
+    if (! entry)
+        return 1;
+    entry->tag = tag;
+    entry->addr = addr;
+    entry->len = len;
+    entry->n = n;
+    entry->z = z;
+    entry->inc = inc;
+    entry->seqn = s->hi_seqn[tag];
+    s->hi_seqn[tag]++;
+    return 0;
+}
+
+/* Returns a pointer to queue slot containing instruction which satisfies
+   following conditions:
+    - it has valid tag value (not PL330_UNTAGGED)
+    - it can be issued without violating queue logic (see above)
+    - if TAG argument is not PL330_UNTAGGED this instruction has tag value
+      equivalent to the argument TAG value.
+   If such instruction cannot be found NULL is returned. */
+static pl330_queue_entry *pl330_queue_find_insn(pl330_queue *s,
+                                                         uint8_t tag)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        if (s->queue[i].tag != PL330_UNTAGGED) {
+            if (s->queue[i].seqn == s->lo_seqn[s->queue[i].tag] &&
+                (s->queue[i].tag == tag ||
+                 tag == PL330_UNTAGGED ||
+                 s->queue[i].z))
+                return &s->queue[i];
+        }
+    }
+    return NULL;
+}
+
+/* Removes instruction from queue. */
+static inline void pl330_insn_from_queue(pl330_queue *s,
+                                         pl330_queue_entry *e)
+{
+    s->lo_seqn[e->tag]++;
+    e->tag = PL330_UNTAGGED;
+}
+
+/* Removes all instructions tagged with TAG from queue. */
+static inline void pl330_tag_from_queue(pl330_queue *s, uint8_t tag)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        if (s->queue[i].tag == tag)
+            s->queue[i].tag = PL330_UNTAGGED;
+    }
+}
+
+
+/* DMA instruction execution engine */
+
+/* Moves DMA channel to the FAULT state and updates it's status. */
+static inline void pl330_fault(pl330_chan_state *ch, uint32_t flags)
+{
+    ch->fault_type |= flags;
+    ch->state = pl330_chan_fault;
+    ch->parent->num_faulting++;
+    if (ch->parent->num_faulting == 1) {
+        qemu_irq_raise(ch->parent->irq_abort);
+    }
+}
+
+/*
+ * For information about instructions see PL330 Technical Reference Manual.
+ *
+ * Arguments:
+ *   CH - chanel executing the instruction
+ *   OPCODE - opcode
+ *   ARGS - array of 8-bit arguments
+ *   LEN - number of elements in ARGS array
+ */
+static void pl330_dmaaddh(pl330_chan_state *ch, uint8_t opcode,
+                          uint8_t *args, int len)
+{
+    uint16_t im = (((uint16_t)args[0]) << 8) | ((uint16_t)args[1]);
+    uint8_t ra = (opcode >> 1) & 1;
+
+    if (ra) {
+        ch->dst += im;
+    } else {
+        ch->src += im;
+    }
+}
+
+static void pl330_dmaend(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    if (ch->state == pl330_chan_executing && !ch->is_manager) {
+        /* Wait for all transfers to complete */
+        if (pl330_fifo_has_tag(&ch->parent->fifo, ch->tag) ||
+            pl330_queue_find_insn(&ch->parent->read_queue, ch->tag) != NULL ||
+            pl330_queue_find_insn(&ch->parent->write_queue, ch->tag) != NULL) {
+
+            ch->stall = 1;
+            return;
+        }
+    }
+    pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
+    pl330_tag_from_queue(&ch->parent->read_queue, ch->tag);
+    pl330_tag_from_queue(&ch->parent->write_queue, ch->tag);
+    ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmaflushp(pl330_chan_state *ch, uint8_t opcode,
+                            uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->periph_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+        return;
+    }
+    /* Do nothing */
+}
+
+static void pl330_dmago(pl330_chan_state *ch, uint8_t opcode,
+                        uint8_t *args, int len)
+{
+    uint8_t chan_id;
+    uint8_t ns;
+    uint32_t pc;
+    pl330_chan_state *s;
+
+    if (! ch->is_manager) {
+        /* TODO: what error actually is it? */
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    ns = (opcode >> 1) & 1;
+    chan_id = args[0] & 7;
+    if ((args[0] >> 3)) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (chan_id >= ch->parent->chan_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
+    if (ch->parent->chan[chan_id].state != pl330_chan_stopped) {
+        /* TODO: what error actually is it? */
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ns) {
+        pl330_fault(ch, PL330_FAULT_DMAGO_ER);
+        return;
+    }
+    s = &ch->parent->chan[chan_id];
+    s->ns = ns;
+    s->pc = pc;
+    s->state = pl330_chan_executing;
+}
+
+static void pl330_dmald(pl330_chan_state *ch, uint8_t opcode,
+                        uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint32_t size, num, inc;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    num = ((ch->control >> 4) & 0xf) + 1;
+    size = (uint32_t)1 << ((ch->control >> 1) & 0x7);
+    inc = ch->control & 1;
+    ch->stall = pl330_insn_to_queue(&ch->parent->read_queue, ch->src,
+                                    size, num, inc, 0, ch->tag);
+    if (inc) {
+        ch->src += size * num;
+    }
+}
+
+static void pl330_dmaldp(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->periph_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+        return;
+    }
+    pl330_dmald(ch, opcode, args, len);
+}
+
+static void pl330_dmalp(pl330_chan_state *ch, uint8_t opcode,
+                        uint8_t *args, int len)
+{
+    uint8_t lc = (opcode & 2) >> 1;
+
+    ch->lc[lc] = args[0];
+}
+
+static void pl330_dmalpend(pl330_chan_state *ch, uint8_t opcode,
+                           uint8_t *args, int len)
+{
+    uint8_t nf = (opcode & 0x10) >> 4;
+    uint8_t bs = opcode & 3;
+    uint8_t lc = (opcode & 4) >> 2;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    if (!nf || ch->lc[lc]) {
+        if (nf) {
+            ch->lc[lc]--;
+        }
+        ch->pc -= args[0];
+        ch->pc -= len + 1;
+        /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
+    }
+}
+
+static void pl330_dmakill(pl330_chan_state *ch, uint8_t opcode,
+                          uint8_t *args, int len)
+{
+    if (ch->state == pl330_chan_fault ||
+        ch->state == pl330_chan_fault_completing) {
+        /* This is the only way for chanel from faulting state */
+        ch->fault_type = 0;
+        ch->parent->num_faulting--;
+        if (ch->parent->num_faulting == 0) {
+            qemu_irq_lower(ch->parent->irq_abort);
+        }
+    }
+    ch->state = pl330_chan_killing;
+    pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
+    pl330_tag_from_queue(&ch->parent->read_queue, ch->tag);
+    pl330_tag_from_queue(&ch->parent->write_queue, ch->tag);
+    ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmamov(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t rd = args[0] & 7;
+    uint32_t im;
+
+    if ((args[0] >> 3)) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
+    switch (rd) {
+    case 0:
+        ch->src = im;
+        break;
+    case 1:
+        ch->control = im;
+        break;
+    case 2:
+        ch->dst = im;
+        break;
+    default:
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+}
+
+static void pl330_dmanop(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    /* NOP is NOP. */
+}
+
+static void pl330_dmarmb(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    /* Do nothing. Since we do not emulate AXI Bus transactions there is no
+       stalls. So we are allways on barrier. */
+}
+
+static void pl330_dmasev(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t ev_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    ev_id = (args[0] >> 3) & 0x1f;
+    if (ev_id >= ch->parent->event_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[3] & (1 << ev_id)) >> ev_id)) {
+        pl330_fault(ch, PL330_FAULT_EVENT_ER);
+        return;
+    }
+    if (ch->parent->inten & (1 << ev_id)) {
+        ch->parent->int_status |= (1 << ev_id);
+        qemu_irq_raise(ch->parent->irq[ev_id]);
+    } else {
+        ch->parent->ev_status |= (1 << ev_id);
+    }
+}
+
+static void pl330_dmast(pl330_chan_state *ch, uint8_t opcode,
+                        uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint32_t size, num, inc;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    num = ((ch->control >> 18) & 0xf) + 1;
+    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+    inc = (ch->control >> 14) & 1;
+    ch->stall = pl330_insn_to_queue(&ch->parent->write_queue, ch->dst,
+                                    size, num, inc, 0, ch->tag);
+    if (inc) {
+        ch->dst += size * num;
+    }
+}
+
+static void pl330_dmastp(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->periph_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+        return;
+    }
+    pl330_dmast(ch, opcode, args, len);
+}
+
+static void pl330_dmastz(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint32_t size, num, inc;
+
+    num = ((ch->control >> 18) & 0xf) + 1;
+    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+    inc = (ch->control >> 14) & 1;
+    ch->stall = pl330_insn_to_queue(&ch->parent->write_queue, ch->dst,
+                                    size, num, inc, 1, ch->tag);
+    if (inc) {
+        ch->dst += size * num;
+    }
+}
+
+static void pl330_dmawfe(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t ev_id;
+
+    if (args[0] & 5) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    ev_id = (args[0] >> 3) & 0x1f;
+    if (ev_id >= ch->parent->event_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[3] & (1 << ev_id)) >> ev_id)) {
+        pl330_fault(ch, PL330_FAULT_EVENT_ER);
+        return;
+    }
+    ch->wakeup = ev_id;
+}
+
+static void pl330_dmawfp(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->periph_num) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+        return;
+    }
+    switch (bs) {
+    case 0: /* S */
+        ch->request_flag = PL330_SINGLE;
+        ch->wfp_sbp = 0;
+        break;
+    case 1: /* P */
+        ch->request_flag = PL330_BURST;
+        ch->wfp_sbp = 2;
+        break;
+    case 2: /* B */
+        ch->request_flag = PL330_BURST;
+        ch->wfp_sbp = 1;
+        break;
+    default:
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+
+    if (ch->parent->periph_busy[periph_id]) {
+        ch->state = pl330_chan_waiting_periph;
+    } else if (ch->state == pl330_chan_waiting_periph) {
+        ch->state = pl330_chan_executing;
+    }
+}
+
+static void pl330_dmawmb(pl330_chan_state *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    /* Do nothing. Since we do not emulate AXI Bus transactions there is no
+       stalls. So we are allways on barrier. */
+}
+
+/* "NULL" terminated array of the instruction descriptions. */
+static const pl330_insn_desc insn_desc[] = {
+    { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh },
+    { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend },
+    { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp },
+    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago },
+    { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald },
+    { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp },
+    { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp },
+    /* dmastp  must be before dmalpend in tis map, because thay maps
+     * are overrided */
+    { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp },
+    { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend },
+    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill },
+    { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov },
+    { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop },
+    { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb },
+    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev },
+    { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast },
+    { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz },
+    { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe },
+    { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp },
+    { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb },
+    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL }
+};
+
+/* Instructions which can be issued via debug registers. */
+static const pl330_insn_desc debug_insn_desc[] = {
+    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago },
+    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill },
+    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev },
+    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL }
+};
+
+static inline const pl330_insn_desc *
+    pl330_fetch_insn(pl330_chan_state *ch)
+{
+    uint8_t opcode;
+    int i;
+
+    cpu_physical_memory_read(ch->pc, &opcode, 1);
+    for (i = 0; insn_desc[i].size; i++)
+        if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode)
+            return &insn_desc[i];
+    return NULL;
+}
+
+static inline void pl330_exec_insn(pl330_chan_state *ch,
+                                   const pl330_insn_desc *insn)
+{
+    uint8_t buf[PL330_INSN_MAXSIZE];
+
+    cpu_physical_memory_read(ch->pc, buf, insn->size);
+    insn->exec(ch, buf[0], &buf[1], insn->size - 1);
+}
+
+static inline void pl330_update_pc(pl330_chan_state *ch,
+                                   const pl330_insn_desc *insn)
+{
+    ch->pc += insn->size;
+}
+
+/* Try to execute current instruction in channel CH. Number of executed
+   instructions is returned (0 or 1). */
+static int pl330_chan_exec(pl330_chan_state *ch)
+{
+    const pl330_insn_desc *insn;
+
+    if (ch->state != pl330_chan_executing && ch->state != pl330_chan_waiting_periph) {
+        return 0;
+    }
+    ch->stall = 0;
+    insn = pl330_fetch_insn(ch);
+    if (! insn) {
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+        return 0;
+    }
+    pl330_exec_insn(ch, insn);
+    if (ch->state == pl330_chan_executing && !ch->stall) {
+        pl330_update_pc(ch, insn);
+        ch->watchdog_timer = 0;
+        return 1;
+    } else {
+        if (ch->stall) {
+            ch->watchdog_timer++;
+            if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) {
+                pl330_fault(ch, PL330_FAULT_LOCKUP_ER);
+            }
+        }
+    }
+    return 0;
+}
+
+/* Try to execute 1 instruction in each channel, one instruction from read
+   queue and one instruction from write queue. Number of successfully executed
+   instructions is returned. */
+static int pl330_exec_cycle(pl330_chan_state *channel)
+{
+    pl330_state *s = channel->parent;
+    pl330_queue_entry *q;
+    int i;
+    int num_exec = 0;
+    int fifo_res = 0;
+    uint8_t buf[PL330_MAX_BURST_LEN];
+
+    /* Execute one instruction in each channel */
+    num_exec += pl330_chan_exec(channel);
+
+    /* Execute one instruction from read queue */
+    q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED);
+    if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) {
+        cpu_physical_memory_read(q->addr, buf, q->len);
+        fifo_res = pl330_fifo_push(&s->fifo, buf, q->len, q->tag);
+        if (fifo_res == PL330_FIFO_OK) {
+            if (q->inc) {
+                q->addr += q->len;
+            }
+            q->n--;
+            if (! q->n) {
+                pl330_insn_from_queue(&s->read_queue, q);
+            }
+            num_exec++;
+        }
+    }
+
+    /* Execute one instruction from write queue. */
+    q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo));
+    if (q != NULL && (q->z || q->len <= pl330_fifo_num_used(&s->fifo))) {
+        if (q->z) {
+            for (i = 0; i < q->len; i++) {
+                buf[i] = 0;
+            }
+        } else {
+            fifo_res = pl330_fifo_get(&s->fifo, buf, q->len, q->tag);
+        }
+        if (fifo_res == PL330_FIFO_OK || q->z) {
+            cpu_physical_memory_write(q->addr, buf, q->len);
+            if (q->inc) {
+                q->addr += q->len;
+            }
+            q->n--;
+            if (! q->n) {
+                pl330_insn_from_queue(&s->write_queue, q);
+            }
+            num_exec++;
+        }
+    }
+
+    return num_exec;
+}
+
+static int pl330_exec_channel(pl330_chan_state *channel)
+{
+    int insr_exec = 0;
+
+    /* TODO: Is it all right to execute everything or should we do per-cycle
+       simulation? */
+    while (pl330_exec_cycle(channel)) {
+        insr_exec++;
+    }
+
+    /* Detect deadlock */
+    if (channel->state == pl330_chan_executing) {
+        pl330_fault(channel, PL330_FAULT_LOCKUP_ER);
+    }
+    /* Situation when one of the queues has deadlocked but all channels
+       has finished their programs should be impossible. */
+
+    return insr_exec;
+}
+
+
+static inline void pl330_exec(pl330_state *s)
+{
+    int i, insr_exec;
+    do {
+        insr_exec = pl330_exec_channel(&s->manager);
+
+        for (i = 0; i < s->chan_num; i++) {
+            insr_exec += pl330_exec_channel(&s->chan[i]);
+        }
+    } while (insr_exec);
+}
+
+static void pl330_exec_cycle_timer(void *opaque)
+{
+    struct pl330_state *s = (struct pl330_state *)opaque;
+    pl330_exec(s);
+}
+
+/* Stop or restore dma operations */
+static void pl330_dma_stop_irq(void *opaque, int irq, int level)
+{
+    struct pl330_state *s = (struct pl330_state *)opaque;
+
+    if (s->periph_busy[irq] != level) {
+        s->periph_busy[irq] = level;
+        qemu_mod_timer(s->timer, qemu_get_clock(vm_clock));
+    }
+}
+
+static void pl330_debug_exec(pl330_state *s)
+{
+    uint8_t args[5];
+    uint8_t opcode;
+    uint8_t chan_id;
+    int i;
+    pl330_chan_state *ch;
+    const pl330_insn_desc *insn;
+
+    s->debug_status = 1;
+    chan_id = (s->dbg[0] >>  8) & 0x07;
+    opcode  = (s->dbg[0] >> 16) & 0xff;
+    args[0] = (s->dbg[0] >> 24) & 0xff;
+    args[1] = (s->dbg[1] >>  0) & 0xff;
+    args[2] = (s->dbg[1] >>  8) & 0xff;
+    args[3] = (s->dbg[1] >> 16) & 0xff;
+    args[4] = (s->dbg[1] >> 24) & 0xff;
+    if (s->dbg[0] & 1) {
+        ch = &s->chan[chan_id];
+    } else {
+        ch = &s->manager;
+    }
+    insn = NULL;
+    for (i = 0; debug_insn_desc[i].size; i++)
+        if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode)
+            insn = &debug_insn_desc[i];
+    if (!insn) {
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+        return ;
+    }
+    ch->stall = 0;
+    insn->exec(ch, opcode, args, insn->size - 1);
+    if (ch->stall) {
+        hw_error("pl330: stall of debug instruction not implemented\n");
+    }
+    s->debug_status = 0;
+}
+
+
+/* IOMEM mapped registers */
+
+static void pl330_iomem_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    pl330_state *s = (pl330_state *) opaque;
+    uint32_t i;
+
+    if (offset & 3) {
+        hw_error("pl330: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+    switch (offset) {
+    case PL330_REG_INTEN:
+        s->inten = value;
+        break;
+    case PL330_REG_INTCLR:
+        for (i = 0; i < s->event_num; i++) {
+            if (s->int_status & s->inten & value & (1 << i)) {
+                qemu_irq_lower(s->irq[i]);
+            }
+        }
+        s->int_status &= ~value;
+        break;
+    case PL330_REG_DBGCMD:
+        if ((value & 3) == 0) {
+            pl330_debug_exec(s);
+            pl330_exec(s);
+        } else {
+            hw_error("pl330: write of illegal value %u for offset "
+                     TARGET_FMT_plx "\n", value, offset);
+        }
+        break;
+    case PL330_REG_DBGINST0:
+        s->dbg[0] = value;
+        break;
+    case PL330_REG_DBGINST1:
+        s->dbg[1] = value;
+        break;
+    default:
+        hw_error("pl330: bad write offset " TARGET_FMT_plx "\n", offset);
+        break;
+    }
+}
+
+static uint32_t pl330_iomem_read(void *opaque, target_phys_addr_t offset)
+{
+    pl330_state *s = (pl330_state *) opaque;
+    int chan_id;
+    int i;
+    uint32_t res;
+
+    if (offset & 3) {
+        hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    if (offset >= PL330_REG_ID && offset < PL330_REG_ID + 32) {
+        return pl330_id[(offset - PL330_REG_ID) >> 2];
+    }
+    if (offset >= PL330_REG_CONFIG && offset < PL330_REG_CONFIG + 24) {
+        return s->cfg[(offset - PL330_REG_CONFIG) >> 2];
+    }
+    if (offset >= PL330_REG_CHANCTRL && offset < 0xD00) {
+        offset -= PL330_REG_CHANCTRL;
+        chan_id = offset >> 5;
+        if (chan_id >= s->chan_num) {
+            hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+        }
+        switch (offset & 0x1f) {
+        case 0x00:
+            return s->chan[chan_id].src;
+        case 0x04:
+            return s->chan[chan_id].dst;
+        case 0x08:
+            return s->chan[chan_id].control;
+        case 0x0C:
+            return s->chan[chan_id].lc[0];
+        case 0x10:
+            return s->chan[chan_id].lc[1];
+        default:
+            hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+        }
+    }
+    if (offset >= PL330_REG_CS_BASE && offset < 0x400) {
+        offset -= PL330_REG_CS_BASE;
+        chan_id = offset >> 3;
+        if (chan_id >= s->chan_num) {
+            hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+        }
+        switch ((offset >> 2) & 1) {
+        case 0x0:
+            return (s->chan[chan_id].ns << 20) | (s->chan[chan_id].wakeup << 4) |
+                   (s->chan[chan_id].state & 0xf) |
+                   (s->chan[chan_id].wfp_sbp << 13);
+        case 0x1:
+            return s->chan[chan_id].pc;
+        default:
+            hw_error("pl330: read error\n");
+        }
+    }
+    if (offset >= PL330_REG_FTC_BASE && offset < 0x100) {
+        offset -= PL330_REG_FTC_BASE;
+        chan_id = offset >> 2;
+        if (chan_id >= s->chan_num) {
+            hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+        }
+        return s->chan[chan_id].fault_type;
+    }
+    switch (offset) {
+    case PL330_REG_DS:
+        return (s->manager.ns << 8) | (s->manager.wakeup << 4) |
+            (s->manager.state & 0xf);
+    case PL330_REG_DPC:
+        return s->manager.pc;
+    case PL330_REG_INTEN:
+        return s->inten;
+    case PL330_REG_ES:
+        return s->ev_status;
+    case PL330_REG_INTSTATUS:
+        return s->int_status;
+    case PL330_REG_INTCLR:
+        /* Documentation says that we can't read this register
+         * but linux kernel does it */
+        return 0;
+    case PL330_REG_FSM:
+        return s->manager.fault_type ? 1 : 0;
+    case PL330_REG_FSC:
+        res = 0;
+        for (i = 0; i < s->chan_num; i++) {
+            if (s->chan[i].state == pl330_chan_fault ||
+                s->chan[i].state == pl330_chan_fault_completing) {
+                res |= 1 << i;
+            }
+        }
+        return res;
+    case PL330_REG_FTM:
+        return s->manager.fault_type;
+    case PL330_REG_DBGSTATUS:
+        return s->debug_status;
+    default:
+        hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc * const pl330_readfn[] = {
+    pl330_iomem_read,
+    pl330_iomem_read,
+    pl330_iomem_read
+};
+
+static CPUWriteMemoryFunc * const pl330_writefn[] = {
+    pl330_iomem_write,
+    pl330_iomem_write,
+    pl330_iomem_write
+};
+
+static void pl330_save(QEMUFile *f, void *opaque)
+{
+    pl330_state *s = (pl330_state *) opaque;
+    int i;
+    /* manager */
+    qemu_put_be32s(f, &s->manager.src);
+    qemu_put_be32s(f, &s->manager.dst);
+    qemu_put_be32s(f, &s->manager.pc);
+    qemu_put_be32s(f, &s->manager.control);
+    qemu_put_be32s(f, &s->manager.status);
+    qemu_put_be32s(f, &s->manager.lc[0]);
+    qemu_put_be32s(f, &s->manager.lc[1]);
+    qemu_put_be32s(f, &s->manager.fault_type);
+    qemu_put_8s   (f, &s->manager.request_flag);
+    qemu_put_8s   (f, &s->manager.wakeup);
+    qemu_put_8s   (f, &s->manager.wfp_sbp);
+    qemu_put_byte (f, (uint8_t) s->manager.state);
+    qemu_put_8s   (f, &s->manager.stall);
+    qemu_put_be32s(f, &s->manager.watchdog_timer);
+    /* chan[chan_num] */
+    for (i = 0; i < s->chan_num; i++) {
+        qemu_put_be32s(f, &s->chan[i].src);
+        qemu_put_be32s(f, &s->chan[i].dst);
+        qemu_put_be32s(f, &s->chan[i].pc);
+        qemu_put_be32s(f, &s->chan[i].control);
+        qemu_put_be32s(f, &s->chan[i].status);
+        qemu_put_be32s(f, &s->chan[i].lc[0]);
+        qemu_put_be32s(f, &s->chan[i].lc[1]);
+        qemu_put_be32s(f, &s->chan[i].fault_type);
+        qemu_put_8s   (f, &s->chan[i].ns);
+        qemu_put_8s   (f, &s->chan[i].request_flag);
+        qemu_put_8s   (f, &s->chan[i].wakeup);
+        qemu_put_8s   (f, &s->chan[i].wfp_sbp);
+        qemu_put_byte (f, (uint8_t) s->chan[i].state);
+        qemu_put_8s   (f, &s->chan[i].stall);
+        qemu_put_be32s(f, &s->chan[i].watchdog_timer);
+    }
+    /* fifo */
+    qemu_put_buffer(f, s->fifo.buf, s->fifo.buf_size);
+    qemu_put_buffer(f, s->fifo.tag, s->fifo.buf_size);
+    qemu_put_sbe32s(f, &s->fifo.s);
+    qemu_put_sbe32s(f, &s->fifo.t);
+    /* read_queue */
+    for (i = 0; i < s->read_queue.queue_size; i++) {
+        qemu_put_be32s (f, &s->read_queue.queue->addr);
+        qemu_put_sbe32s(f, &s->read_queue.queue->len);
+        qemu_put_sbe32s(f, &s->read_queue.queue->n);
+        qemu_put_sbe32s(f, &s->read_queue.queue->inc);
+        qemu_put_sbe32s(f, &s->read_queue.queue->z);
+        qemu_put_8s    (f, &s->read_queue.queue->tag);
+        qemu_put_8s    (f, &s->read_queue.queue->seqn);
+    }
+    qemu_put_buffer(f, s->read_queue.lo_seqn, s->chan_num);
+    qemu_put_buffer(f, s->read_queue.hi_seqn, s->chan_num);
+    /* write_queue */
+    for (i = 0; i < s->write_queue.queue_size; i++) {
+        qemu_put_be32s (f, &s->write_queue.queue->addr);
+        qemu_put_sbe32s(f, &s->write_queue.queue->len);
+        qemu_put_sbe32s(f, &s->write_queue.queue->n);
+        qemu_put_sbe32s(f, &s->write_queue.queue->inc);
+        qemu_put_sbe32s(f, &s->write_queue.queue->z);
+        qemu_put_8s    (f, &s->write_queue.queue->tag);
+        qemu_put_8s    (f, &s->write_queue.queue->seqn);
+    }
+    qemu_put_buffer(f, s->write_queue.lo_seqn, s->chan_num);
+    qemu_put_buffer(f, s->write_queue.hi_seqn, s->chan_num);
+    /* config registers */
+    qemu_put_be32s(f, &s->inten);
+    qemu_put_be32s(f, &s->int_status);
+    qemu_put_be32s(f, &s->ev_status);
+    for (i = 0; i < 6; i++) {
+        qemu_put_be32s(f, s->cfg + i);
+        if (i > 1)
+            continue;
+        qemu_put_be32s(f, s->dbg + i);
+    }
+    qemu_put_8s     (f, &s->debug_status);
+    /* other */
+    qemu_put_8s     (f, &s->num_faulting);
+    qemu_put_sbuffer(f, s->periph_busy, PL330_PERIPH_NUM);
+    qemu_put_timer  (f, s->timer);
+}
+
+static int pl330_load(QEMUFile *f, void *opaque, int version_id)
+{
+    pl330_state *s = (pl330_state *) opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    /* manager */
+    qemu_get_be32s(f, &s->manager.src);
+    qemu_get_be32s(f, &s->manager.dst);
+    qemu_get_be32s(f, &s->manager.pc);
+    qemu_get_be32s(f, &s->manager.control);
+    qemu_get_be32s(f, &s->manager.status);
+    qemu_get_be32s(f, &s->manager.lc[0]);
+    qemu_get_be32s(f, &s->manager.lc[1]);
+    qemu_get_be32s(f, &s->manager.fault_type);
+    qemu_get_8s   (f, &s->manager.request_flag);
+    qemu_get_8s   (f, &s->manager.wakeup);
+    qemu_get_8s   (f, &s->manager.wfp_sbp);
+    s->manager.state = qemu_get_byte(f);
+    qemu_get_8s   (f, &s->manager.stall);
+    qemu_get_be32s(f, &s->manager.watchdog_timer);
+    /* chan[chan_num] */
+    for (i = 0; i < s->chan_num; i++) {
+        qemu_get_be32s(f, &s->chan[i].src);
+        qemu_get_be32s(f, &s->chan[i].dst);
+        qemu_get_be32s(f, &s->chan[i].pc);
+        qemu_get_be32s(f, &s->chan[i].control);
+        qemu_get_be32s(f, &s->chan[i].status);
+        qemu_get_be32s(f, &s->chan[i].lc[0]);
+        qemu_get_be32s(f, &s->chan[i].lc[1]);
+        qemu_get_be32s(f, &s->chan[i].fault_type);
+        qemu_get_8s   (f, &s->chan[i].ns);
+        qemu_get_8s   (f, &s->chan[i].request_flag);
+        qemu_get_8s   (f, &s->chan[i].wakeup);
+        qemu_get_8s   (f, &s->chan[i].wfp_sbp);
+        s->chan[i].state = qemu_get_byte(f);
+        qemu_get_8s   (f, &s->chan[i].stall);
+        qemu_get_be32s(f, &s->chan[i].watchdog_timer);
+    }
+    /* fifo */
+    qemu_get_buffer(f, s->fifo.buf, s->fifo.buf_size);
+    qemu_get_buffer(f, s->fifo.tag, s->fifo.buf_size);
+    qemu_get_sbe32s(f, &s->fifo.s);
+    qemu_get_sbe32s(f, &s->fifo.t);
+    /* read_queue */
+    for (i = 0; i < s->read_queue.queue_size; i++) {
+        qemu_get_be32s (f, &s->read_queue.queue->addr);
+        qemu_get_sbe32s(f, &s->read_queue.queue->len);
+        qemu_get_sbe32s(f, &s->read_queue.queue->n);
+        qemu_get_sbe32s(f, &s->read_queue.queue->inc);
+        qemu_get_sbe32s(f, &s->read_queue.queue->z);
+        qemu_get_8s    (f, &s->read_queue.queue->tag);
+        qemu_get_8s    (f, &s->read_queue.queue->seqn);
+    }
+    qemu_get_buffer(f, s->read_queue.lo_seqn, s->chan_num);
+    qemu_get_buffer(f, s->read_queue.hi_seqn, s->chan_num);
+    /* write_queue */
+    for (i = 0; i < s->write_queue.queue_size; i++) {
+        qemu_get_be32s (f, &s->write_queue.queue->addr);
+        qemu_get_sbe32s(f, &s->write_queue.queue->len);
+        qemu_get_sbe32s(f, &s->write_queue.queue->n);
+        qemu_get_sbe32s(f, &s->write_queue.queue->inc);
+        qemu_get_sbe32s(f, &s->write_queue.queue->z);
+        qemu_get_8s    (f, &s->write_queue.queue->tag);
+        qemu_get_8s    (f, &s->write_queue.queue->seqn);
+    }
+    qemu_get_buffer(f, s->write_queue.lo_seqn, s->chan_num);
+    qemu_get_buffer(f, s->write_queue.hi_seqn, s->chan_num);
+    /* config registers */
+    qemu_get_be32s(f, &s->inten);
+    qemu_get_be32s(f, &s->int_status);
+    qemu_get_be32s(f, &s->ev_status);
+    for (i = 0; i < 6; i++) {
+        qemu_get_be32s(f, &s->cfg[i]);
+        if (i > 1)
+            continue;
+        qemu_get_be32s(f, &s->dbg[i]);
+    }
+    qemu_get_8s     (f, &s->debug_status);
+    /* other */
+    qemu_get_8s     (f, &s->num_faulting);
+    qemu_get_sbuffer(f, s->periph_busy, PL330_PERIPH_NUM);
+    qemu_get_timer  (f, s->timer);
+
+    return 0;
+}
+
+/* Controller logic and initialization */
+
+static void pl330_chan_reset(pl330_chan_state *ch)
+{
+    ch->src = 0;
+    ch->dst = 0;
+    ch->pc = 0;
+    ch->state = pl330_chan_stopped;
+    ch->watchdog_timer = 0;
+    ch->stall = 0;
+    ch->control = 0;
+    ch->status = 0;
+    ch->fault_type = 0;
+}
+
+static void pl330_reset(DeviceState *d)
+{
+    int i;
+    pl330_state *s = FROM_SYSBUS(pl330_state, sysbus_from_qdev(d));
+
+    s->inten = 0;
+    s->int_status = 0;
+    s->ev_status = 0;
+    s->debug_status = 0;
+    s->num_faulting = 0;
+    pl330_fifo_reset(&s->fifo);
+    pl330_queue_reset(&s->read_queue);
+    pl330_queue_reset(&s->write_queue);
+
+    for (i = 0; i < s->chan_num; i++) {
+        pl330_chan_reset(&s->chan[i]);
+    }
+    for (i = 0; i < s->periph_num; i++) {
+        s->periph_busy[i] = 0;
+    }
+}
+
+/* PrimeCell PL330 Initialization. CFG is a 6-element array of 32-bit integers.
+   CFG[0] is Config Register 0, CFG[1] - Config Register 1, ..., CFG[4] -
+   Config Register 4 and CFG[5] - Config Register Dn. IRQS is an array of
+   interrupts. Required number of elements in this array is discribed by
+   bits [21:17] of CFG[0]. */
+DeviceState *pl330_init(target_phys_addr_t base,
+                        uint32_t instance, const uint32_t *cfg,
+                        qemu_irq *irqs, qemu_irq irq_abort)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    int i;
+
+    dev = qdev_create(NULL, "pl330");
+    qdev_prop_set_uint32(dev, "instance", instance);
+    qdev_prop_set_uint32(dev, "cfg0", cfg[0]);
+    qdev_prop_set_uint32(dev, "cfg1", cfg[1]);
+    qdev_prop_set_uint32(dev, "cfg2", cfg[2]);
+    qdev_prop_set_uint32(dev, "cfg3", cfg[3]);
+    qdev_prop_set_uint32(dev, "cfg4", cfg[4]);
+    qdev_prop_set_uint32(dev, "cfg5", cfg[5]);
+
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_connect_irq(s, 0, irq_abort);
+    for (i = 0; i < ((cfg[0] >> 17) & 0x1f) + 1; i++) {
+        sysbus_connect_irq(s, i + 1, irqs[i]);
+    }
+    sysbus_mmio_map(s, 0, base);
+
+    return dev;
+}
+
+static int pl330_init1(SysBusDevice *dev)
+{
+    int i;
+    int iomem;
+    pl330_state *s = FROM_SYSBUS(pl330_state, dev);
+
+    sysbus_init_irq(dev, &s->irq_abort);
+    iomem = cpu_register_io_memory(pl330_readfn, pl330_writefn, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, PL330_IOMEM_SIZE, iomem);
+    s->timer = qemu_new_timer(vm_clock, pl330_exec_cycle_timer, s);
+
+    s->chan_num = ((s->cfg[0] >> 4) & 7) + 1;
+    s->chan = qemu_mallocz(sizeof(pl330_chan_state) * s->chan_num);
+    for (i = 0; i < s->chan_num; i++) {
+        s->chan[i].parent = s;
+        s->chan[i].tag = (uint8_t)i;
+    }
+    s->manager.parent = s;
+    s->manager.tag = s->chan_num;
+    s->manager.ns = (s->cfg[0] >> 2) & 1;
+    s->manager.is_manager = 1;
+    if (s->cfg[0] & 1) {
+        s->periph_num = ((s->cfg[0] >> 12) & 0x1f) + 1;
+    } else {
+        s->periph_num = 0;
+    }
+    s->event_num = ((s->cfg[0] >> 17) & 0x1f) + 1;
+
+    s->irq = qemu_mallocz(sizeof(qemu_irq) * s->event_num);
+    for (i = 0; i < s->event_num; i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+
+    qdev_init_gpio_in(&dev->qdev, pl330_dma_stop_irq, PL330_PERIPH_NUM);
+
+    pl330_queue_init(&s->read_queue, ((s->cfg[5] >> 16) & 0xf) + 1, s->chan_num);
+    pl330_queue_init(&s->write_queue, ((s->cfg[5] >> 8) & 0xf) + 1, s->chan_num);
+    pl330_fifo_init(&s->fifo, ((s->cfg[5] >> 20) & 0x1ff) + 1);
+    pl330_reset(&s->busdev.qdev);
+
+    register_savevm(&dev->qdev, "pl330", s->instance, 1,
+                    pl330_save, pl330_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo pl330_info = {
+    .init = pl330_init1,
+    .qdev.name  = "pl330",
+    .qdev.size  = sizeof(pl330_state),
+    .qdev.reset = pl330_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("instance", pl330_state, instance, 0),
+        DEFINE_PROP_HEX32 ("cfg0", pl330_state, cfg[0], 0),
+        DEFINE_PROP_HEX32 ("cfg1", pl330_state, cfg[1], 0),
+        DEFINE_PROP_HEX32 ("cfg2", pl330_state, cfg[2], 0),
+        DEFINE_PROP_HEX32 ("cfg3", pl330_state, cfg[3], 0),
+        DEFINE_PROP_HEX32 ("cfg4", pl330_state, cfg[4], 0),
+        DEFINE_PROP_HEX32 ("cfg5", pl330_state, cfg[5], 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void pl330_register_devices(void)
+{
+    sysbus_register_withprop(&pl330_info);
+}
+
+device_init(pl330_register_devices)
index fb456ad..5e3c565 100644 (file)
@@ -8,6 +8,16 @@
 /* pl080.c */
 void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
 
+/* pl192.c */
+DeviceState *pl192_init(target_phys_addr_t base,
+                        int instance, ...);
+void pl192_chain(void *first, void *next);
+
+/* pl330.c */
+DeviceState *pl330_init(target_phys_addr_t base,
+                        uint32_t instance, const uint32_t *cfg,
+                        qemu_irq *irqs, qemu_irq irq_abort);
+
 /* arm_sysctl.c */
 void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id);
 
index 9ccf8cb..edc24dd 100644 (file)
--- a/hw/ps2.c
+++ b/hw/ps2.c
@@ -139,7 +139,7 @@ void ps2_queue(void *opaque, int b)
    bit 7    - 0 key pressed, 1 = key released
    bits 6-0 - translated scancode set 2
  */
-static void ps2_put_keycode(void *opaque, int keycode)
+static int ps2_put_keycode(void *opaque, int keycode)
 {
     PS2KbdState *s = opaque;
 
@@ -151,6 +151,7 @@ static void ps2_put_keycode(void *opaque, int keycode)
         keycode = ps2_raw_keycode[keycode & 0x7f];
       }
     ps2_queue(&s->common, keycode);
+    return 0;
 }
 
 uint32_t ps2_read_data(void *opaque)
@@ -596,7 +597,7 @@ void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
     s->common.update_arg = update_arg;
     s->scancode_set = 2;
     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
-    qemu_add_ps2kbd_event_handler(ps2_put_keycode, s);
+    qemu_add_kbd_event_handler(ps2_put_keycode, s, "QEMU PS/2 Keyboard");
     qemu_register_reset(ps2_kbd_reset, s);
     return s;
 }
index 4c99917..9d9eea0 100644 (file)
@@ -95,12 +95,12 @@ struct PXA2xxKeyPadState {
     uint32_t    kpkdi;
 };
 
-static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
+static int pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
 {
     int row, col,rel;
 
     if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
-        return;
+        return 0;
 
     if(kp->kpc & KPC_AS || kp->kpc & KPC_ASACT) {
         if(kp->kpc & KPC_AS)
@@ -111,7 +111,7 @@ static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
         row = kp->map[keycode].row;
         col = kp->map[keycode].column;
         if(row == -1 || col == -1)
-            return;
+            return 0;
         switch (col) {
         case 0:
         case 1:
@@ -144,14 +144,14 @@ static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
         } /* switch */
         goto out;
     }
-    return;
+    return 0;
 
 out:
     if(kp->kpc & KPC_MIE) {
         kp->kpc |= KPC_MI;
         qemu_irq_raise(kp->irq);
     }
-    return;
+    return 0;
 }
 
 static uint32_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset)
@@ -332,5 +332,6 @@ void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
     }
 
     kp->map = map;
-    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
+    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp,
+            "PXA keypad");
 }
index c7fec44..dbdb596 100644 (file)
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -210,6 +210,9 @@ DeviceState *qdev_device_add(QemuOpts *opts)
     if (!info || info->no_user) {
         qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "a driver name");
         error_printf_unless_qmp("Try with argument '?' for a list.\n");
+        if( info ) 
+          error_printf_unless_qmp("INFO OK.\n");
+
         return NULL;
     }
 
@@ -469,6 +472,22 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
     return NULL;
 }
 
+static int next_block_unit[IF_COUNT];
+
+/* Get a block device.  This should only be used for single-drive devices
+   (e.g. SD/Floppy/MTD).  Multi-disk devices (scsi/ide) should use the
+   appropriate bus.  */
+BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
+{
+    int unit = next_block_unit[type]++;
+    DriveInfo *dinfo;
+
+    dinfo = drive_get(type, 0, unit);
+    return dinfo ? dinfo->bdrv : NULL;
+}
+
+
+
 int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
                        qbus_walkerfn *busfn, void *opaque)
 {
index 9808f85..b57991c 100644 (file)
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -2,6 +2,7 @@
 #define QDEV_H
 
 #include "hw.h"
+#include "blockdev.h"
 #include "qemu-queue.h"
 #include "qemu-char.h"
 #include "qemu-option.h"
@@ -137,6 +138,7 @@ bool qdev_machine_modified(void);
 qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
 void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
 
+BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type);
 BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
 
 /*** Device API.  ***/
diff --git a/hw/qt602240_ts.c b/hw/qt602240_ts.c
new file mode 100644 (file)
index 0000000..8530d21
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * AT42QT602240 Touchscreen
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * NB: Only features used in the kernel driver is implemented currently.
+ */
+
+#include "console.h"
+#include "i2c-addressable.h"
+
+/* Object types */
+#define QT602240_DEBUG_DELTAS       2
+#define QT602240_DEBUG_REFERENCES   3
+#define QT602240_DEBUG_CTERANGE     26
+#define QT602240_GEN_MESSAGE        5
+#define QT602240_GEN_COMMAND        6
+#define QT602240_GEN_POWER          7
+#define QT602240_GEN_ACQUIRE        8
+#define QT602240_TOUCH_MULTI        9
+#define QT602240_TOUCH_KEYARRAY     15
+#define QT602240_PROCI_GRIPFACE     20
+#define QT602240_PROCG_NOISE        22
+#define QT602240_PROCI_ONETOUCH     24
+#define QT602240_PROCI_TWOTOUCH     27
+#define QT602240_SPT_GPIOPWM        19
+#define QT602240_SPT_SELFTEST       25
+#define QT602240_SPT_CTECONFIG      28
+
+/* Orient */
+#define QT602240_NORMAL             0x0
+#define QT602240_DIAGONAL           0x1
+#define QT602240_HORIZONTAL_FLIP    0x2
+#define QT602240_ROTATED_90_COUNTER 0x3
+#define QT602240_VERTICAL_FLIP      0x4
+#define QT602240_ROTATED_90         0x5
+#define QT602240_ROTATED_180        0x6
+#define QT602240_DIAGONAL_COUNTER   0x7
+
+/* Touch status */
+#define QT602240_SUPPRESS           (1 << 1)
+#define QT602240_AMP                (1 << 2)
+#define QT602240_VECTOR             (1 << 3)
+#define QT602240_MOVE               (1 << 4)
+#define QT602240_RELEASE            (1 << 5)
+#define QT602240_PRESS              (1 << 6)
+#define QT602240_DETECT             (1 << 7)
+
+/* Message */
+#define QT602240_REPORTID           0
+#define QT602240_MSG_STATUS         1
+#define QT602240_MSG_XPOSMSB        2
+#define QT602240_MSG_YPOSMSB        3
+#define QT602240_MSG_XYPOSLSB       4
+#define QT602240_MSG_TCHAREA        5
+#define QT602240_MSG_TCHAMPLITUDE   6
+#define QT602240_MSG_TCHVECTOR      7
+#define QT602240_CHECKSUM           8
+
+/* Message format */
+#define OBJECT_TABLE_MAX_SIZE       16
+
+#define OBJ_ADDR_TYPE               0
+#define OBJ_ADDR_START              1
+#define OBJ_ADDR_SIZE               3
+#define OBJ_ADDR_INSTANCES          4
+#define OBJ_ADDR_REPORT_IDS         5
+#define OBJ_SIZE                    6
+
+/* Size of message queue */
+#define QT602240_MAX_MESSAGE        10
+
+#define QEMUMAXX                    0x7FFF
+#define QEMUMAXY                    0x7FFF
+
+#define FAMILY_ID_SIZE              1
+#define VARIANT_ID_SIZE             1
+#define VERSION_SIZE                1
+#define BUILD_SIZE                  1
+#define MATRIX_X_SIZE_SIZE          1
+#define MATRIX_Y_SIZE_SIZE          1
+#define OBJECTS_NUM_SIZE            1
+#define OBJECT_TABLE_SIZE           (OBJECT_TABLE_MAX_SIZE * OBJ_SIZE)
+#define CHECKSUM_SIZE               1
+#define MESSAGE_SIZE                9
+#define MULTITOUCH_SIZE             30
+#define GENCOMMAND_SIZE             5
+#define SPT_CTECONFIG_SIZE          5
+
+#define FAMILY_ID                   0
+#define VARIANT_ID                  (FAMILY_ID + FAMILY_ID_SIZE)
+#define VERSION                     (VARIANT_ID + VARIANT_ID_SIZE)
+#define BUILD                       (VERSION + VERSION_SIZE)
+#define MATRIX_X_SIZE               (BUILD + BUILD_SIZE)
+#define MATRIX_Y_SIZE               (MATRIX_X_SIZE + MATRIX_X_SIZE_SIZE)
+#define OBJECTS_NUM                 (MATRIX_Y_SIZE + MATRIX_Y_SIZE_SIZE)
+#define OBJECT_TABLE                (OBJECTS_NUM + OBJECTS_NUM_SIZE)
+#define CHECKSUM                    (OBJECT_TABLE + OBJECT_TABLE_SIZE)
+#define MESSAGE                     (CHECKSUM + CHECKSUM_SIZE)
+#define MULTITOUCH                  (MESSAGE + MESSAGE_SIZE)
+#define GENCOMMAND                  (MULTITOUCH + MULTITOUCH_SIZE)
+#define SPT_CTECONFIG               (GENCOMMAND + GENCOMMAND_SIZE)
+#define TOTAL_SIZE                  (SPT_CTECONFIG + SPT_CTECONFIG_SIZE)
+
+#define MULTITOUCH_CTRL         0
+#define MULTITOUCH_ORIENT       9
+#define MULTITOUCH_XRANGE_LSB   18
+#define MULTITOUCH_XRANGE_MSB   19
+#define MULTITOUCH_YRANGE_LSB   20
+#define MULTITOUCH_YRANGE_MSB   21
+
+/* This structure closely correspond to the memory map of the real device.
+ * We use this property in read\write functions by directly reading\writing
+ * data at the offsets provided by the driver.  It is possible due to proper
+ * filling of ADDR_START field of each object with this object's structure
+ * offset in the next structure and byte-to-byte equivalence of each object
+ * structure to that of the real device.  */
+typedef struct QT602240State {
+
+    I2CAddressableState i2c_addressable;
+
+    uint8_t regs[TOTAL_SIZE];
+
+    /* Supported objects are MESSAGE, MULTITOUCH, SPT_CTECONFIG, GENCOMMAND.  */
+
+    int32_t prev_x, prev_y;
+    int32_t pressure;
+    qemu_irq irq;
+
+    /* Messages are stored in a cyclic buffer */
+    int32_t queue_start, queue_end;
+    uint8_t queue[QT602240_MAX_MESSAGE][MESSAGE_SIZE];
+
+    /* Boundary reported coordinates */
+    uint32_t minx, maxx, miny, maxy, orient;
+} QT602240State;
+
+typedef struct QT602240MultitouchMessage {
+    uint8_t status;
+    uint8_t xposmsb;
+    uint8_t yposmsb;
+    uint8_t xyposlsb;
+    uint8_t tcharea;
+    uint8_t tchamplitude;
+    uint8_t tchvector;
+} QT602240MultitouchMessage;
+
+
+/* Add one object to the table */
+static void qt602240_add_object(QT602240State *s, uint16_t offset,
+                                uint8_t size, int type, uint8_t report_ids)
+{
+    int i;
+
+    for (i = 0; i < OBJECT_TABLE_MAX_SIZE; i++) {
+        if (s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] == 0) {
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] = type;
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START] =
+                offset & 0xFF;
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START + 1] =
+                (offset >> 8) & 0xFF;
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_SIZE] = size - 1;
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_INSTANCES] = 0;
+
+            s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_REPORT_IDS] = report_ids;
+
+            s->regs[OBJECTS_NUM]++;
+            break;
+        }
+    }
+}
+
+/* Reset to default values */
+static void qt602240_reset(DeviceState *d)
+{
+    QT602240State *s =
+        FROM_I2CADDR_SLAVE(QT602240State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+    s->regs[FAMILY_ID] = 0x80;
+    s->regs[VARIANT_ID] = 0x00;
+    s->regs[VERSION] = 20;
+    s->regs[BUILD] = 0x00;
+    s->regs[MATRIX_X_SIZE] = 16;
+    s->regs[MATRIX_Y_SIZE] = 14;
+    s->regs[OBJECTS_NUM] = 0;
+
+    s->minx = 0;
+    s->maxx = 1;
+    s->miny = 0;
+    s->maxy = 1;
+
+    qt602240_add_object(s, MESSAGE, MESSAGE_SIZE,
+                        QT602240_GEN_MESSAGE, 0);
+    qt602240_add_object(s, MULTITOUCH, MULTITOUCH_SIZE,
+                        QT602240_TOUCH_MULTI, 10);
+    qt602240_add_object(s, SPT_CTECONFIG, SPT_CTECONFIG_SIZE,
+                        QT602240_SPT_CTECONFIG, 0);
+    qt602240_add_object(s, GENCOMMAND, GENCOMMAND_SIZE,
+                        QT602240_GEN_COMMAND, 0);
+
+    s->regs[MESSAGE + QT602240_REPORTID] = 0xFF;
+
+    s->queue_start = 0;
+    s->queue_end = 0;
+}
+
+#define OFFESETOF_MEM(s, mem) ((void *)(&(mem)) - (void *)(s))
+
+static uint8_t qt602240_read(void *opaque, uint32_t address, uint8_t offset)
+{
+    QT602240State *s = (QT602240State *)opaque;
+    uint8_t retval;
+    uint32_t reg = address + offset;
+
+    if (reg > TOTAL_SIZE) {
+        hw_error("qt602240: bad read offset 0x%x\n", reg);
+    }
+
+    retval = s->regs[reg];
+    if (reg >= MESSAGE + QT602240_REPORTID &&
+        reg <= MESSAGE + QT602240_CHECKSUM) {
+        /* Get message from the queue */
+        if (s->queue_start == s->queue_end) {
+            /* No messages */
+            return 0xFF;
+        }
+        retval = s->queue[s->queue_start][reg - MESSAGE];
+        /* Here is an assumption that message is read till the end */
+        if (reg == MESSAGE + QT602240_CHECKSUM) {
+            /* Move to the next message from the queue */
+            s->queue_start = (s->queue_start + 1) % QT602240_MAX_MESSAGE;
+            if (s->queue_start != s->queue_end) {
+                qemu_irq_raise(s->irq);  // GRS : # of interrupt & # of message does not match
+            }
+        }
+    }
+
+    if(reg == 127){
+        return 3;  // GRS : Temporary code. Unimplemented reg
+    }
+    return retval;
+}
+
+static void qt602240_write(void *opaque, uint32_t address, uint8_t offset,
+                           uint8_t val)
+{
+    QT602240State *s = (QT602240State *)opaque;
+    uint32_t reg = address + offset;
+
+    if (reg >= MULTITOUCH &&
+        reg < MULTITOUCH + MULTITOUCH_SIZE) {
+        s->regs[reg] = val;
+
+        if (reg == MULTITOUCH + MULTITOUCH_ORIENT) {
+            s->orient = s->regs[reg] & 1;
+        }
+
+        if (reg == MULTITOUCH + MULTITOUCH_XRANGE_LSB ||
+            reg == MULTITOUCH + MULTITOUCH_XRANGE_MSB) {
+            int res = s->regs[MULTITOUCH + MULTITOUCH_XRANGE_LSB] +
+                     (s->regs[MULTITOUCH + MULTITOUCH_XRANGE_MSB] << 8) + 1;
+
+            if (s->orient == 0) {
+                s->maxx = res;
+            } else {
+                s->maxy = res;
+            }
+        }
+
+        if (reg == MULTITOUCH + MULTITOUCH_YRANGE_LSB ||
+            reg == MULTITOUCH + MULTITOUCH_YRANGE_MSB) {
+            int res = s->regs[MULTITOUCH + MULTITOUCH_YRANGE_LSB] +
+                     (s->regs[MULTITOUCH + MULTITOUCH_YRANGE_MSB] << 8) + 1;
+
+            if (s->orient == 0) {
+                s->maxy = res;
+            } else {
+                s->maxx = res;
+            }
+        }
+
+    } else if (reg >= GENCOMMAND &&
+        reg < GENCOMMAND + GENCOMMAND_SIZE) {
+        s->regs[reg] = val;
+    } else if (reg >= SPT_CTECONFIG &&
+        reg < SPT_CTECONFIG + SPT_CTECONFIG_SIZE) {
+        s->regs[reg] = val;
+    } else {
+        hw_error("qt602240: bad write offset 0x%x\n", reg);
+    }
+}
+
+static int qt602240_enabled(QT602240State *s)
+{
+    /* Check for ENABLE and RPTEN bits */
+    return ((s->regs[MULTITOUCH + MULTITOUCH_CTRL] & 0x3) == 0x3);
+}
+
+/* Modify the message read by the driver */
+static void qt602240_msg(QT602240State *s, int x, int y, int status)
+{
+    /* Check if queue is full */
+    if ((s->queue_end + 1) % QT602240_MAX_MESSAGE == s->queue_start) {
+        if(status != QT602240_RELEASE){
+            return;  // GRS : Heuristic - Drag does not finish, by dropping release msg 
+        }
+    }
+
+    memset(s->queue[s->queue_end], 0, MESSAGE_SIZE);
+    s->queue[s->queue_end][QT602240_REPORTID] = 2;
+    s->queue[s->queue_end][QT602240_MSG_XPOSMSB] = x >> 2;
+    s->queue[s->queue_end][QT602240_MSG_YPOSMSB] = y >> 2;
+    s->queue[s->queue_end][QT602240_MSG_XYPOSLSB] =
+        ((x & 3) << 6) | ((y & 3) << 2);
+    s->queue[s->queue_end][QT602240_MSG_STATUS] = status;
+    s->queue[s->queue_end][QT602240_MSG_TCHAREA] = 1;
+
+    if ((s->queue_end + 1) % QT602240_MAX_MESSAGE != s->queue_start) {
+        s->queue_end = (s->queue_end + 1) % QT602240_MAX_MESSAGE;
+        // GRS : In case of queue full & release msg - drop the last msg instead of release
+    }
+
+}
+
+static void qt602240_ts_event(void *opaque,
+                              int x, int y, int z, int buttons_state)
+{
+    QT602240State *s = (QT602240State *)opaque;
+
+    if (!qt602240_enabled(s)) {
+        return;
+    }
+
+    /* Convert QEMU mouse coordinates to the touchscreen */
+    /* FIXME: should we use configuration data provided by the driver? */
+    y = (s->miny + y * (s->maxy - s->miny) / QEMUMAXY);
+    x = (s->minx + x * (s->maxx - s->minx) / QEMUMAXX);
+
+    if (s->pressure == !buttons_state) {
+        if (buttons_state) {
+            qt602240_msg(s, x, y, QT602240_PRESS | QT602240_DETECT);
+        } else {
+            qt602240_msg(s, x, y, QT602240_RELEASE);
+        }
+        qemu_irq_raise(s->irq);
+    } else if (s->pressure && (x != s->prev_x || y != s->prev_y)) {
+        static int drop_move = 1;
+        if(drop_move){
+            qt602240_msg(s, x, y, QT602240_MOVE | QT602240_DETECT);
+            qemu_irq_raise(s->irq);
+            drop_move = 0;
+        }else{
+            drop_move = 1;
+        }
+    }
+
+    s->pressure = !!buttons_state;
+    s->prev_x = x;
+    s->prev_y = y;
+}
+
+static void qt602240_save(QEMUFile *f, void *opaque)
+{
+    QT602240State *s = (QT602240State *)opaque;
+    int i;
+
+    qemu_put_buffer(f, s->regs, TOTAL_SIZE);
+    qemu_put_sbe32s(f, &s->prev_x);
+    qemu_put_sbe32s(f, &s->prev_y);
+    qemu_put_sbe32s(f, &s->pressure);
+    qemu_put_sbe32s(f, &s->queue_start);
+    qemu_put_sbe32s(f, &s->queue_end);
+
+    for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
+        qemu_put_buffer(f, s->queue[i], MESSAGE_SIZE);
+    }
+
+    qemu_put_be32s(f, &s->minx);
+    qemu_put_be32s(f, &s->miny);
+    qemu_put_be32s(f, &s->maxx);
+    qemu_put_be32s(f, &s->maxy);
+    qemu_put_be32s(f, &s->orient);
+}
+
+static int qt602240_load(QEMUFile *f, void *opaque, int version_id)
+{
+    QT602240State *s = (QT602240State *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_buffer(f, s->regs, TOTAL_SIZE);
+    qemu_get_sbe32s(f, &s->prev_x);
+    qemu_get_sbe32s(f, &s->prev_y);
+    qemu_get_sbe32s(f, &s->pressure);
+    qemu_get_sbe32s(f, &s->queue_start);
+    qemu_get_sbe32s(f, &s->queue_end);
+
+    for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
+        qemu_get_buffer(f, s->queue[i], MESSAGE_SIZE);
+    }
+
+    qemu_get_be32s(f, &s->minx);
+    qemu_get_be32s(f, &s->miny);
+    qemu_get_be32s(f, &s->maxx);
+    qemu_get_be32s(f, &s->maxy);
+    qemu_get_be32s(f, &s->orient);
+
+    return 0;
+}
+
+static int qt602240_init(I2CAddressableState *s)
+{
+    QT602240State *t = FROM_I2CADDR_SLAVE(QT602240State, s);
+
+    qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+
+    qemu_add_mouse_event_handler(qt602240_ts_event, t, 1,
+                                 "AT42QT602240 Touchscreen");
+    qt602240_reset(&s->i2c.qdev);
+
+    register_savevm(&s->i2c.qdev, "qt602240", -1, 1,
+                    qt602240_save, qt602240_load, s);
+
+    return 0;
+}
+
+static I2CAddressableDeviceInfo qt602240_info = {
+    .i2c.qdev.name  = "qt602240",
+    .i2c.qdev.size  = sizeof(QT602240State),
+    .i2c.qdev.reset = qt602240_reset,
+    .init  = qt602240_init,
+    .read  = qt602240_read,
+    .write = qt602240_write,
+    .size  = 2,
+    .rev   = 0
+};
+
+static void qt602240_register_devices(void)
+{
+    i2c_addressable_register_device(&qt602240_info);
+}
+
+device_init(qt602240_register_devices)
diff --git a/hw/s5pc1xx.c b/hw/s5pc1xx.c
new file mode 100644 (file)
index 0000000..9c1a5a5
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ * Samsung S5PC1XX-based board emulation.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ *                Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *                Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+
+/* turn off some memory-hungry but useless (yet) devices */
+#define LOWMEMORY
+
+
+#include "s5pc1xx.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "primecell.h"
+#include "qemu-timer.h"
+#include "net.h"
+#include "i2c.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysemu.h"
+#include "ide.h"
+#include "ak8973.h"
+
+
+#ifdef CONFIG_GLES2
+#include "gles2.h"
+#endif
+
+
+/* Memory map */
+#define S5PC1XX_BOOT_BASE        0x00000000
+#define S5PC1XX_DRAM0_BASE       0x20000000
+#define S5PC1XX_ONEDRAM_BASE     0x30000000
+#define S5PC1XX_DRAM1_BASE       0x40000000
+#define S5PC1XX_SROMC_B0_BASE    0x80000000
+#define S5PC1XX_SROMC_B1_BASE    0x88000000
+#define S5PC1XX_SROMC_B2_BASE    0x90000000
+#define S5PC1XX_SROMC_B3_BASE    0x98000000
+#define S5PC1XX_SROMC_B4_BASE    0xA0000000
+#define S5PC1XX_SROMC_B5_BASE    0xA8000000
+#define S5PC1XX_ONENAND_BASE     0xB0000000
+#define S5PC1XX_NAND_BASE        0xB0E00000
+#define S5PC1XX_MP3_SRAM_BASE    0xC0000000
+#define S5PC1XX_IROM_BASE        0xD0000000 /* Internal SROM */
+#define S5PC1XX_IRAM_BASE        0xD0020000 /* Internal SRAM */
+#define S5PC1XX_DMZ_ROM          0xD8000000
+#define S5PC1XX_SFRS             0xE0000000
+
+#define S5PC1XX_JPEG_BASE        0xFB600000
+#define S5PC1XX_USB_OTG_BASE     0xEC000000
+
+/* Memory size */
+#define S5PC1XX_BOOT_SIZE        (1 << 29)  /* 512 MB */
+#define S5PC1XX_ISROM_SIZE       (1 << 16)  /* 64 KB  */
+#define S5PC1XX_ISRAM_SIZE       (1 << 17)  /* 128 KB */
+#define S5PC1XX_DRAM_MAX_SIZE    (1 << 29)  /* 512 MB */
+
+#ifndef LOWMEMORY
+#define S5PC1XX_SFRS_SIZE        (1 << 29)  /* 512 MB */
+#else
+#define S5PC1XX_SFRS_SIZE        (1 << 27)  /* 128 MB */
+#endif
+
+/* Interrputs Handling */
+
+/* Number of interrupts */
+#define S5PC1XX_IRQ_COUNT        128
+/* Number of Vector Interrupt Controllers */
+#define S5PC1XX_VIC_N            4
+/* Number of vectors in each Vector Interrupt Controller */
+#define S5PC1XX_VIC_SIZE         32
+#define S5PC1XX_VIC_BASE         0xF2000000
+#define S5PC1XX_VIC_SHIFT        0x00100000
+
+#define S5PC1XX_LCD_BASE         0xF8000000
+#define S5PC1XX_AC97_BASE        0xE2200000
+
+#define S5PC1XX_SPI_BASE         0xE1300000
+#define S5PC1XX_SPI_SHIFT        0x00100000
+
+#define S5PC1XX_IRQ_EXTEND       16
+#define S5PC1XX_IRQ_DMAMEM       18
+#define S5PC1XX_IRQ_DMA0         19
+#define S5PC1XX_IRQ_DMA1         20
+#define S5PC1XX_IRQ_TIMER0       21
+#define S5PC1XX_IRQ_TIMER1       22
+#define S5PC1XX_IRQ_TIMER2       23
+#define S5PC1XX_IRQ_TIMER3       24
+#define S5PC1XX_IRQ_TIMER4       25
+#define S5PC1XX_IRQ_SYS_TIMER    26
+#define S5PC1XX_IRQ_WDT          27
+#define S5PC1XX_IRQ_RTC_ALARM    28
+#define S5PC1XX_IRQ_RTC_TICK     29
+#define S5PC1XX_IRQ_GPIOINT      30
+
+#define S5PC1XX_IRQ_CFCON        41
+#define S5PC1XX_IRQ_UART0        42
+#define S5PC1XX_IRQ_UART1        43
+#define S5PC1XX_IRQ_UART2        44
+#define S5PC1XX_IRQ_UART3        45
+#define S5PC1XX_IRQ_SPI0         47
+#define S5PC1XX_IRQ_SPI1         48
+#define S5PC1XX_IRQ_SPI2         49
+
+#define S5PC1XX_IRQ_UHOST        55
+#define S5PC1XX_IRQ_OTG          56
+
+#define S5PC1XX_IRQ_MMC0         58
+#define S5PC1XX_IRQ_MMC1         59
+#define S5PC1XX_IRQ_MMC2         60
+#define S5PC1XX_IRQ_MMC3         98
+
+#define S5PC1XX_IRQ_I2C_0        46
+#define S5PC1XX_IRQ_I2C_2        51
+#define S5PC1XX_IRQ_I2C_PHY      52
+#define S5PC1XX_IRQ_I2C_DDC      77
+
+#define S5PC1XX_IRQ_JPEG         72
+
+#define S5PC1XX_IRQ_I2S0         80
+#define S5PC1XX_IRQ_I2S1         81
+#define S5PC1XX_IRQ_I2S_V5       S5PC1XX_IRQ_I2S0
+
+#define S5PC1XX_IRQ_AC97         83
+#define S5PC1XX_IRQ_PCM0         84
+#define S5PC1XX_IRQ_PCM1         85
+#define S5PC1XX_IRQ_SPDIF        86
+#define S5PC1XX_IRQ_ADC0         87
+#define S5PC1XX_IRQ_PENDN0       88
+#define S5PC1XX_IRQ_KEYPAD       89
+#define S5PC1XX_IRQ_PCM2         93
+
+#define S5PC1XX_IRQ_LCD0         64
+#define S5PC1XX_IRQ_LCD1         65
+#define S5PC1XX_IRQ_LCD2         66
+
+#define S5PC1XX_IRQ_ADC1         105
+#define S5PC1XX_IRQ_PENDN1       106
+
+#define S5PC1XX_DMAMEM_BASE      0xFA200000
+#define S5PC1XX_DMA0_BASE        0xE0900000
+#define S5PC1XX_DMA1_BASE        0xE0A00000
+
+#define S5PC1XX_GPIO_BASE        0xE0200000
+#define S5PC1XX_PWM_BASE         0xE2500000
+#define S5PC1XX_ST_BASE          0xE2600000
+#define S5PC1XX_WDT_BASE         0xE2700000
+#define S5PC1XX_RTC_BASE         0xE2800000
+
+#define S5PC1XX_UART_BASE        0xE2900000
+#define S5PC1XX_UART_SHIFT       0x00000400
+
+#define S5PC1XX_PCM0_BASE        0xE2300000
+#define S5PC1XX_PCM1_BASE        0xE1200000
+#define S5PC1XX_PCM2_BASE        0xE2B00000
+
+#define S5PC1XX_SPDIF_BASE       0xE1100000
+
+#define S5PC1XX_SROMC_BASE       0xE8000000
+
+#define S5PC1XX_I2S1_BASE        0xE2100000
+#define S5PC1XX_I2S2_BASE        0xE2A00000
+
+#define S5PC1XX_I2C_0_BASE       0xE1800000
+#define S5PC1XX_I2C_2_BASE       0xE1A00000
+#define S5PC1XX_I2C_PHY_BASE     0xFA900000
+#define S5PC1XX_I2C_DDC_BASE     0xFAB00000
+
+#define S5PC1XX_USB_EHCI_BASE    0xEC200000
+#define S5PC1XX_USB_OHCI_BASE    0xEC300000
+
+#define S5PC1XX_SROMC_MAX_BANK_SIZE  0x08000000
+
+#define S5PC1XX_SWRST_BASE       0xE0102000
+#define S5PC1XX_PMU_BASE         0xE0108000
+#define S5PC1XX_CMU_BASE         0xE0100000
+
+#define S5PC1XX_KEYPAD_BASE      0xE1600000
+#define S5PC1XX_TSADC0_BASE      0xE1700000
+#define S5PC1XX_TSADC1_BASE      0xE1701000
+
+#define S5PC1XX_I2S2_V5_BASE     0xEEE30000
+
+#define S5PC1XX_CFCON_ATAPI1     0xE8200054
+#define S5PC1XX_CFCON_ATAPI2     0xE8200074
+
+#define S5PC1XX_HSMMC0_BASE      0xEB000000
+#define S5PC1XX_HSMMC1_BASE      0xEB100000
+#define S5PC1XX_HSMMC2_BASE      0xEB200000
+#define S5PC1XX_HSMMC3_BASE      0xEB300000
+
+#define S5PC1XX_IRQ_ONEDRAM_INT_AP      11
+
+#define S5PC1XX_USB_NUM_PORTS    1
+#define S5PC1XX_SROMC_NUM_BANKS  6
+
+#define S5PC1XX_QT602240_ADDR    0x4A
+#define S5PC1XX_QT602240_IRQ     GPIOINT_IRQ(GPJ0, 5)
+
+#define S5PC1XX_WM8994_ADDR      0x1A
+
+#define S5PC1XX_MCS5000_ADDR     0x20
+#define S5PC1XX_MCS5000_IRQ      GPIOINT_IRQ(GPJ2, 7)
+
+#define S5PC1XX_MAX17040_ADDR    0x36
+#define S5PC1XX_MAX8998_ADDR     0x66
+#define S5PC1XX_MAX8998_RTC_ADDR 0x06
+
+#define S5PC1XX_AK8973_ADDR      0x1C
+#define S5PC1XX_AK8973_IRQ       GPIOEXT_IRQ(29)
+
+/* pl330 peripheral numbers */
+#define PL330_PERIPH_NUM_I2S1    10
+#define PL330_PERIPH_NUM_I2S2    11
+
+
+static const uint32_t dmamem_cfg[] =
+{ 0x003E1071, 0x00000075, 0x0, 0xFFFFFFFF, 0x00000003, 0x01F73733 };
+
+static const uint32_t dma0_cfg[] =
+{ 0x003FF075, 0x00000074, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00773732 };
+
+static const uint32_t dma1_cfg[] =
+{ 0x003FF075, 0x00000074, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00773732 };
+
+static const uint8_t chipid_and_omr[] =
+{ 0x00, 0x02, 0x11, 0x43, 0x09, 0x00, 0x00, 0x00 }; /* Little-endian */
+
+/* FIXME: this should be considered a hack, but is there any other way?
+          now at least this may be easily changed if another compass device
+          is used */
+static DeviceState *compass_device = NULL;
+static DeviceState *sensor_device = NULL;
+
+
+int compass_update(int8_t x, int8_t y, int8_t z, int8_t temp)
+{
+    if (compass_device) {
+        ak8973_update(compass_device, x, y, z, temp);
+    }
+    return 0;
+}
+
+#if 0
+/* smb380 module has this function */
+int sensor_update(int16_t x, int16_t y, int16_t z)
+{
+    if (sensor_device) {
+        /* TODO: implement proper emulation for SMB380 */
+        /* smb380_update(sensor_device, x, y, z); */
+    }
+    return 0;
+}
+#endif
+
+/* Find IRQ by it's number */
+static inline qemu_irq s5pc1xx_get_irq(struct s5pc1xx_state_s *s, int n)
+{
+    return s->irq[n / S5PC1XX_VIC_SIZE][n % S5PC1XX_VIC_SIZE];
+}
+
+//#include "s5pc1xx_debug.c"
+
+struct i2c
+{
+    i2c_bus *intrf0;
+    i2c_bus *intrf2;
+    i2c_bus *intrfDDC;
+    i2c_bus *intrfPHY;
+
+    i2c_bus *gpio3;
+    i2c_bus *gpio4;
+    i2c_bus *gpio5;
+    i2c_bus *gpio10;
+};
+
+/* Helper structure and two functions. Allow you to connect several irq
+   sources to one irq input using logical OR. */
+struct irq_multiplexer_s {
+    qemu_irq parent;
+    int count;
+    uint8_t mask[];
+};
+
+static void irq_mult_handler(void *opaque, int irq, int level)
+{
+    uint8_t old_level;
+    struct irq_multiplexer_s *s = (struct irq_multiplexer_s *)opaque;
+
+    old_level = (s->mask[irq >> 3] >> (irq & 7)) & 1;
+    if (!old_level && level) {
+        s->count++;
+        s->mask[irq >> 3] |= (1 << (irq & 7));
+        if (s->count == 1) {
+            qemu_irq_raise(s->parent);
+        }
+    }
+    if (old_level && !level) {
+        s->count--;
+        s->mask[irq >> 3] &= ~(1 << (irq & 7));
+        if (s->count == 0) {
+            qemu_irq_lower(s->parent);
+        }
+    }
+}
+
+static qemu_irq *irq_multiplexer(qemu_irq irq, int n)
+{
+    struct irq_multiplexer_s *s =
+        qemu_mallocz(sizeof(struct irq_multiplexer_s) +
+                ((n + 7) << 3) * sizeof(uint8_t));
+    s->parent = irq;
+    return qemu_allocate_irqs(irq_mult_handler, s, n);
+}
+
+static void s5pc110_rst_mm_write(void *opaque, target_phys_addr_t offset,
+                                 uint32_t val)
+{
+    if (val & 1) {
+        qemu_system_reset_request();
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc110_rst_readfn[] = {
+    NULL,
+    NULL,
+    NULL
+};
+
+static CPUWriteMemoryFunc * const s5pc110_rst_writefn[] = {
+    s5pc110_rst_mm_write,
+    s5pc110_rst_mm_write,
+    s5pc110_rst_mm_write
+};
+
+static struct arm_boot_info s5pc110x_binfo = {
+    .loader_start = 0x0,
+    //.board_id = 0xA74, /* Aquila */
+    .board_id = 0xb2e, /* Aquila */
+    .revision = 0x803, /* Aquila LiMo Universal */
+};
+
+static void s5pc110_reset(void *opaque)
+{
+    struct s5pc1xx_state_s *s = (struct s5pc1xx_state_s *)opaque;
+
+    s->env->regs[15] = 0x30000000;
+}
+
+/* Initialize and start system */
+static void s5pc110_init(ram_addr_t ram_size, const char *boot_device,
+                         const char *kernel_fname, const char *kernel_cmdline,
+                         const char *initrd_name, const char *cpu_model)
+{
+    ram_addr_t sdram_off, isrom_off, isram_off, chipid_off;
+    qemu_irq *cpu_irq;
+    DeviceState *dev, *dev_prev, *gpio, *dmamem, *dma0, *dma1;
+    CharDriverState *chr2, *chr3;
+    qemu_irq *dma_irqs, dma_stop_irq1, dma_stop_irq2;
+    struct i2c *i2c = (struct i2c *)qemu_mallocz(sizeof(*i2c));
+    struct s5pc1xx_state_s *s =
+        (struct s5pc1xx_state_s *)qemu_mallocz(sizeof(*s));
+    int i, j, iomemtype;
+    DriveInfo *dinfo;
+
+    /* Core */
+    s->mpu_model = s5pc110;
+    s->env = cpu_init("cortex-a8");
+    if (!s->env)
+        hw_error("Unable to find CPU definition (%s)\n", "cortex-a8");
+
+    /* Chip-ID and OMR */
+    chipid_off = qemu_ram_alloc(NULL, "s5pc100.chipid", sizeof(chipid_and_omr));
+    cpu_register_physical_memory(S5PC1XX_SFRS, sizeof(chipid_and_omr),
+                                 chipid_off | IO_MEM_ROM);
+    cpu_physical_memory_write_rom(S5PC1XX_SFRS, chipid_and_omr,
+                                  sizeof(chipid_and_omr));
+
+    /* Software reset */
+    iomemtype =
+        cpu_register_io_memory(s5pc110_rst_readfn, s5pc110_rst_writefn, NULL, DEVICE_NATIVE_ENDIAN);
+    cpu_register_physical_memory(S5PC1XX_SWRST_BASE, 0x4, iomemtype);
+
+    /* Memory */
+    /* Main RAM */
+    if (ram_size > S5PC1XX_DRAM_MAX_SIZE) {
+        hw_error("Too much memory was requested for this board "
+                 "(requested: %lu MB, max: %u MB)\n",
+                 ram_size / (1024 * 1024),
+                 S5PC1XX_DRAM_MAX_SIZE / (1024 * 1024));
+    }
+    s->sdram_size = ram_size;
+    sdram_off = qemu_ram_alloc(NULL, "s5pc100.ram", ram_size);
+    cpu_register_physical_memory(S5PC1XX_DRAM1_BASE, ram_size,
+                                 sdram_off | IO_MEM_RAM);
+    /* Also map the same memory to boot area.  */
+    cpu_register_physical_memory(S5PC1XX_BOOT_BASE, ram_size,
+                                 sdram_off | IO_MEM_RAM);
+
+    /* Internal SROM */
+    s->isrom_size = S5PC1XX_ISROM_SIZE;
+    isrom_off = qemu_ram_alloc(NULL, "s5pc100.srom", s->isrom_size);
+    cpu_register_physical_memory(S5PC1XX_IROM_BASE, s->isrom_size,
+                                 isrom_off | IO_MEM_ROM);
+
+    /* Internal SRAM */
+    s->isram_size = S5PC1XX_ISRAM_SIZE;
+    isram_off = qemu_ram_alloc(NULL, "s5pc100.sram", s->isram_size);
+    cpu_register_physical_memory(S5PC1XX_IRAM_BASE, s->isram_size,
+                                 isram_off | IO_MEM_RAM);
+
+#ifndef LOWMEMORY
+    /* SROMC banks */
+    ram_addr_t srom_bank_off = qemu_ram_alloc(NULL, "s5pc100.srombanks",
+                                              S5PC1XX_SROMC_MAX_BANK_SIZE);
+    cpu_register_physical_memory(S5PC1XX_SROMC_B0_BASE,
+                         S5PC1XX_SROMC_NUM_BANKS * S5PC1XX_SROMC_MAX_BANK_SIZE,
+                         srom_bank_off | IO_MEM_ROM);
+#endif
+
+    /* System devices */
+    /* Interrupts */
+    cpu_irq = arm_pic_init_cpu(s->env);
+    s->irq =
+        qemu_mallocz(S5PC1XX_VIC_N * sizeof(qemu_irq *));
+    dev = pl192_init(S5PC1XX_VIC_BASE, 0,
+                     cpu_irq[ARM_PIC_CPU_IRQ],
+                     cpu_irq[ARM_PIC_CPU_FIQ], NULL);
+    s->irq[0] = qemu_mallocz(S5PC1XX_VIC_SIZE * sizeof(qemu_irq));
+    for (i = 0; i < S5PC1XX_VIC_SIZE; i++)
+        s->irq[0][i] = qdev_get_gpio_in(dev, i);
+    for (j = 1; j < S5PC1XX_VIC_N; j++) {
+        dev_prev = dev;
+        dev =
+            pl192_init(S5PC1XX_VIC_BASE + S5PC1XX_VIC_SHIFT * j, j, NULL);
+
+        s->irq[j] = qemu_mallocz(S5PC1XX_VIC_SIZE * sizeof(qemu_irq));
+        for (i = 0; i < S5PC1XX_VIC_SIZE; i++)
+            s->irq[j][i] = qdev_get_gpio_in(dev, i);
+        pl192_chain(sysbus_from_qdev(dev_prev), sysbus_from_qdev(dev));
+    }
+
+    /* GPIO */
+    gpio = sysbus_create_varargs("s5pc1xx.gpio", S5PC1XX_GPIO_BASE,
+                                 s5pc1xx_get_irq(s, S5PC1XX_IRQ_GPIOINT),
+                                 s5pc1xx_get_irq(s, S5PC1XX_IRQ_EXTEND),
+                                 NULL);
+
+    /* DMA Controller */
+    dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMAMEM),
+                               ((dmamem_cfg[0] >> 17) & 0x1f) + 2);
+    dmamem =
+        pl330_init(S5PC1XX_DMAMEM_BASE, 0, dmamem_cfg, dma_irqs + 1, *dma_irqs);
+    dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMA0),
+                               ((dma0_cfg[0] >> 17) & 0x1f) + 2);
+    dma0 = pl330_init(S5PC1XX_DMA0_BASE, 1, dma0_cfg, dma_irqs + 1, *dma_irqs);
+    dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMA1),
+                               ((dma1_cfg[0] >> 17) & 0x1f) + 2);
+    dma1 = pl330_init(S5PC1XX_DMA1_BASE, 2, dma1_cfg, dma_irqs + 1, *dma_irqs);
+
+    /* I2C */
+    dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_0_BASE,
+                               s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_0));
+    i2c->intrf0 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_2_BASE,
+                               s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_2));
+    i2c->intrf2 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_PHY_BASE,
+                               s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_PHY));
+    i2c->intrfPHY = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_DDC_BASE,
+                               s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_DDC));
+    i2c->intrfDDC = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+
+    /* GPIO I2C */
+    dev = s5pc1xx_i2c_gpio_init(3);
+    i2c->gpio3 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = s5pc1xx_i2c_gpio_init(4);
+    i2c->gpio4 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = s5pc1xx_i2c_gpio_init(5);
+    i2c->gpio5 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+    dev = s5pc1xx_i2c_gpio_init(10);
+    i2c->gpio10 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+
+    /* SPI */
+    sysbus_create_simple("s5pc1xx.spi",
+                         S5PC1XX_SPI_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI0));
+    sysbus_create_simple("s5pc1xx.spi",
+                         S5PC1XX_SPI_BASE + S5PC1XX_SPI_SHIFT,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI1));
+    sysbus_create_simple("s5pc1xx.spi",
+                         S5PC1XX_SPI_BASE + S5PC1XX_SPI_SHIFT * 2,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI2));
+
+    /* PMU */
+    sysbus_create_simple("s5pc1xx.pmu", S5PC1XX_PMU_BASE, NULL);
+
+    /* MAX17040 */
+    max17040_init(i2c->gpio3, S5PC1XX_MAX17040_ADDR);
+
+    /* MAX8998 */
+    max8998_init(i2c->gpio4, S5PC1XX_MAX8998_ADDR);
+
+    /* MAX8998 RTC */
+    max8998_rtc_init(i2c->gpio4, S5PC1XX_MAX8998_RTC_ADDR);
+
+    /* USB */
+    if (usb_enabled) {
+        sysbus_create_simple("sysbus-ohci", S5PC1XX_USB_OHCI_BASE,
+                             s5pc1xx_get_irq(s, S5PC1XX_IRQ_UHOST));
+        sysbus_create_simple("usb-ehci", S5PC1XX_USB_EHCI_BASE,
+                             s5pc1xx_get_irq(s, S5PC1XX_IRQ_UHOST));
+    }
+
+    /* Clock devices */
+    /* CMU */
+    sysbus_create_simple("s5pc1xx.clk", S5PC1XX_CMU_BASE, NULL);
+
+    /* RTC */
+    sysbus_create_varargs("s5pc1xx.rtc", S5PC1XX_RTC_BASE,
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_RTC_ALARM),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_RTC_TICK), NULL);
+
+    /* PWM */
+    sysbus_create_varargs("s5pc1xx.pwm", S5PC1XX_PWM_BASE,
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER0),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER1),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER2),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER3),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER4), NULL);
+
+    /* WDT */
+    sysbus_create_simple("s5pc1xx.wdt", S5PC1XX_WDT_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_WDT));
+
+    /* ST */
+    sysbus_create_simple("s5pc1xx.st", S5PC1XX_ST_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_SYS_TIMER));
+
+    /* Storage devices */
+    /* NAND */
+    sysbus_create_simple("s5pc1xx.nand", S5PC1XX_NAND_BASE, NULL);
+
+    /* OneNAND */
+    s5pc1xx_onenand_init(S5PC1XX_ONENAND_BASE);
+
+    /* SROMC */
+    s5pc1xx_srom_init(S5PC1XX_SROMC_BASE, S5PC1XX_SROMC_NUM_BANKS);
+
+    /* SD/MMC */
+    sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC0_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC0));
+
+    sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC1_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC1));
+
+    sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC2_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC2));
+
+    sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC3_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC3));
+
+    dinfo = drive_get(IF_IDE, 0, 0);
+    mmio_ide_init(S5PC1XX_CFCON_ATAPI1, S5PC1XX_CFCON_ATAPI2,
+                  s5pc1xx_get_irq(s, S5PC1XX_IRQ_CFCON), 2, dinfo, NULL);
+
+    /* Input/Output devices */
+    /* UART */
+    if (serial_hds[0]) {
+        chr2 = serial_hds[0];
+    } else {
+        chr2 = qemu_chr_open("s5pc1xx.uart", "vc:800x600", NULL);
+    }
+    chr3 =
+        qemu_chr_open("AT_socket", "tcp:localhost:7776,server,nowait", NULL);
+    s5pc1xx_uart_init(S5PC1XX_UART_BASE, 0, 256,
+                      s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART0), NULL);
+    s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT, 1, 64,
+                      s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART1), NULL);
+    s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT * 2, 2, 16,
+                      s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART2), chr2);
+    s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT * 3, 3, 16,
+                      s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART3), chr3);
+
+    /* QT602240 Touchscreen */
+    dev = i2c_create_slave(i2c->intrf2, "qt602240", S5PC1XX_QT602240_ADDR);
+    qdev_connect_gpio_out(dev, 0,
+                          s5pc1xx_gpoint_irq(gpio, S5PC1XX_QT602240_IRQ));
+
+    /* S3C Touchscreen */
+    s5pc1xx_tsadc_init(S5PC1XX_TSADC0_BASE,
+                       s5pc1xx_get_irq(s, S5PC1XX_IRQ_ADC0),
+                       s5pc1xx_get_irq(s, S5PC1XX_IRQ_PENDN0),
+                       1, 12, 0, 120, 0, 200);
+    s5pc1xx_tsadc_init(S5PC1XX_TSADC1_BASE,
+                       s5pc1xx_get_irq(s, S5PC1XX_IRQ_ADC1),
+                       s5pc1xx_get_irq(s, S5PC1XX_IRQ_PENDN1),
+                       1, 12, 0, 120, 0, 200);
+
+    /* Keypad */
+    s5pc1xx_keyif_init(S5PC1XX_KEYPAD_BASE,
+                       s5pc1xx_get_irq(s, S5PC1XX_IRQ_KEYPAD),
+                       "aquila", 8);
+
+    /* MSC5000 Touchkeys */
+    dev = i2c_create_slave(i2c->gpio10, "mcs5000.universal",
+                           S5PC1XX_MCS5000_ADDR);
+    qdev_connect_gpio_out(dev, 0,
+                          s5pc1xx_gpoint_irq(gpio, S5PC1XX_MCS5000_IRQ));
+
+    /* USB-OTG */
+    s5pc1xx_usb_otg_init(&nd_table[0],
+                         S5PC1XX_USB_OTG_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_OTG));
+
+    /* OneDRAM */
+    s5pc1xx_onedram_init("s5pc1xx.onedram.aquila.xmm", S5PC1XX_ONEDRAM_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_ONEDRAM_INT_AP));
+
+    /* AK8973 Compass Emulation */
+    dev = i2c_create_slave(i2c->intrfDDC, "ak8973", S5PC1XX_AK8973_ADDR);
+    compass_device = dev;
+    qdev_connect_gpio_out(dev, 0, s5pc1xx_gpoint_irq(gpio, S5PC1XX_AK8973_IRQ));
+
+    /* Audio devices and interfaces */
+    /* SPDIF */
+    sysbus_create_simple("s5pc1xx.spdif", S5PC1XX_SPDIF_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPDIF));
+
+    /* AC97 */
+    sysbus_create_simple("s5pc1xx.ac97", S5PC1XX_AC97_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_AC97));
+
+    /* PCM */
+    s5pc1xx_pcm_init(S5PC1XX_PCM0_BASE, 0,
+                     s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM0));
+    s5pc1xx_pcm_init(S5PC1XX_PCM1_BASE, 1,
+                     s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM1));
+    s5pc1xx_pcm_init(S5PC1XX_PCM2_BASE, 2,
+                     s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM2));
+
+    /* WM8994 */
+    dev = i2c_create_slave(i2c->gpio5, "wm8994", S5PC1XX_WM8994_ADDR);
+
+    /* I2S */
+    dma_stop_irq1 = qdev_get_gpio_in(dma0, PL330_PERIPH_NUM_I2S1);
+    dma_stop_irq2 = qdev_get_gpio_in(dma1, PL330_PERIPH_NUM_I2S2);
+    s5pc1xx_i2s_init(S5PC1XX_I2S2_V5_BASE,
+                     s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2S_V5), dev /* WM8994 */,
+                     dma_stop_irq1, dma_stop_irq2);
+
+    /* Video devices */
+    /* LCD */
+    sysbus_create_varargs("s5pc1xx.lcd", S5PC1XX_LCD_BASE,
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD0),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD1),
+                          s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD2), NULL);
+
+    /* JPEG */
+    sysbus_create_simple("s5pc1xx.jpeg", S5PC1XX_JPEG_BASE,
+                         s5pc1xx_get_irq(s, S5PC1XX_IRQ_JPEG));
+
+#ifdef CONFIG_GLES2
+    gles2_init(s->env);
+#endif
+
+    /* Load the kernel */
+    s5pc110x_binfo.ram_size = ram_size;
+    s5pc110x_binfo.kernel_filename = kernel_fname;
+    s5pc110x_binfo.initrd_filename = initrd_name;
+    s5pc110x_binfo.loader_start = 0x30000000;
+    if (kernel_cmdline) {
+        if (strstr(kernel_cmdline,"duallcd")) {
+            /* Board is 'media' for enabling duallcd */
+            s5pc110x_binfo.revision = 0x1003;
+        }
+        s5pc110x_binfo.kernel_cmdline = kernel_cmdline;
+    } else {
+        s5pc110x_binfo.kernel_cmdline =
+            "root=/dev/mtdblock2 rootfstype=cramfs init=/linuxrc "
+            "console=ttySAC2,115200 mem=128M debug";
+    }
+
+    arm_load_kernel(s->env, &s5pc110x_binfo);
+
+    qemu_register_reset(s5pc110_reset, s);
+    s5pc110_reset(s);
+
+    /* Don't hide cursor since we are using touchscreen */
+    cursor_hide = 0;
+}
+
+static QEMUMachine s5pc110_machine = {
+    .name = "s5pc110",
+    .desc = "Samsung S5PC110-base board",
+    .init = s5pc110_init,
+};
+
+static void s5pc1xx_machine_init(void)
+{
+    qemu_register_machine(&s5pc110_machine);
+}
+
+machine_init(s5pc1xx_machine_init);
diff --git a/hw/s5pc1xx.h b/hw/s5pc1xx.h
new file mode 100644 (file)
index 0000000..5ec95d5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Samsung S5PC1XX-based board emulation.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ *                Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *                Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#ifndef hw_s5pc1xx_h
+#define hw_s5pc1xx_h     "s5pc1xx.h"
+
+#include "qemu-common.h"
+
+
+typedef struct Clk *S5pc1xxClk;
+typedef void GPIOWriteMemoryFunc(void *opaque, int io_index, uint32_t value);
+typedef uint32_t GPIOReadMemoryFunc(void *opaque, int io_index);
+
+struct s5pc1xx_state_s {
+    /* Model */
+    enum s5pc1xx_model {
+        s5pc110
+    } mpu_model;
+
+    /* CPU Core */
+    CPUState *env;
+
+    /* Interrupts */
+    qemu_irq **irq;
+
+    /* Amount of different memory types */
+    ram_addr_t sdram_size;
+    ram_addr_t isrom_size;
+    ram_addr_t isram_size;
+};
+
+
+int compass_update(int8_t x, int8_t y, int8_t z, int8_t temp);
+
+/* s5pc1xx_gpio.c */
+void s5pc1xx_gpio_register_io_memory(int io_index, int instance,
+                                     GPIOReadMemoryFunc *mem_read,
+                                     GPIOWriteMemoryFunc *mem_write,
+                                     GPIOWriteMemoryFunc *mem_conf,
+                                     void *opaque);
+
+uint32_t s5pc1xx_empty_gpio_read(void *opaque, int io_index);
+void s5pc1xx_empty_gpio_write(void *opaque, int io_index, uint32_t value);
+
+/* s5pc1xx_i2c_gpio.c */
+DeviceState *s5pc1xx_i2c_gpio_init(int instance);
+
+/* s5pc1xx_clk.c */
+S5pc1xxClk s5pc1xx_findclk(const char *name);
+int64_t s5pc1xx_clk_getrate(S5pc1xxClk clk);
+
+/* s5pc1xx_pmu.c */
+DeviceState *max17040_init(i2c_bus *bus, int addr);
+DeviceState *max8998_init(i2c_bus *bus, int addr);
+DeviceState *max8998_rtc_init(i2c_bus *bus, int addr);
+
+/* s5pc1xx_srom.c */
+DeviceState *s5pc1xx_srom_init(target_phys_addr_t base, int num_banks);
+
+/* s5pc1xx_gpio.c */
+qemu_irq s5pc1xx_gpoint_irq(DeviceState *d, int irq_num);
+
+/* s5pc1xx_uart.c */
+DeviceState *s5pc1xx_uart_init(target_phys_addr_t base, int instance,
+                               int queue_size, qemu_irq irq,
+                               CharDriverState *chr);
+
+/* s5pc1xx_onenand.c */
+DeviceState *s5pc1xx_onenand_init(target_phys_addr_t base);
+
+/* s5pc1xx_tsadc.c */
+DeviceState *s5pc1xx_tsadc_init(target_phys_addr_t base, qemu_irq irq_adc,
+                                qemu_irq irq_pennd, int new, int resolution,
+                                int minx, int maxx, int miny, int maxy);
+
+/* s5pc1xx_keyif.c */
+DeviceState *s5pc1xx_keyif_init(target_phys_addr_t base, qemu_irq irq,
+                                const char *keymapname, uint32_t shift);
+
+/* s5pc1xx_i2s.c */
+DeviceState *s5pc1xx_i2s_init(target_phys_addr_t base, qemu_irq irq,
+                              DeviceState *wm8994_dev, qemu_irq dma_irq1,
+                              qemu_irq dma_irq2);
+
+/* s5pc1xx_pcm.c */
+DeviceState *s5pc1xx_pcm_init(target_phys_addr_t base,
+                              int instance,
+                              qemu_irq irq);
+
+/* s5pc1xx_onedram.c */
+DeviceState *s5pc1xx_onedram_init(const char *name, target_phys_addr_t base,
+                                  qemu_irq irq_ap);
+
+/* usb-ohci.c */
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+                       qemu_irq irq);
+
+void s5pc1xx_usb_otg_init(NICInfo *nd, target_phys_addr_t base, qemu_irq irq);
+
+#endif /* hw_s5pc1xx_h */
diff --git a/hw/s5pc1xx_ac97.c b/hw/s5pc1xx_ac97.c
new file mode 100644 (file)
index 0000000..fc72188
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * AC97 controller for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+#define ALL_BITS(b,a)               (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the AC97 Global Control Register 0x00000000 */
+#define AC_GLBCTRL          0x00
+    #define ALL_CLEAR               ALL_BITS(30, 24)
+    #define INT_EN(stat)            (stat)
+    #define TRANSFER_EN             (1 << 3)
+    #define AC_LINK_ON              (1 << 2)
+    #define WARM_RESET              (1 << 1)
+    #define COLD_RESET              (1 << 0)
+
+/* R Specifies the AC97 Global Status Register 0x00000001 */
+#define AC_GLBSTAT          0x04
+    #define CODEC_READY_INT         (1 << 22)
+    #define PCM_OUT_UNDER_INT       (1 << 21)
+    #define PCM_IN_OVER_INT         (1 << 20)
+    #define MIC_IN_OVER_INT         (1 << 19)
+    #define PCM_OUT_TH_INT          (1 << 18)
+    #define PCM_IN_TH_INT           (1 << 17)
+    #define MIC_IN_TH_INT           (1 << 16)
+    #define ALL_STAT                ALL_BITS(22, 16)
+
+/* R/W Specifies the AC97 Codec Command Register 0x00000000 */
+#define AC_CODEC_CMD        0x08
+/* R Specifies the AC97 Codec Status Register 0x00000000 */
+#define AC_CODEC_STAT       0x0C
+/* R Specifies the AC97 PCM Out/In Channel FIFO Address Register 0x00000000 */
+#define AC_PCMADDR          0x10
+/* R Specifies the AC97 MIC In Channel FIFO Address Register 0x00000000 */
+#define AC_MICADDR          0x14
+/* R/W Specifies the AC97 PCM Out/In Channel FIFO Data Register 0x00000000 */
+#define AC_PCMDATA          0x18
+/* R/W Specifies the AC97 MIC In Channel FIFO Data Register 0x00000000 */
+#define AC_MICDATA          0x1C
+
+#define S5PC1XX_AC97_REG_MEM_SIZE 0x20
+
+
+typedef struct S5pc1xxAC97State {
+    SysBusDevice busdev;
+
+    uint32_t glbctrl;
+    uint32_t glbstat;
+    uint32_t codec_cmd;
+    uint32_t codec_stat;
+    uint32_t pcmaddr;
+    uint32_t micaddr;
+    uint32_t pcmdata;
+    uint32_t micdata;
+
+    struct FrameIn {
+        uint16_t pcm_left_fifo[16];
+        uint16_t pcm_right_fifo[16];
+
+        uint64_t pcm_write_idx;
+        uint64_t pcm_read_idx;
+
+        uint16_t mic_fifo[16];
+
+        uint64_t mic_write_idx;
+        uint64_t mic_read_idx;
+
+        uint16_t tag_phase;
+        uint32_t data_phase[12];
+    } in;
+
+    struct FrameOut {
+        uint16_t pcm_left_fifo[16];
+        uint16_t pcm_right_fifo[16];
+
+        uint64_t pcm_write_idx;
+        uint64_t pcm_read_idx;
+
+        uint16_t tag_phase;
+        uint32_t data_phase[12];
+    } out;
+
+    uint8_t delay;
+    uint8_t cur_pos;
+    uint8_t stream;
+    uint8_t sync_en;
+    uint8_t reset;
+
+    qemu_irq  irq;
+    QEMUTimer *ac97_timer;
+    uint64_t  last_ac97_time;
+} S5pc1xxAC97State;
+
+
+/* Function for initialization and cold reset */
+static void s5pc1xx_ac97_reset(void *opaque)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+    int i;
+
+    s->delay      = 2;
+    s->stream     = 0;
+    s->sync_en    = 0;
+
+    s->glbctrl    = 0;
+    s->glbstat    = 0;
+    s->codec_cmd  = 0;
+    s->codec_stat = 0;
+    s->pcmaddr    = 0;
+    s->micaddr    = 0;
+    s->pcmdata    = 0;
+    s->micdata    = 0;
+
+    s->in.tag_phase  = 0;
+    s->out.tag_phase = 0;
+
+    for (i = 0; i < 12; i++) {
+        s->in.data_phase[i]  = 0;
+        s->out.data_phase[i] = 0;
+    }
+}
+
+/* Interrupts handler */
+static void s5pc1xx_ac97_irq(S5pc1xxAC97State *s,
+                             uint32_t stat, uint32_t clear)
+{
+    if (stat) {
+        s->glbstat |= stat;
+        if (s->glbctrl & INT_EN(stat))  /* if enabled */
+            qemu_irq_raise(s->irq);
+    }
+    if (clear) {
+        s->glbstat &= ~(clear >> 8);
+        if (!(s->glbstat & ALL_STAT))   /* if all clear */
+            qemu_irq_lower(s->irq);
+    }
+}
+
+/* Controls input fifo stage */
+static void s5pc1xx_ac97_infifo_control(S5pc1xxAC97State *s)
+{
+    uint8_t pcm_depth, mic_depth;
+
+    pcm_depth = (s->in.pcm_write_idx - s->in.pcm_read_idx) % 17;
+    mic_depth = (s->in.mic_write_idx - s->in.mic_read_idx) % 17;
+
+    if (pcm_depth == 16)
+        s5pc1xx_ac97_irq(s, PCM_IN_OVER_INT, 0);
+
+    if (pcm_depth > 7)
+        s5pc1xx_ac97_irq(s, PCM_IN_TH_INT, 0);
+
+    if (mic_depth == 16)
+        s5pc1xx_ac97_irq(s, MIC_IN_OVER_INT, 0);
+
+    if (mic_depth > 7)
+        s5pc1xx_ac97_irq(s, MIC_IN_TH_INT, 0);
+}
+
+/* Controls output fifo stage */
+static void s5pc1xx_ac97_outfifo_control(S5pc1xxAC97State *s)
+{
+    uint8_t pcm_depth;
+
+    pcm_depth = (s->out.pcm_write_idx - s->out.pcm_read_idx) % 17;
+
+    if (pcm_depth == 0)
+        s5pc1xx_ac97_irq(s, PCM_OUT_UNDER_INT, 0);
+
+    if (pcm_depth < 9)
+        s5pc1xx_ac97_irq(s, PCM_OUT_TH_INT, 0);
+}
+
+/* Sync timer */
+static void s5pc1xx_ac97_sync(void *opaque)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+    uint64_t next_ac97_time;
+
+    if (s->sync_en) {
+        s->delay = 0;   /* used for 1/12MHz delay */
+        s->last_ac97_time = qemu_get_clock(vm_clock);
+        next_ac97_time = s->last_ac97_time +
+            muldiv64(1, get_ticks_per_sec(), 48000);  /* 48 KHz cycle */
+        qemu_mod_timer(s->ac97_timer, next_ac97_time);
+    } else {
+        qemu_del_timer(s->ac97_timer);
+    }
+}
+
+/* Bitclk cycles counter */
+static uint8_t s5pc1xx_ac97_spent_cycles(S5pc1xxAC97State *s)
+{
+    uint64_t spent_1G, spent_12M;
+
+    spent_1G = qemu_get_clock(vm_clock) - s->last_ac97_time;
+    spent_12M = muldiv64(spent_1G, 12288000, get_ticks_per_sec());
+
+    return (spent_12M % 256);
+}
+
+/* Get current input frame and prepare next output frame */
+static void s5pc1xx_ac97_next_frame(S5pc1xxAC97State *s)
+{
+    short i;
+
+    /* Get input frame */
+
+    /* check if codec_ready */
+    if  (s->in.tag_phase & (1 << 15)) {
+        /* check tag bits and check if the received address
+         * is equal to the most recent sent address */
+        if ((s->in.tag_phase & (1 << 14)) &&
+            (s->in.tag_phase & (1 << 13)) &&
+            (s->in.data_phase[0] & ALL_BITS(18, 12)) ==
+            (s->out.data_phase[0] & ALL_BITS(18, 12)))
+            s->codec_stat = (s->in.data_phase[0] << 4 & ALL_BITS(22, 16)) |
+                            (s->in.data_phase[1] >> 4);
+
+        if (s->in.pcm_write_idx < s->in.pcm_read_idx + 16) {
+
+            if (s->in.tag_phase & (1 << 12))
+                s->in.pcm_left_fifo[s->in.pcm_write_idx % 16]  =
+                    (s->in.data_phase[2] >> 4);
+
+            if (s->in.tag_phase & (1 << 11))
+                s->in.pcm_right_fifo[s->in.pcm_write_idx % 16] =
+                    (s->in.data_phase[3] >> 4);
+
+            s->in.pcm_write_idx++;
+        }
+
+        if (s->in.mic_write_idx < s->in.mic_read_idx + 16) {
+
+            if (s->in.tag_phase & (1 << 9))
+                s->in.mic_fifo[s->in.mic_write_idx % 16] =
+                    (s->in.data_phase[5] >> 4);
+
+            s->in.mic_write_idx++;
+        }
+
+        s5pc1xx_ac97_infifo_control(s);
+    }
+
+    /* Set output frame */
+
+    s->out.tag_phase = 0;
+    for (i = 0; i < 4; i++)
+        s->out.data_phase[i] = 0;
+
+    if (s->codec_cmd) {
+        /* enable slots 1 and 2 */
+        s->out.tag_phase |= ALL_BITS(14, 13);
+        s->out.data_phase[0] = s->codec_cmd >> 4 & ALL_BITS(19, 12);
+        s->out.data_phase[1] = s->codec_cmd << 4 & ALL_BITS(19, 4);
+        s->codec_cmd = 0;
+    } else {
+        s->out.tag_phase &= ~ALL_BITS(14, 13);
+    }
+
+    /* check if fifo is not empty */
+    if (s->out.pcm_read_idx < s->out.pcm_write_idx) {
+        /* enable slots 3 and 4 */
+        s->out.tag_phase |= ALL_BITS(12, 11);
+        s->out.data_phase[2] =
+            s->out.pcm_left_fifo[s->out.pcm_read_idx % 16] << 4;
+        s->out.data_phase[3] =
+            s->out.pcm_right_fifo[s->out.pcm_read_idx % 16] << 4;
+        s->out.pcm_read_idx++;
+    } else {
+        s->out.tag_phase &= ~ALL_BITS(12, 11);
+    }
+
+    /* set bit 15 if any of bits 14~11 is high */
+    if (s->out.tag_phase & ALL_BITS(14, 11))
+        s->out.tag_phase |= (1 << 15);
+
+    s5pc1xx_ac97_outfifo_control(s);
+}
+
+/* Read AC97 by GPIO */
+static uint32_t s5pc1xx_ac97_gpio_read(void *opaque, int io_index)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+    uint8_t ret_val;
+
+    switch (io_index) {
+    case GPIO_AC97RESETn:
+        return s->reset;
+
+    case GPIO_AC97SYNC:
+        return ((s->sync_en) && (s5pc1xx_ac97_spent_cycles(s) < 16));
+
+    case GPIO_AC97SDO:
+        if (!s->stream)
+            break;
+
+        /* Note: '-1' is a delay just after start */
+        s->cur_pos = s5pc1xx_ac97_spent_cycles(s) - 1;
+
+        if (s->cur_pos < 16)
+            ret_val = s->out.tag_phase >> (15 - s->cur_pos) & 0x1;
+        else
+            ret_val =
+                s->out.data_phase[(s->cur_pos - 16) / 20] >>
+                    (19 - (s->cur_pos - 16) % 20) & 0x1;
+        return ret_val;
+    }
+    return 0;
+}
+
+/* Write AC97 by GPIO */
+static void s5pc1xx_ac97_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+    switch (io_index) {
+    case GPIO_AC97BITCLK:
+        if (value) {
+            if (s->delay == 1)
+                s5pc1xx_ac97_next_frame(s);
+            if (s->delay < 2)
+                s->delay++;
+        }
+        break;
+
+    case GPIO_AC97SDI:
+        if (!(s->stream))
+            break;
+
+        /* Note: '-1' is a delay just after start */
+        s->cur_pos = s5pc1xx_ac97_spent_cycles(s) - 1;
+
+        if (s->cur_pos < 16)
+            s->in.tag_phase |= value << (15 - s->cur_pos);
+        else
+            s->in.data_phase[(s->cur_pos - 16) / 20] |=
+                value << (19 - (s->cur_pos - 16) % 20);
+        break;
+    }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_ac97_gpio_readfn   = s5pc1xx_ac97_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_ac97_gpio_writefn = s5pc1xx_ac97_gpio_write;
+
+/* Read AC97 by OS */
+static uint32_t s5pc1xx_ac97_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+    switch(offset) {
+    case AC_GLBCTRL:
+        return s->glbctrl;
+    case AC_GLBSTAT:
+        return s->glbstat;
+    case AC_CODEC_CMD:
+        return s->codec_cmd;
+    case AC_CODEC_STAT:
+        return s->codec_stat;
+    case AC_PCMADDR:
+        s->pcmaddr = ((s->in.pcm_write_idx % 16) << 0) |
+                     ((s->out.pcm_write_idx % 16) << 8) |
+                     ((s->in.pcm_read_idx % 16) << 16) |
+                     ((s->out.pcm_read_idx % 16) << 24);
+        return s->pcmaddr;
+    case AC_MICADDR:
+        s->micaddr = ((s->in.mic_write_idx % 16) << 0) |
+                     ((s->in.mic_read_idx % 16) << 16);
+        return s->micaddr;
+    case AC_PCMDATA:
+        /* check if fifo is not empty */
+        if (s->in.pcm_read_idx < s->in.pcm_write_idx) {
+            s->pcmdata = s->in.pcm_left_fifo[s->in.pcm_read_idx % 16] |
+                (s->in.pcm_right_fifo[s->in.pcm_read_idx % 16] << 16);
+            s->in.pcm_read_idx++;
+        } else {
+            return 0;
+        }
+        return s->pcmdata;
+    case AC_MICDATA:
+        /* check if fifo is not empty */
+        if (s->in.mic_read_idx < s->in.mic_write_idx) {
+            s->micdata = s->in.mic_fifo[s->in.mic_read_idx % 16];
+            s->in.mic_read_idx++;
+        } else {
+            return 0;
+        }
+        return s->micdata;
+    default:
+        hw_error("s5pc1xx.ac97: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+/* Write AC97 by OS */
+static void s5pc1xx_ac97_write(void *opaque, target_phys_addr_t offset,
+                               uint32_t value)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+    switch(offset) {
+    case AC_GLBCTRL:
+        if (value & COLD_RESET) {
+            s->reset = 1;
+            s5pc1xx_ac97_reset(s);
+        }
+
+        if (value & WARM_RESET) {
+            s->reset = 0;
+            s5pc1xx_ac97_irq(s, CODEC_READY_INT, 0);
+        }
+
+        if (s->reset)
+            break;
+
+        if ((value & AC_LINK_ON) > (s->glbctrl & AC_LINK_ON)) {
+            s->sync_en = 1;
+            s5pc1xx_ac97_sync(s);
+        }
+
+        /* the value is set high above */
+        s->sync_en = (value & AC_LINK_ON) ? : 0;
+        s->stream = (value & TRANSFER_EN) ? 1 : 0;
+
+        if (value & ALL_CLEAR)
+            s5pc1xx_ac97_irq(s, 0, value & ALL_CLEAR);
+
+        s->glbctrl = value & ~ALL_CLEAR;
+        break;
+    case AC_CODEC_CMD:
+        if (!s->reset)
+            s->codec_cmd = value;
+        break;
+    case AC_PCMDATA:
+        if (s->reset)
+            break;
+
+        /* check if fifo is full */
+        if (s->out.pcm_write_idx == s->out.pcm_read_idx + 16)
+            break;
+
+        s->out.pcm_left_fifo[s->out.pcm_write_idx % 16] =
+            value & ALL_BITS(15, 0);
+        s->out.pcm_right_fifo[s->out.pcm_write_idx % 16] =
+            value >> 16 & ALL_BITS(15, 0);
+
+        s->out.pcm_write_idx++;
+        break;
+    case AC_MICDATA:
+        /* mic data can't be written */
+        break;
+    default:
+        hw_error("s5pc1xx.ac97: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_ac97_readfn[] = {
+   s5pc1xx_ac97_read,
+   s5pc1xx_ac97_read,
+   s5pc1xx_ac97_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_ac97_writefn[] = {
+   s5pc1xx_ac97_write,
+   s5pc1xx_ac97_write,
+   s5pc1xx_ac97_write
+};
+
+static void s5pc1xx_ac97_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+    uint64_t last;
+    int i;
+
+    qemu_put_be32s(f, &s->glbctrl);
+    qemu_put_be32s(f, &s->glbstat);
+    qemu_put_be32s(f, &s->codec_cmd);
+    qemu_put_be32s(f, &s->codec_stat);
+    qemu_put_be32s(f, &s->pcmaddr);
+    qemu_put_be32s(f, &s->micaddr);
+    qemu_put_be32s(f, &s->pcmdata);
+    qemu_put_be32s(f, &s->micdata);
+
+    for (i = 0; i < 16; i++) {
+        qemu_put_be16s(f, &s->in.pcm_left_fifo[i]);
+        qemu_put_be16s(f, &s->in.pcm_right_fifo[i]);
+        qemu_put_be16s(f, &s->in.mic_fifo[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_put_be32s(f, &s->in.data_phase[i]);
+    }
+
+    qemu_put_be64s(f, &s->in.pcm_write_idx);
+    qemu_put_be64s(f, &s->in.pcm_read_idx);
+
+    qemu_put_be64s(f, &s->in.mic_write_idx);
+    qemu_put_be64s(f, &s->in.mic_read_idx);
+
+    qemu_put_be16s(f, &s->in.tag_phase);
+
+    for (i = 0; i < 16; i++) {
+        qemu_put_be16s(f, &s->out.pcm_left_fifo[i]);
+        qemu_put_be16s(f, &s->out.pcm_right_fifo[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_put_be32s(f, &s->out.data_phase[i]);
+    }
+
+    qemu_put_be64s(f, &s->out.pcm_write_idx);
+    qemu_put_be64s(f, &s->out.pcm_read_idx);
+    qemu_put_be16s(f, &s->out.tag_phase);
+
+    qemu_put_8s(f, &s->delay);
+    qemu_put_8s(f, &s->cur_pos);
+    qemu_put_8s(f, &s->stream);
+    qemu_put_8s(f, &s->sync_en);
+    qemu_put_8s(f, &s->reset);
+
+    last = qemu_get_clock(vm_clock) - s->last_ac97_time;
+    qemu_put_be64s(f, &last);
+
+    qemu_put_timer(f, s->ac97_timer);
+}
+
+static int s5pc1xx_ac97_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+    uint64_t last;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->glbctrl);
+    qemu_get_be32s(f, &s->glbstat);
+    qemu_get_be32s(f, &s->codec_cmd);
+    qemu_get_be32s(f, &s->codec_stat);
+    qemu_get_be32s(f, &s->pcmaddr);
+    qemu_get_be32s(f, &s->micaddr);
+    qemu_get_be32s(f, &s->pcmdata);
+    qemu_get_be32s(f, &s->micdata);
+
+    for (i = 0; i < 16; i++) {
+        qemu_get_be16s(f, &s->in.pcm_left_fifo[i]);
+        qemu_get_be16s(f, &s->in.pcm_right_fifo[i]);
+        qemu_get_be16s(f, &s->in.mic_fifo[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_get_be32s(f, &s->in.data_phase[i]);
+    }
+
+    qemu_get_be64s(f, &s->in.pcm_write_idx);
+    qemu_get_be64s(f, &s->in.pcm_read_idx);
+
+    qemu_get_be64s(f, &s->in.mic_write_idx);
+    qemu_get_be64s(f, &s->in.mic_read_idx);
+
+    qemu_get_be16s(f, &s->in.tag_phase);
+
+    for (i = 0; i < 16; i++) {
+        qemu_get_be16s(f, &s->out.pcm_left_fifo[i]);
+        qemu_get_be16s(f, &s->out.pcm_right_fifo[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_get_be32s(f, &s->out.data_phase[i]);
+    }
+
+    qemu_get_be64s(f, &s->out.pcm_write_idx);
+    qemu_get_be64s(f, &s->out.pcm_read_idx);
+    qemu_get_be16s(f, &s->out.tag_phase);
+
+    qemu_get_8s(f, &s->delay);
+    qemu_get_8s(f, &s->cur_pos);
+    qemu_get_8s(f, &s->stream);
+    qemu_get_8s(f, &s->sync_en);
+    qemu_get_8s(f, &s->reset);
+
+    qemu_get_be64s(f, &last);
+    s->last_ac97_time = qemu_get_clock(vm_clock) - last;
+
+    qemu_get_timer(f, s->ac97_timer);
+
+    return 0;
+}
+
+/* AC97 initialization */
+static int s5pc1xx_ac97_init(SysBusDevice *dev)
+{
+    S5pc1xxAC97State *s = FROM_SYSBUS(S5pc1xxAC97State, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_ac97_readfn, s5pc1xx_ac97_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_AC97_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_AC97, 0, s5pc1xx_ac97_gpio_readfn,
+                                    s5pc1xx_ac97_gpio_writefn, NULL, s);
+    s->ac97_timer = qemu_new_timer(vm_clock, s5pc1xx_ac97_sync, s);
+
+    qemu_register_reset(s5pc1xx_ac97_reset, s);
+    s5pc1xx_ac97_reset(s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.ac97", -1, 1,
+                    s5pc1xx_ac97_save, s5pc1xx_ac97_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_ac97_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.ac97", sizeof(S5pc1xxAC97State),
+                        s5pc1xx_ac97_init);
+}
+
+device_init(s5pc1xx_ac97_register_devices)
diff --git a/hw/s5pc1xx_clk.c b/hw/s5pc1xx_clk.c
new file mode 100644 (file)
index 0000000..c04975c
--- /dev/null
@@ -0,0 +1,827 @@
+/*
+ * Clock controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on OMAP CMU (hw/omap_clk.c)
+ */
+
+#include "sysbus.h"
+#include "s5pc1xx.h"
+
+
+/* PLL lock */
+#define APLL_LOCK   0x000  /* R/W Control PLL locking period for APLL 0x0000_0FFF */
+#define MPLL_LOCK   0x008  /* R/W Control PLL locking period for MPLL 0x0000_0FFF */
+#define EPLL_LOCK   0x010  /* R/W Control PLL locking period for EPLL 0x0000_0FFF */
+#define VPLL_LOCK   0x020  /* R/W Control PLL locking period for VPLL 0x0000_0FFF */
+
+/* PLL control */
+#define APLL_CON    0x100   /* R/W Control PLL output frequency for APLL 0x0C80_0301 */
+#define MPLL_CON    0x108   /* R/W Control PLL output frequency for MPLL 0x014D_0301 */
+#define EPLL_CON    0x110   /* R/W Control PLL output frequency for EPLL 0x0085_0302 */
+#define VPLL_CON    0x120   /* R/W Control PLL output frequency for VPLL 0x006C_0303 */
+
+/* Clock source */
+#define CLK_SRC0    0x200   /* R/W Select clock source 0 (Main) 0x0000_0000 */
+
+    #define ONENAND_SHIFT   28
+    #define MUX133_SHIFT    24
+    #define MUX166_SHIFT    20
+    #define MUX200_SHIFT    16
+    #define VPLL_SHIFT      12
+    #define EPLL_SHIFT      8
+    #define MPLL_SHIFT      4
+    #define APLL_SHIFT      0
+
+#define CLK_SRC1    0x204   /* R/W Select clock source 1 (Multimedia) 0x0000_0000 */
+#define CLK_SRC2    0x208   /* R/W Select clock source 2 (Multimedia) 0x0000_0000 */
+
+    #define MFC_SHIFT       4
+    #define G3D_SHIFT       0
+
+#define CLK_SRC3    0x20C   /* R/W Select clock source 3 (Multimedia) 0x0000_0000 */
+#define CLK_SRC4    0x210   /* R/W Select clock source 4 (Connectivity) 0x0000_0000 */
+#define CLK_SRC5    0x214   /* R/W Select clock source 5 (Connectivity) 0x0000_0000 */
+
+    #define MUX_PWM_SHIFT       12
+
+#define CLK_SRC6    0x218   /* R/W Select clock source 6 (Audio) 0x0000_0000 */
+
+    #define MUX_ONEDRAM_SHIFT   24
+    #define MUX_HPM_SHIFT       16
+    #define MUX_SPDIF_SHIFT     12
+    #define MUX_AUDIO_2_SHIFT   8
+    #define MUX_AUDIO_1_SHIFT   4
+    #define MUX_AUDIO_0_SHIFT   0
+
+#define CLK_SRC_MASK0   0x280  /* R/W Clock source mask0 0xFFFF_FFFF */
+#define CLK_SRC_MASK1   0x284  /* R/W Clock source mask1 0xFFFF_FFFF */
+
+/* Clock divider */
+#define CLK_DIV0    0x300   /* R/W Set clock divider ratio 0 (System clocks) 0x0000_0000 */
+
+#define PCLK66_SHIFT    28
+#define HCLK133_SHIFT   24
+#define PCLK83_SHIFT    20
+#define HCLK166_SHIFT   16
+#define PCLK100_SHIFT   12
+#define HCLK200_SHIFT   8
+#define A2M_SHIFT       4
+#define APLL_SHIFT      0
+
+#define CLK_DIV1    0x304   /* R/W Set clock divider ratio 1 (Multimedia) 0x0000_0000 */
+#define CLK_DIV2    0x308   /* R/W Set clock divider ratio 2 (Multimedia) 0x0000_0000 */
+#define CLK_DIV3    0x30C   /* R/W Set clock divider ratio 3 (Multimedia) 0x0000_0000 */
+#define CLK_DIV4    0x310   /* R/W Set clock divider ratio 4 (Connectivity) 0x0000_0000 */
+#define CLK_DIV5    0x314   /* R/W Set clock divider ratio 5 (Connectivity) 0x0000_0000 */
+
+    #define DIV_PWM_SHIFT       12
+
+#define CLK_DIV6    0x318   /* R/W Set clock divider ratio 6 (Audio & Others) 0x0000_0000 */
+
+    #define DIV_AUDIO_2_SHIFT   8
+    #define DIV_AUDIO_1_SHIFT   4
+    #define DIV_AUDIO_0_SHIFT   0
+
+#define CLK_DIV7    0x31C   /* R/W Set clock divider ratio 7 (IEM_IEC) 0x0000_0000 */
+
+/* Clock gating */
+#define CLK_GATE_MAIN0  0x400   /* R/W Control AXI/AHB clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_MAIN1  0x404   /* R/W Control AXI/AHB clock gating 1 0xFFFF_FFFF */
+#define CLK_GATE_MAIN2  0x408   /* R/W Control AXI/AHB clock gating 2 0xFFFF_FFFF */
+#define CLK_GATE_PERI0  0x420   /* R/W Control APB clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_PERI1  0x424   /* R/W Control APB clock gating 1 0xFFFF_FFFF */
+#define CLK_GATE_SCLK0  0x440   /* R/W Control SCLK clock gating0 0xFFFF_FFFF */
+#define CLK_GATE_SCLK1  0x444   /* R/W Control SCLK clock gating1 0xFFFF_FFFF */
+#define CLK_GATE_IP0    0x460   /* R/W Control IP clock gating0 0xFFFF_FFFF */
+#define CLK_GATE_IP1    0x464   /* R/W Control IP clock gating1 0xFFFF_FFFF */
+#define CLK_GATE_IP2    0x468   /* R/W Control IP clock gating2 0xFFFF_FFFF */
+#define CLK_GATE_IP3    0x46C   /* R/W Control IP clock gating3 0xFFFF_FFFF */
+#define CLK_GATE_IP4    0x470   /* R/W Control IP clock gating4 0xFFFF_FFFF */
+#define CLK_GATE_BLOCK  0x480   /* R/W Control block clock gating 0xFFFF_FFFF */
+#define CLK_GATE_BUS0   0x484   /* R/W Control AXI/AHB bus clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_BUS1   0x488   /* R/W Control AXI/AHB bus clock gating 1 0xFFFF_FFFF */
+
+/* Clock output */
+#define CLK_OUT         0x500   /* R/W Select clock output 0x0000_0000 */
+
+/* Clock divider status */
+#define CLK_DIV_STAT0   0x1000  /* R Clock divider status 0 (CLK_DIV0~3) 0x1111_1111 */
+#define CLK_DIV_STAT1   0x1004  /* R Clock divider status 1 (CLK_DIV4~5) 0x0001_0000 */
+
+/* Clock MUX status */
+#define CLK_MUX_STAT0   0x1100  /* R Clock MUX status 0 0x0000_0000 */
+#define CLK_MUX_STAT1   0x1104  /* R Clock MUX status 1 0x0000_0000 */
+
+/* Control bits */
+#define XOM_0           0
+#define VPLLSRC_SEL     1
+#define ALL_MUX_BITS_0  (1 << ONENAND_SHIFT)    | (1 << MUX133_SHIFT) | \
+                            (1 << MUX166_SHIFT) | (1 << MUX200_SHIFT) | \
+                            (1 << VPLL_SHIFT)   | (1 << EPLL_SHIFT)   | \
+                            (1 << MPLL_SHIFT)   | (1 << APLL_SHIFT)
+#define ALL_DIV_BITS_0  (7 << PCLK66_SHIFT)     | (0xf << HCLK133_SHIFT) | \
+                            (7 << PCLK83_SHIFT) | (0xf << HCLK166_SHIFT) | \
+                            (7 << PCLK100_SHIFT)| (7 << HCLK200_SHIFT)   | \
+                            (7 << A2M_SHIFT)    | (7 << APLL_SHIFT)
+
+#define ALL_MUX_BITS_5  0xffff  /* all the [15:0] bits are used */
+#define ALL_DIV_BITS_5  0xffff  /* all the [15:0] bits are used */
+
+#define NONE            -1
+
+#define S5PC1XX_CLK_REG_MEM_SIZE 0x1110
+
+
+typedef struct {
+    SysBusDevice busdev;
+
+    uint32_t lock_data[5];      /* PLL lock */
+    uint32_t conf_data[5];      /* PLL control */
+    uint32_t clk_src[7];        /* Clock source */
+    uint32_t src_mask[2];       /* Clock source mask */
+    uint32_t clk_div[8];        /* Clock divider */
+    uint32_t clk_gate[23];      /* Clock gating */
+    uint32_t clk_out;           /* Clock output */
+    uint32_t div_stat[2];       /* Clock divider status */
+    uint32_t mux_stat[2];       /* Clock MUX status */
+} CmuStat;
+
+typedef struct Clk {
+    const char *name;           /* Clock name */
+    const char *alias;          /* Clock notes */
+    struct Clk *parent;         /* Parent clock */
+
+    struct Clk *parents[9];     /* Parent cases */
+
+    unsigned short enabled;     /* Is enabled, regardless of its input clk */
+    unsigned long rate;         /* Current rate */
+
+    unsigned int div_val;       /* Rate relative to input (if .enabled) */
+    unsigned int mult_val;      /* Rate relative to input (if .enabled) */
+
+    short mux_shift;            /* MANDATORY FIELD - Shift for mux value in CLK_SRC<src_reg_num> */
+    short div_shift;            /* MANDATORY FIELD - Shift for divisor value in CLK_DIV<div_reg_num> */
+
+    unsigned short src_reg_num; /* See above (default value = 0) */
+    unsigned short div_reg_num; /* See above (default value = 0) */
+} Clk;
+
+
+/* Clocks */
+
+/* oscillators */
+static Clk xtal_osc12m = {
+    .name       = "XXTI",
+    .rate       = 24000000,
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk xtal_osc27m = {
+    .name       = "XXTI27",
+    .rate       = 27000000,
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk xtal_usb_osc48m = {
+    .name       = "XusbXTI",
+    .rate       = 24000000,
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk xtal_rtc_osc32k = {
+    .name       = "XrtcXTI",
+    .rate       = 32768,
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk sclk_hdmi27m = {
+    .name       = "SCLK_HDMI27M",
+    .alias      = "clkin",
+    .parents    = {&xtal_osc27m},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk fin_pll = {
+    .name       = "fin_pll",
+    .alias      = "clkin",
+    .parents    = {XOM_0 ? &xtal_usb_osc48m : &xtal_osc12m},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+/* PLLs */
+static Clk apll = {
+    .name       = "apll",
+    .parents    = {&fin_pll},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk mpll = {
+    .name       = "mpll",
+    .parents    = {&fin_pll},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk epll = {
+    .name       = "epll",
+    .parents    = {&fin_pll},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+static Clk vpll = {
+    .name       = "vpll",
+    .parents    = {VPLLSRC_SEL ? &sclk_hdmi27m : &fin_pll},
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+/* reference clocks */
+static Clk sclk_apll = {
+    .name       = "sclk_apll",
+    .parents    = {&fin_pll, &apll},
+    .mux_shift  = APLL_SHIFT,       /* MUXapll */
+    .div_shift  = NONE,
+};
+
+static Clk sclk_mpll = {
+    .name       = "sclk_mpll",
+    .parents    = {&fin_pll, &mpll},
+    .mux_shift  = MPLL_SHIFT,       /* MUXmpll */
+    .div_shift  = NONE,
+};
+
+static Clk sclk_epll = {
+    .name       = "sclk_epll",
+    .parents    = {&fin_pll, &epll},
+    .mux_shift  = EPLL_SHIFT,       /* MUXepll */
+    .div_shift  = NONE,
+};
+
+static Clk sclk_vpll = {
+    .name       = "sclk_vpll",
+    .parents    = {&fin_pll, &vpll},
+    .mux_shift  = VPLL_SHIFT,       /* MUXvpll */
+    .div_shift  = NONE,
+};
+
+static Clk sclk_a2m = {
+    .name       = "sclk_a2m",
+    .parents    = {&sclk_apll},
+    .mux_shift  = NONE,
+    .div_shift  = A2M_SHIFT,        /* DIVa2m (1~8) */
+};
+
+/* 133MHz domain clocks */
+static Clk hclk133 = {
+    .name       = "hclk_133",
+    .parents    = {&sclk_mpll, &sclk_a2m},
+    .mux_shift  = MUX133_SHIFT,     /* MUX133 */
+    .div_shift  = HCLK133_SHIFT,    /* DIVck133 (1~16) */
+};
+
+static Clk pclk66 = {
+    .name       = "pclk_66",
+    .parents    = {&hclk133},
+    .mux_shift  = NONE,
+    .div_shift  = PCLK66_SHIFT,     /* DIVck66 (1~8) */
+};
+
+/* 166MHz domain clocks */
+static Clk hclk166 = {
+    .name       = "hclk_166",
+    .parents    = {&sclk_mpll, &sclk_a2m},
+    .mux_shift  = MUX166_SHIFT,     /* MUX166 */
+    .div_shift  = HCLK166_SHIFT,    /* DIVck166 (1~16) */
+};
+
+static Clk pclk83 = {
+    .name       = "pclk_83",
+    .parents    = {&hclk166},
+    .mux_shift  = NONE,
+    .div_shift  = PCLK83_SHIFT,     /* DIVck83 (1~8) */
+};
+
+/* 200MHz domain clocks */
+static Clk armclk = {
+    .name       = "armclk",
+    .parents    = {&sclk_apll, &sclk_mpll},
+    .mux_shift  = MUX200_SHIFT,     /* MUX200 */
+    .div_shift  = APLL_SHIFT,       /* DIVapll (1~8) */
+};
+
+static Clk hclk200 = {
+    .name       = "hclk_200",
+    .parents    = {&armclk},
+    .mux_shift  = NONE,
+    .div_shift  = HCLK200_SHIFT,    /* DIVck200 (1~8) */
+};
+
+static Clk pclk100 = {
+    .name       = "pclk_100",
+    .parents    = {&hclk200},
+    .mux_shift  = NONE,
+    .div_shift  = PCLK100_SHIFT,    /* DIVck100 (1~8) */
+};
+
+static Clk hclk100 = {
+    .name       = "hclk_100",
+    .parents    = {&hclk200},
+    .div_val    = 2,                /* DIVimem (2) */
+    .mux_shift  = NONE,
+    .div_shift  = NONE,
+};
+
+/* Special clocks */
+
+static Clk sclk_pwm = {
+    .name       = "sclk_pwm",
+    .parents    = {&xtal_osc12m, &xtal_usb_osc48m, &sclk_hdmi27m,
+
+                   /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+                   &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+                   &sclk_mpll, &sclk_epll, &sclk_vpll},
+    .src_reg_num = 5,               /* CLK_SRC5 register */
+    .mux_shift   = MUX_PWM_SHIFT,   /* MUXpwm */
+    .div_reg_num = 5,               /* CLK_DIV5 register */
+    .div_shift   = DIV_PWM_SHIFT,   /* DIVpwm (1~16) */
+};
+
+static Clk sclk_audio_0 = {
+    .name       = "sclk_audio_0",
+    .parents    = {&xtal_osc12m,
+
+                   /* TODO: should be PCMCDCLK0 below */
+                   &xtal_usb_osc48m,
+
+                   &sclk_hdmi27m,
+
+                   /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+                   &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+                   &sclk_mpll, &sclk_epll, &sclk_vpll},
+    .src_reg_num = 6,                   /* CLK_SRC6 register */
+    .mux_shift   = MUX_AUDIO_0_SHIFT,   /* MUXaudio0 */
+    .div_reg_num = 6,                   /* CLK_DIV6 register */
+    .div_shift   = DIV_AUDIO_0_SHIFT,   /* DIVaudio0 (1~16) */
+};
+
+static Clk sclk_audio_1 = {
+    .name       = "sclk_audio_1",
+    .parents    = {
+                   /* TODO should be I2SCDCLK1, PCMCDCLK1 below */
+                   &xtal_osc12m, &xtal_usb_osc48m,
+
+                   &sclk_hdmi27m,
+
+                   /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+                   &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+                   &sclk_mpll, &sclk_epll, &sclk_vpll},
+    .src_reg_num = 6,                   /* CLK_SRC6 register */
+    .mux_shift   = MUX_AUDIO_1_SHIFT,   /* MUXaudio1 */
+    .div_reg_num = 6,                   /* CLK_DIV6 register */
+    .div_shift   = DIV_AUDIO_1_SHIFT,   /* DIVaudio1 (1~16) */
+};
+
+static Clk sclk_audio_2 = {
+    .name       = "sclk_audio_2",
+    .parents    = {
+                   /* TODO: should be I2SCDCLK2, PCMCDCLK2 below */
+                   &xtal_osc12m, &xtal_usb_osc48m,
+
+                   &sclk_hdmi27m,
+
+                   /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+                   &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+                   &sclk_mpll, &sclk_epll, &sclk_vpll},
+    .src_reg_num = 6,                   /* CLK_SRC6 register */
+    .mux_shift   = MUX_AUDIO_2_SHIFT,   /* MUXaudio2 */
+    .div_reg_num = 6,                   /* CLK_DIV6 register */
+    .div_shift   = DIV_AUDIO_2_SHIFT,   /* DIVaudio2 (1~16) */
+};
+
+static Clk sclk_spdif = {
+    .name        = "sclk_spdif",
+    .parents     = {&sclk_audio_0, &sclk_audio_1, &sclk_audio_2},
+    .src_reg_num = 6,                   /* CLK_SRC6 register */
+    .mux_shift   = MUX_SPDIF_SHIFT,     /* MUXspdif */
+    .div_shift   = NONE,
+};
+
+static Clk *onchip_clks[] = {
+
+    /* non-ULPD clocks */
+    &xtal_osc12m,
+    &xtal_osc27m,
+    &xtal_usb_osc48m,
+    &xtal_rtc_osc32k,
+    &sclk_hdmi27m,
+    &fin_pll,
+
+    /* CLOCKS FROM CMU */
+    &apll,
+    &mpll,
+    &epll,
+    &vpll,
+    &sclk_apll,
+    &sclk_mpll,
+    &sclk_epll,
+    &sclk_vpll,
+    &sclk_a2m,
+    &hclk133,
+    &pclk66,
+    &hclk166,
+    &pclk83,
+    &armclk,
+    &hclk200,
+    &pclk100,
+    &hclk100,
+    &sclk_pwm,
+    &sclk_audio_0,
+    &sclk_audio_1,
+    &sclk_audio_2,
+    &sclk_spdif,
+
+    0
+};
+
+static void s5pc1xx_clk_reset(void *opaque)
+{
+    CmuStat *s = (CmuStat *)opaque;
+
+    /* Set default values for registers */
+    /* TODO: Add all the rest */
+    s->clk_div[0] = 0x14131330;
+    s->clk_src[0] = 0x10001111;
+    s->clk_src[4] = 0x66666666;
+    s->clk_src[5] = 0x777;
+    s->clk_src[6] = 0x1000000;
+
+    /* LOCKED bit is always set */
+    s->conf_data[0] = 0xA0C80601 | (1 << 29);
+    s->conf_data[1] = 0xA29B0C01 | (1 << 29);
+    s->conf_data[2] = 0xA0600602 | (1 << 29);
+    s->conf_data[4] = 0xA06C0603 | (1 << 29);
+}
+
+/* Find a clock by its name and return the clk structure */
+Clk *s5pc1xx_findclk(const char *name)
+{
+    Clk **i, *cur;
+
+    for (i = onchip_clks; *i; i++) {
+        cur = *i;
+        if (!strcmp(cur->name, name) ||
+            (cur->alias && !strcmp(cur->alias, name)))
+            return cur;
+    }
+    hw_error("%s: clock %s not found\n", __FUNCTION__, name);
+}
+
+/* Get a frequency */
+int64_t s5pc1xx_clk_getrate(Clk *clk)
+{
+    return clk->rate;
+}
+
+/* Update parents flow */
+static void s5pc1xx_clk_reparent(CmuStat *cmu_stat)
+{
+    Clk **i, *cur;
+    unsigned short parent_num;
+
+    for (i = onchip_clks; *i; i++) {
+        cur = *i;
+        parent_num = 0;
+
+        if (cur->mux_shift > NONE)
+            parent_num =
+                cmu_stat->clk_src[cur->src_reg_num] >> cur->mux_shift & 0xf;
+        cur->parent = cur->parents[parent_num];
+    }
+}
+
+/* Update clocks rates */
+static void s5pc1xx_clk_rate_update(CmuStat *cmu_stat)
+{
+    Clk **i, *cur;
+
+    for (i = onchip_clks; *i; i++) {
+        cur = *i;
+
+        cur->div_val  = cur->div_val ?: 1;
+        cur->mult_val = cur->mult_val ?: 1;
+
+        /* update all divisors using div_shift if any,
+         * divisors for (A,E,M,V)PLL are not updated here */
+        if (cur->div_shift > NONE)
+            cur->div_val =
+                (cmu_stat->clk_div[cur->div_reg_num]  >> cur->div_shift & 0xf) + 1;
+
+        /* update frequencies for all the clocks except the oscillators */
+        if (cur->parent)
+            cur->rate = muldiv64(cur->parent->rate, cur->mult_val, cur->div_val);
+    }
+}
+
+/* Set a frequency */
+static void s5pc1xx_clk_setrate(CmuStat *cmu_stat, Clk *clk,
+                                int divide, int multiply)
+{
+    clk->div_val  = divide;
+    clk->mult_val = multiply;
+}
+
+/* Set (A,M,E,V)PLL params after write operation */
+static void set_pll_conf(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    CmuStat *s = (CmuStat *)opaque;
+
+    /* Calculate control values depending on clock kind */
+    switch ((offset - 0x100) >> 3) {
+
+    case 0:
+        apll.enabled = (val >> 31) & 0x1;
+        /* include/exclude clock depending on .enabled value */
+        if (apll.enabled)
+            s->clk_src[0] |= (1 << sclk_apll.mux_shift);
+        else
+            s->clk_src[0] &= ~(1 << sclk_apll.mux_shift);
+
+        s5pc1xx_clk_setrate(s, &apll,
+                            ((val >> 8) & 0x3F) << ((val & 0x7) - 1),
+                            (val >> 16) & 0x3FF);
+        break;
+
+    case 1:
+        mpll.enabled = (val >> 31) & 0x1;
+        if (mpll.enabled)
+            s->clk_src[0] |= (1 << sclk_mpll.mux_shift);
+        else
+            s->clk_src[0] &= ~(1 << sclk_mpll.mux_shift);
+
+        s5pc1xx_clk_setrate(s, &mpll,
+                            ((val >> 8) & 0x3F) << (val & 0x7),
+                            (val >> 16) & 0x3FF);
+        break;
+
+    case 2:
+        epll.enabled = (val >> 31) & 0x1;
+        if (epll.enabled)
+            s->clk_src[0] |= (1 << sclk_epll.mux_shift);
+        else
+            s->clk_src[0] &= ~(1 << sclk_epll.mux_shift);
+
+        s5pc1xx_clk_setrate(s, &epll,
+                            ((val >> 8) & 0x3F) << (val & 0x7),
+                            (val >> 16) & 0x3FF);
+        break;
+
+    case 4:
+        vpll.enabled = (val >> 31) & 0x1;
+        if (vpll.enabled)
+            s->clk_src[0] |= (1 << sclk_vpll.mux_shift);
+        else
+            s->clk_src[0] &= ~(1 << sclk_vpll.mux_shift);
+
+        s5pc1xx_clk_setrate(s, &vpll,
+                            ((val >> 8) & 0x3F) << (val & 0x7),
+                            (val >> 16) & 0x3FF);
+        break;
+
+    default:
+        hw_error("s5pc1xx.clk: bad pll offset 0x" TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static void s5pc1xx_clk_config(CmuStat *s)
+{
+    set_pll_conf(s, APLL_CON, s->conf_data[0]);
+    set_pll_conf(s, MPLL_CON, s->conf_data[1]);
+    set_pll_conf(s, EPLL_CON, s->conf_data[2]);
+    set_pll_conf(s, VPLL_CON, s->conf_data[4]);
+
+    s5pc1xx_clk_reparent(s);
+    s5pc1xx_clk_rate_update(s);
+}
+
+static uint32_t register_read(void *opaque, target_phys_addr_t offset)
+{
+    CmuStat *s = (CmuStat *)opaque;
+
+    switch (offset) {
+    case 0x000 ... 0x020:
+        return s->lock_data[offset >> 3];
+    case 0x100 ... 0x120:
+        return s->conf_data[(offset - 0x100) >> 3];
+    case 0x200 ... 0x218:
+        return s->clk_src[(offset - 0x200) >> 2];
+    case 0x280 ... 0x284:
+        return s->src_mask[(offset - 0x280) >> 2];
+    case 0x300 ... 0x31C:
+        return s->clk_div[(offset - 0x300) >> 2];
+    case 0x400 ... 0x488:
+        return s->clk_gate[(offset - 0x400) >> 2];
+    case 0x500:
+        return s->clk_out;
+    case 0x1000:
+    case 0x1004:
+        return 0;
+    case 0x1100:
+        return
+            (1 << ((s->clk_src[0] >> ONENAND_SHIFT) & 0x1)) << ONENAND_SHIFT |
+            (1 << ((s->clk_src[0] >> MUX133_SHIFT) & 0x1)) << MUX133_SHIFT |
+            (1 << ((s->clk_src[0] >> MUX166_SHIFT) & 0x1)) << MUX166_SHIFT |
+            (1 << ((s->clk_src[0] >> MUX200_SHIFT) & 0x1)) << MUX200_SHIFT |
+            (1 << ((s->clk_src[0] >> VPLL_SHIFT) & 0x1)) << VPLL_SHIFT |
+            (1 << ((s->clk_src[0] >> EPLL_SHIFT) & 0x1)) << EPLL_SHIFT |
+            (1 << ((s->clk_src[0] >> MPLL_SHIFT) & 0x1)) << MPLL_SHIFT |
+            (1 << ((s->clk_src[0] >> APLL_SHIFT) & 0x1)) << APLL_SHIFT;
+    case 0x1104:
+        return
+            (1 << ((s->clk_src[6] >> MUX_HPM_SHIFT) & 0x1)) << MUX_HPM_SHIFT |
+            ((((s->clk_src[6] >> MUX_ONEDRAM_SHIFT) << 1) & 0x6) |
+             ((s->clk_src[6] >> MUX_ONEDRAM_SHIFT) & 0x1)) << 8 |
+            ((((s->clk_src[2] >> MFC_SHIFT) << 1) & 0x6) |
+             ((s->clk_src[2] >> MFC_SHIFT) & 0x1)) << MFC_SHIFT |
+            ((((s->clk_src[2] >> G3D_SHIFT) << 1) & 0x6) |
+             ((s->clk_src[2] >> G3D_SHIFT) & 0x1)) << G3D_SHIFT;
+    default:
+        hw_error("s5pc1xx.clk: bad read offset 0x" TARGET_FMT_plx "\n", offset);
+        return 0;
+    }
+}
+
+static void register_write(void *opaque, target_phys_addr_t offset,
+                           uint32_t val)
+{
+    CmuStat *s = (CmuStat *)opaque;
+
+    switch (offset) {
+    case 0x000 ... 0x020:
+        s->lock_data[offset >> 3] = val;
+        break;
+    case 0x100 ... 0x120:
+        /* LOCKED bit is always set */
+        s->conf_data[(offset - 0x100) >> 3] = val | (1 << 29);
+
+        set_pll_conf(s, offset, val);
+        s5pc1xx_clk_reparent(s);
+        s5pc1xx_clk_rate_update(s);
+
+        break;
+    case 0x200 ... 0x218:
+        s->clk_src[(offset - 0x200) >> 2] = val;
+
+        /* clear reserved bits for security */
+        s->clk_src[0] &= ALL_MUX_BITS_0;
+        s->clk_src[5] &= ALL_MUX_BITS_5;
+
+        s5pc1xx_clk_reparent(s);
+
+        break;
+    case 0x280 ... 0x284:
+        s->src_mask[(offset - 0x280) >> 2] = val;
+        break;
+    case 0x300 ... 0x31C:
+        s->clk_div[(offset - 0x300) >> 2] = val;
+
+        /* clear reserved bits for security */
+        s->clk_div[0] &= ALL_DIV_BITS_0;
+        s->clk_div[5] &= ALL_DIV_BITS_5;
+
+        s5pc1xx_clk_rate_update(s);
+
+        break;
+    case 0x400 ... 0x488:
+        s->clk_gate[(offset - 0x400) >> 2] = val;
+        break;
+    case 0x500:
+        s->clk_out = val;
+        break;
+    case 0x1000 ... 0x1004:
+    case 0x1100 ... 0x1104:
+    default:
+        hw_error("s5pc1xx.clk: bad write offset 0x" TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc * const register_readfn[] = {
+   register_read,
+   register_read,
+   register_read
+};
+
+static CPUWriteMemoryFunc * const register_writefn[] = {
+   register_write,
+   register_write,
+   register_write
+};
+
+static void s5pc1xx_clk_save(QEMUFile *f, void *opaque)
+{
+    CmuStat *s = (CmuStat *)opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->clk_out);
+
+    for (i = 0; i < 23; i++) {
+        qemu_put_be32s(f, s->clk_gate + i);
+        if (i > 7) {
+            continue;
+        }
+        qemu_put_be32s(f, s->clk_div + i);
+        if (i > 6) {
+            continue;
+        }
+        qemu_put_be32s(f, s->clk_src + i);
+        if (i > 4) {
+            continue;
+        }
+        qemu_put_be32s(f, s->lock_data + i);
+        qemu_put_be32s(f, s->conf_data + i);
+        if (i > 1) {
+            continue;
+        }
+        qemu_put_be32s(f, s->src_mask + i);
+        qemu_put_be32s(f, s->div_stat + i);
+        qemu_put_be32s(f, s->mux_stat + i);
+    }
+}
+
+static int s5pc1xx_clk_load(QEMUFile *f, void *opaque, int version_id)
+{
+    CmuStat *s = (CmuStat *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->clk_out);
+
+    for (i = 0; i < 23; i++) {
+        qemu_get_be32s(f, s->clk_gate + i);
+        if (i > 7) {
+            continue;
+        }
+        qemu_get_be32s(f, s->clk_div + i);
+        if (i > 6) {
+            continue;
+        }
+        qemu_get_be32s(f, s->clk_src + i);
+        if (i > 4) {
+            continue;
+        }
+        qemu_get_be32s(f, s->lock_data + i);
+        qemu_get_be32s(f, s->conf_data + i);
+        if (i > 1) {
+            continue;
+        }
+        qemu_get_be32s(f, s->src_mask + i);
+        qemu_get_be32s(f, s->div_stat + i);
+        qemu_get_be32s(f, s->mux_stat + i);
+    }
+    s5pc1xx_clk_config(s);
+    return 0;
+}
+
+/* Initialize clock */
+static int s5pc1xx_clk_init(SysBusDevice *dev)
+{
+    CmuStat *s = FROM_SYSBUS(CmuStat, dev);
+    int iomemtype;
+
+    s5pc1xx_clk_reset(s);
+    s5pc1xx_clk_config(s);
+
+    /* memory mapping */
+    iomemtype = cpu_register_io_memory(register_readfn, register_writefn, s,
+                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_CLK_REG_MEM_SIZE, iomemtype);
+
+    qemu_register_reset(s5pc1xx_clk_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.clk", -1, 1,
+                    s5pc1xx_clk_save, s5pc1xx_clk_load, s);
+    return 0;
+}
+
+static void s5pc1xx_clk_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.clk", sizeof(CmuStat), s5pc1xx_clk_init);
+}
+
+device_init(s5pc1xx_clk_register_devices)
diff --git a/hw/s5pc1xx_debug.c b/hw/s5pc1xx_debug.c
new file mode 100644 (file)
index 0000000..334f1fb
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * S5PC110 Test & Debug
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+/* Print all system information to stderr */
+static __attribute__((unused)) void debug_sysinfo(const struct s5pc1xx_state_s *s)
+{
+    fprintf(stderr, "CPU: %s\n", s->env->cpu_model_str);
+    fprintf(stderr, "SDRAM: %lu MB\n", s->sdram_size / (1024 * 1024));
+    fprintf(stderr, "SRAM: %lu KB\n", s->isram_size / 1024);
+    fprintf(stderr, "SROM: %lu KB\n", s->isrom_size / 1024);
+}
+
+
+/* Interrupt Controller */
+
+#define ADDR    0xF2000F00
+
+
+target_phys_addr_t vic_base[] = {
+    S5PC1XX_VIC0_BASE, S5PC1XX_VIC1_BASE, S5PC1XX_VIC2_BASE, S5PC1XX_VIC3_BASE
+};
+
+typedef enum {
+    enable, disable, swint, swclear, select_irq, selclear, priomask, daisyprio
+} irq_op;
+
+
+static __attribute__((unused)) void test_irq_handler(void *opaque, int irq,
+                                                     int level)
+{
+    const char *name;
+
+    if (irq)
+        name = "FIQ";
+    else
+        name = "IRQ";
+
+    if (level)
+        fprintf(stderr, "%s was raised\n", name);
+}
+
+static __attribute__((unused)) qemu_irq *test_irq_init(void)
+{
+    return qemu_allocate_irqs(test_irq_handler, NULL, 2);
+}
+
+static __attribute__((unused)) uint32_t test_irq_op(irq_op op, int irq,
+                                                    int is_wr, uint32_t val)
+{
+    int vic_id = irq / S5PC1XX_VIC_SIZE;
+    uint32_t res;
+    target_phys_addr_t base = vic_base[vic_id];
+    target_phys_addr_t off;
+
+    switch (op) {
+        case enable:
+            off = 0x10;
+            break;
+        case disable:
+            off = 0x14;
+            break;
+        case swint:
+            off = 0x18;
+            break;
+        case swclear:
+            off = 0x1C;
+            break;
+        case select_irq:
+        case selclear:
+            off = 0xC;
+            break;
+        case priomask:
+            off = 0x24;
+            break;
+        case daisyprio:
+            off = 0x28;
+            break;
+        default:
+            off = 0x0;
+            break;
+    }
+    if (op == priomask || op == daisyprio || !is_wr) {
+        if (is_wr) {
+            cpu_physical_memory_write(base + off, (uint8_t *)&val, 4);
+        } else {
+            cpu_physical_memory_read(base + off, (uint8_t *)&res, 4);
+        }
+        return res;
+    }
+    if (op == select_irq || op == selclear)
+        cpu_physical_memory_read(base + off, (uint8_t *)&res, 4);
+    if (op == select_irq)
+        res |= 1 << (irq % S5PC1XX_VIC_SIZE);
+    else if (op == selclear)
+        res &= ~(1 << (irq % S5PC1XX_VIC_SIZE));
+    else
+        res = 1 << (irq % S5PC1XX_VIC_SIZE);
+    cpu_physical_memory_write(base + off, (uint8_t *)&res, 4);
+    return 0;
+}
+
+static __attribute__((unused)) void test_irq_script(struct s5pc1xx_state_s *s)
+{
+    uint32_t res;
+
+    fprintf(stderr,"Step 1: Interrupts disabled. Raising and lowering them.\n");
+    qemu_irq_raise(s5pc1xx_get_irq(s, 14));
+    qemu_irq_raise(s5pc1xx_get_irq(s, 33));
+    qemu_irq_lower(s5pc1xx_get_irq(s, 14));
+    qemu_irq_lower(s5pc1xx_get_irq(s, 33));
+    qemu_irq_raise(s5pc1xx_get_irq(s, 69));
+    qemu_irq_lower(s5pc1xx_get_irq(s, 69));
+    qemu_irq_raise(s5pc1xx_get_irq(s, 101));
+
+    fprintf(stderr, "Step 2: Interrupt 101 is raised. Enable some other.\n");
+    test_irq_op(enable, 4, 1, 0);
+    test_irq_op(enable, 34, 1, 0);
+    test_irq_op(enable, 5, 1, 0);
+
+    fprintf(stderr, "Step 3: Interrupt 101 is raised. Enable it.\n");
+    res = 0xDDEEAABB;
+    cpu_physical_memory_write(0xF2300114, (const uint8_t *)&res, 4);
+    test_irq_op(enable, 101, 1, 0);
+    cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+    fprintf(stderr, "Interrupt 101 vector is %x\n", res);
+    qemu_irq_raise(s5pc1xx_get_irq(s, 5));
+    fprintf(stderr, "Step 4: Interrupt 101 has been acknoledged. "
+                    "Interrupt 5 has been raised.\n");
+
+    fprintf(stderr, "Step 5: Generate IRQ 4 with higher priority.\n");
+    res = 0xa;
+    cpu_physical_memory_write(0xF2000210, (const uint8_t *)&res, 4);
+    res = 0xDDEEBBAA;
+    cpu_physical_memory_write(0xF2000110, (const uint8_t *)&res, 4);
+    qemu_irq_raise(s5pc1xx_get_irq(s, 4));
+
+    fprintf(stderr, "Step 6: Acknoledge IRQ 4. Then lower it.\n");
+    cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+    fprintf(stderr, "Interrupt 4 vector is %x\n", res);
+    qemu_irq_lower(s5pc1xx_get_irq(s, 4));
+
+    fprintf(stderr, "Step 7: Finalize IRQ 4 processing. No new interrupts "
+                    "should appear as IRQ 101 is in progress.\n");
+    cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+
+    fprintf(stderr, "Step 8: Mask IRQ 4's priority, then raise it again.\n");
+    test_irq_op(priomask, 0, 1, 0xffff & ~(1 << 0xa));
+    qemu_irq_raise(s5pc1xx_get_irq(s, 4));
+
+    fprintf(stderr, "Step 9: Finalize IRQ 101 processing. "
+                    "We should recive IRQ 5.\n");
+    res = 0xDDEEBBCC;
+    cpu_physical_memory_write(0xF2000114, (const uint8_t *)&res, 4);
+    qemu_irq_lower(s5pc1xx_get_irq(s, 101));
+    cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+    cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+    fprintf(stderr, "Interrupt 5 vector is %x\n", res);
+
+    fprintf(stderr, "Step 10: Finalize IRQs. Clear them all.\n");
+    qemu_irq_lower(s5pc1xx_get_irq(s, 5));
+    qemu_irq_lower(s5pc1xx_get_irq(s, 4));
+    cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+}
+
+
+/* DMA */
+
+#define DATA_ADDR       0x20010000
+#define RESULT_ADDR     0x20020000
+#define PROG_ADDR       0x20030000
+
+#define DBGGO_ADDR      0xFA200D04
+#define DBG0_ADDR       0xFA200D08
+#define DBG1_ADDR       0xFA200D0C
+
+#define FSC_ADDR        0xFA200034
+#define FTC1_ADDR       0xFA200044
+#define CPC1_ADDR       0xFA20010C
+
+
+uint32_t dbg0 = 0x01A00000;
+uint32_t dbg1 = PROG_ADDR;
+uint32_t dbggo = 0x0;
+uint32_t dbgkill = 0x00010101;
+
+static const uint8_t dma_data[] = {
+    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+   11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+   21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+   31, 32
+};
+
+static const uint8_t dma_stz[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV   CCR SAI SS4 SB4 DAI DS4 DB4 */
+    0x0C,                               /* DMASTZ   */
+    0x00                                /* DMAEND   */
+};
+
+static const uint8_t dma_stzlp[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV   CCR SAI SS4 SB4 DAI DS4 DB4 */
+    0x20, 0x01,                         /* DMALP    2 */
+    0x0C,                               /* DMASTZ   */
+    0x38, 0x01,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+static const uint8_t dma_copy[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV   CCR SAI SS4 SB4 DAI DS4 DB4 */
+    0x20, 0x01,                         /* DMALP    2 */
+    0x04,                               /* DMALD    */
+    0x08,                               /* DMAST    */
+    0x38, 0x02,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+/* Paradoxically but this should work correctly too. */
+static const uint8_t dma_copy_2[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV   CCR SAI SS4 SB4 DAI DS4 DB4 */
+    0x20, 0x01,                         /* DMALP    2 */
+    0x08,                               /* DMAST    */
+    0x04,                               /* DMALD    */
+    0x38, 0x02,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+static const uint8_t dma_scatter[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x34, 0x40, 0x0D, 0x00, /* DMAMOV   CCR SAF SS4 SB4 DAI DS4 DB4 */
+    0x20, 0x01,                         /* DMALP    2 */
+    0x04,                               /* DMALD    */
+    0x08,                               /* DMAST    */
+    0x38, 0x02,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+static const uint8_t dma_gather[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x35, 0x00, 0x0D, 0x00, /* DMAMOV   CCR SAI SS4 SB4 DAF DS4 DB4 */
+    0x20, 0x01,                         /* DMALP    2 */
+    0x04,                               /* DMALD    */
+    0x08,                               /* DMAST    */
+    0x38, 0x02,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+/* Watchdog abort at DMAEND */
+static const uint8_t dma_nold[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV   CCR SAF SS4 SB4 DAF DS4 DB4 */
+    0x08,                               /* DMAST    */
+    0x18,                               /* DMANOP   */
+    0x00                                /* DMAEND   */
+};
+
+/* Watchdog abort at DMAEND */
+static const uint8_t dma_nost[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV   CCR SAF SS4 SB4 DAF DS4 DB4 */
+    0x04,                               /* DMALD    */
+    0x18,                               /* DMANOP   */
+    0x00                                /* DMAEND   */
+};
+
+/* Watchdog abort at DMALD */
+static const uint8_t dma_ldfe[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV   CCR SAF SS4 SB4 DAF DS4 DB4 */
+                                        /* DMALPFE  */
+    0x04,                               /* DMALD    */
+    0x28, 0x01,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+/* Watchdog abort at DMAST */
+static const uint8_t dma_stfe[] = {
+    0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV   SAR 0x20010000 */
+    0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV   DAR 0x20020000 */
+    0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV   CCR SAF SS4 SB4 DAF DS4 DB4 */
+                                        /* DMALPFE  */
+    0x08,                               /* DMAST    */
+    0x28, 0x01,                         /* DMALPEND */
+    0x00                                /* DMAEND   */
+};
+
+
+static inline void dma_exec_dbg(const uint8_t *prog, int size)
+{
+    cpu_physical_memory_write(PROG_ADDR, prog, size);
+    cpu_physical_memory_write(DBG0_ADDR, (uint8_t *)&dbg0, 4);
+    cpu_physical_memory_write(DBG1_ADDR, (uint8_t *)&dbg1, 4);
+    cpu_physical_memory_write(DBGGO_ADDR, (uint8_t *)&dbggo, 4);
+}
+
+static inline void dma_kill_dbg(void)
+{
+    uint32_t zeroval = 0x0;
+
+    cpu_physical_memory_write(DBG0_ADDR, (uint8_t *)&dbgkill, 4);
+    cpu_physical_memory_write(DBG1_ADDR, (uint8_t *)&zeroval, 4);
+    cpu_physical_memory_write(DBGGO_ADDR, (uint8_t *)&dbggo, 4);
+}
+
+static __attribute__((unused)) void test_dma_script(struct s5pc1xx_state_s *s)
+{
+    uint8_t res[32];
+    uint32_t reg;
+    int outcome;
+    int i;
+
+    cpu_physical_memory_write(DATA_ADDR, dma_data, 32);
+
+    /* TEST 1 */
+    cpu_physical_memory_write(RESULT_ADDR, dma_data, 32);
+    dma_exec_dbg(dma_stz, 20);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 16);
+    for (i = 0; i < 16; i++) {
+        if (res[i] != 0) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 1: DMASTZ. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 1: DMASTZ. OK\n");
+    }
+
+    /* TEST 2 */
+    cpu_physical_memory_write(RESULT_ADDR, dma_data, 32);
+    dma_exec_dbg(dma_stzlp, 24);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 32);
+    for (i = 0; i < 32; i++) {
+        if (res[i] != 0) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 2: DMASTZ in loop. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 2: DMASTZ in loop. OK\n");
+    }
+
+    /* TEST 3 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_copy, 25);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 32);
+    for (i = 0; i < 32; i++) {
+        if (res[i] != dma_data[i]) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 3: DMA copy of 32 bytes of data. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 3: DMA copy of 32 bytes of data. OK\n");
+    }
+
+    /* TEST 4 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_copy_2, 25);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 32);
+    for (i = 0; i < 32; i++) {
+        if (res[i] != dma_data[i]) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 4: DMA copy of 32 bytes of data (store before load). FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 4: DMA copy of 32 bytes of data (store before load). OK\n");
+    }
+
+    /* TEST 5 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_scatter, 25);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 32);
+    for (i = 0; i < 32; i++) {
+        if (res[i] != dma_data[i % 4]) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 5: DMA scatter. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 5: DMA scatter. OK\n");
+    }
+
+    /* TEST 6 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_gather, 25);
+    outcome = 0;
+    cpu_physical_memory_read(RESULT_ADDR, res, 32);
+    for (i = 0; i < 32; i++) {
+        if (res[i] != ((i > 3) ? 0 : dma_data[28 + i])) {
+            outcome = 1;
+        }
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 6: DMA gather. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 6: DMA gather. OK\n");
+    }
+
+    /* TEST 7 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_nost, 21);
+    outcome = 0;
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (! (reg & 2)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != ((unsigned)1 << 31)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != PROG_ADDR + 20) {
+        outcome = 1;
+    }
+    dma_kill_dbg();
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (reg & 2) {
+        outcome = 1;
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 7: DMALD without DMAST. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 7: DMALD without DMAST. OK\n");
+    }
+
+    /* TEST 8 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_nold, 21);
+    outcome = 0;
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (! (reg & 2)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != ((unsigned)1 << 31)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != PROG_ADDR + 20) {
+        outcome = 1;
+    }
+    dma_kill_dbg();
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (reg & 2) {
+        outcome = 1;
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 8: DMAST without DMALD. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 8: DMAST without DMALD. OK\n");
+    }
+
+    /* TEST 9 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_ldfe, 22);
+    outcome = 0;
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (! (reg & 2)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != ((unsigned)1 << 31)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != PROG_ADDR + 18) {
+        outcome = 1;
+    }
+    dma_kill_dbg();
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (reg & 2) {
+        outcome = 1;
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 9: DMALD in infinite loop. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 9: DMALD in infinite loop. OK\n");
+    }
+
+    /* TEST 10 */
+    dma_exec_dbg(dma_stzlp, 24);
+    dma_exec_dbg(dma_stfe, 22);
+    outcome = 0;
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (! (reg & 2)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != ((unsigned)1 << 31)) {
+        outcome = 1;
+    }
+    cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)&reg, 4);
+    if (reg != PROG_ADDR + 18) {
+        outcome = 1;
+    }
+    dma_kill_dbg();
+    cpu_physical_memory_read(FSC_ADDR, (uint8_t *)&reg, 4);
+    if (reg & 2) {
+        outcome = 1;
+    }
+    if (outcome) {
+        fprintf(stderr, "DMA test 10: DMAST in infinite loop. FAILED\n");
+    } else {
+        fprintf(stderr, "DMA test 10: DMAST in infinite loop. OK\n");
+    }
+}
+
+
+/* UART */
+
+#define TRSTATUS_ADDR   0xE2900810
+#define FIFOCTL_ADDR    0xE2900808
+#define TRANSMIT_ADDR   0xE2900820
+#define RECIVE_ADDR     0xE2900824
+
+
+static const char *hello = "Hello world!\n";
+static char buf[256];
+
+
+static __attribute__((unused)) void test_uart_script(void)
+{
+    uint32_t res;
+    char *s;
+    int i;
+
+    res = 1;
+    cpu_physical_memory_write(FIFOCTL_ADDR, (uint8_t *)&res, 4);
+    cpu_physical_memory_read(TRSTATUS_ADDR, (uint8_t *)&res, 4);
+    if (! (res & 4)) {
+        fprintf(stderr, "Error: UART2 transmitter is not ready!\n");
+    }
+    s = (char *)hello;
+    while (*s) {
+        cpu_physical_memory_write(TRANSMIT_ADDR, (uint8_t *)s, 1);
+        s++;
+    }
+    sleep(10);
+    s = buf;
+    i = 0;
+    while (1) {
+        cpu_physical_memory_read(TRSTATUS_ADDR, (uint8_t *)&res, 4);
+        if (! (res & 1)) {
+            break;
+        }
+        if (i >= 255) {
+            fprintf(stderr, "Error: UART2 too many input data!\n");
+            break;
+        }
+        cpu_physical_memory_read(RECIVE_ADDR, (uint8_t *)s, 1);
+        s++;
+    }
+    *s = '\0';
+    fprintf (stderr, "Read data: %s\n", s);
+}
+
diff --git a/hw/s5pc1xx_gpio.c b/hw/s5pc1xx_gpio.c
new file mode 100644 (file)
index 0000000..19806b8
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * GPIO controller for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define GPIO_CONF_CASE 6
+#define GPIO_PIN_CONF(s, group, pin) (((s)->con[(group)] >> ((pin) * 4)) & 0xF)
+#define S5PC1XX_GPIO_REG_MEM_SIZE 0xF84
+
+
+typedef struct S5pc1xxGPIOState {
+    SysBusDevice busdev;
+
+    /* Port Registers */
+    uint32_t con[GPH3 + 1];
+
+    /* Interrupt GPIO Registers
+     * GPI has no interrupts so corresponding field is not used */
+    uint32_t int_con[GPJ4 + 1];
+    uint32_t int_fltcon[GPJ4 + 1][2];
+    uint32_t int_mask[GPJ4 + 1];
+    uint32_t int_pend[GPJ4 + 1];
+    uint32_t int_fixpri[GPJ4 + 1];
+
+    uint32_t int_grppri;
+    uint32_t int_priority;
+    uint32_t int_ser;
+    uint32_t int_ser_pend;
+    uint32_t int_grpfixpri;
+
+    /* Extended Interrupt Registers */
+    uint32_t ext_int_mask[4];
+    uint32_t ext_int_pend[4];
+
+    /* Parent Interrupt for GPIO interrupts */
+    qemu_irq gpioint;
+    /* Parent Extended Interrupt */
+    qemu_irq extend;
+} S5pc1xxGPIOState;
+
+
+int s5pc1xx_gpio_case[GPH3 + 1][8][7] = {
+
+/* GPA0 */      {{GPIO_UART_RXD(0)},   {GPIO_UART_TXD(0)},     {GPIO_UART_CTS(0)},     {GPIO_UART_RTS(0)},
+                 {GPIO_UART_RXD(1)},   {GPIO_UART_TXD(1)},     {GPIO_UART_CTS(1)},     {GPIO_UART_RTS(1)}},
+
+/* GPA1 */      {{GPIO_UART_RXD(2), 0, GPIO_UART_AUDIO_RXD},   {GPIO_UART_TXD(2), 0, GPIO_UART_AUDIO_TXD},
+                 {GPIO_UART_RXD(3), 0, GPIO_UART_CTS(2)},      {GPIO_UART_TXD(3), 0, GPIO_UART_RTS(2)}},
+/* not
+ * covered */   {},
+
+/* GPC0 */      {{0, PCM_SCLK(1), GPIO_AC97BITCLK},  {0, PCM_EXTCLK(1), GPIO_AC97RESETn},    {0, PCM_FSYNC(1), GPIO_AC97SYNC},
+                 {0, PCM_SIN(1), GPIO_AC97SDI},      {0, PCM_SOUT(1), GPIO_AC97SDO}},
+
+/* GPC1 */      {{PCM_SCLK(0), SPDIF_0_OUT},    {PCM_EXTCLK(0), SPDIF_EXTCLK},
+                 {PCM_FSYNC(0), LCD_FRM},       {PCM_SIN(0)},                       {PCM_SOUT(0)}},
+
+/* GPD0 */      {{GPIO_PWM_TOUT(0)},   {GPIO_PWM_TOUT(1)},     {GPIO_PWM_TOUT(2)},     {GPIO_PWM_TOUT(3), PWM_MIE_MDNI}},
+
+/* not
+ * covered */   {}, {}, {},
+
+/* GPF0 */      {{LCD_HSYNC},   {LCD_VSYNC},    {LCD_VDEN},     {LCD_VCLK},
+                 {LCD_VD(0)},   {LCD_VD(1)},    {LCD_VD(2)},    {LCD_VD(3)}},
+
+/* GPF1 */      {{LCD_VD(4)},   {LCD_VD(5)},    {LCD_VD(6)},    {LCD_VD(7)},
+                 {LCD_VD(8)},   {LCD_VD(9)},    {LCD_VD(10)},   {LCD_VD(11)}},
+
+/* GPF2 */      {{LCD_VD(12)},  {LCD_VD(13)},   {LCD_VD(14)},   {LCD_VD(15)},
+                 {LCD_VD(16)},  {LCD_VD(17)},   {LCD_VD(18)},   {LCD_VD(19)}},
+
+/* GPF3 */      {{LCD_VD(20)},  {LCD_VD(21)},   {LCD_VD(22)},   {LCD_VD(23)}},
+
+/* not
+ * covered */   {}, {}, {}, {},
+
+/* GPI */       {{0, PCM_SCLK(2)}, {0, PCM_EXTCLK(2)}, {0, PCM_FSYNC(2)}, {0, PCM_SIN(2)}, {0, PCM_SOUT(2)}},
+
+/* not
+ * covered */   {},
+
+/* GPJ1 */      {{}, {}, {}, {}, {0, GPIO_KP_COL(0)}},
+
+/* GPJ2 */      {{0, GPIO_KP_COL(1)},   {0, GPIO_KP_COL(2)},    {0, GPIO_KP_COL(3)},    {0, GPIO_KP_COL(4)},
+                 {0, GPIO_KP_COL(5)},   {0, GPIO_KP_COL(6)},    {0, GPIO_KP_COL(7)},    {0, GPIO_KP_ROW(0)}},
+
+/* GPJ3 */      {{0, GPIO_KP_ROW(1), 0, 0, GPIO_I2C_SDA(10), 0, GPIO_I2C_SDA(10)},
+                 {0, GPIO_KP_ROW(2), 0, 0, GPIO_I2C_SCL(10), 0, GPIO_I2C_SCL(10)},
+                 {0, GPIO_KP_ROW(3)},
+                 {0, GPIO_KP_ROW(4)},   {0, GPIO_KP_ROW(5)},    {0, GPIO_KP_ROW(6)},
+                 {0, GPIO_KP_ROW(7), 0, 0, GPIO_I2C_SDA(3), 0, GPIO_I2C_SDA(3)},
+                 {0, GPIO_KP_ROW(8), 0, 0, GPIO_I2C_SCL(3), 0, GPIO_I2C_SCL(3)}},
+
+/* GPJ4 */      {{0, GPIO_KP_ROW(9), 0, 0, GPIO_I2C_SDA(4), 0, GPIO_I2C_SDA(4)},
+                 {0, GPIO_KP_ROW(10)},   {0, GPIO_KP_ROW(11)},
+                 {0, GPIO_KP_ROW(12), 0, 0, GPIO_I2C_SCL(4), 0, GPIO_I2C_SCL(4)},
+                 {0, GPIO_KP_ROW(13)}},
+/* not
+ * covered */   {}, {}, {}, {},
+
+/* MP0_5 */     {{}, {}, {0, 0, 0, 0, GPIO_I2C_SCL(5), 0, GPIO_I2C_SCL(5)}, {0, 0, 0, 0, GPIO_I2C_SDA(5), 0, GPIO_I2C_SDA(5)},
+                 {}, {}, {}, {}},
+/* not
+* covered */    {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+                {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+                {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+
+/* GPH2 */      {{0, GPIO_KP_COL(0)},   {0, GPIO_KP_COL(1)},    {0, GPIO_KP_COL(2)},    {0, GPIO_KP_COL(3)},
+                 {0, GPIO_KP_COL(4)},   {0, GPIO_KP_COL(5)},    {0, GPIO_KP_COL(6)},    {0, GPIO_KP_COL(7)}},
+
+/* GPH3 */      {{0, GPIO_KP_ROW(0)},   {0, GPIO_KP_ROW(1)},    {0, GPIO_KP_ROW(2)},    {0, GPIO_KP_ROW(3)},
+                 {0, GPIO_KP_ROW(4)},   {0, GPIO_KP_ROW(5)},    {0, GPIO_KP_ROW(6)},    {0, GPIO_KP_ROW(7)}},
+};
+
+
+/* IO Memory Support */
+
+GPIOWriteMemoryFunc *gpio_io_mem_write[GPIO_IDX_MAX];
+GPIOReadMemoryFunc *gpio_io_mem_read[GPIO_IDX_MAX];
+GPIOWriteMemoryFunc *gpio_io_mem_conf[GPIO_IDX_MAX] = {NULL};
+void *gpio_io_mem_opaque[GPIO_IDX_MAX][8];
+
+
+void s5pc1xx_gpio_register_io_memory(int io_index, int instance,
+                                     GPIOReadMemoryFunc *mem_read,
+                                     GPIOWriteMemoryFunc *mem_write,
+                                     GPIOWriteMemoryFunc *mem_conf,
+                                     void *opaque)
+{
+    gpio_io_mem_read[io_index]  = mem_read;
+    gpio_io_mem_write[io_index] = mem_write;
+    gpio_io_mem_conf[io_index]  = mem_conf;
+    gpio_io_mem_opaque[io_index][instance] = opaque;
+}
+
+uint32_t s5pc1xx_empty_gpio_read(void *opaque, int io_index)
+{
+    return 0;
+}
+
+void s5pc1xx_empty_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+}
+
+/* Empty read and write functions references are used as plugs
+ * for the elements gpio_io_mem_read[0] and gpio_io_mem_write[0] */
+static GPIOReadMemoryFunc *s5pc1xx_empty_gpio_readfn   = s5pc1xx_empty_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_empty_gpio_writefn = s5pc1xx_empty_gpio_write;
+
+
+/* GPIO General Logic */
+
+/* GPIO Reset Function */
+static void s5pc1xx_gpio_reset(void *opaque)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    unsigned int group;
+
+    /* Note: no groups from ETC2 (50) till GPH0 (96) */
+    for (group = GPA0;
+         group <= GPH3;
+         group = (group == ETC2) ? GPH0 : group + 1) {
+        s->con[group] = 0x0;
+    }
+
+    for (group = 0; group < 4; group++) {
+        s->ext_int_mask[group] = 0xFF;
+        s->ext_int_pend[group] = 0x0;
+    }
+
+    for (group = GPA0; group <= GPJ4; group++) {
+        s->int_con[group] = 0x0;
+        s->int_fltcon[group][0] = 0x0;
+        s->int_fltcon[group][1] = 0x0;
+        s->int_mask[group] = 0xFF; /* other values in documentation */
+        s->int_pend[group] = 0x0;
+        s->int_fixpri[group] = 0x0;
+    }
+
+    s->int_grppri    = 0x0;
+    s->int_priority  = 0x0;
+    s->int_ser       = 0x0;
+    s->int_ser_pend  = 0x0;
+    s->int_grpfixpri = 0x0;
+}
+
+static void s5pc1xx_gpio_irq_lower(S5pc1xxGPIOState *s,
+                                   unsigned int group, uint32_t pinmask)
+{
+    unsigned int i = 0;
+    s->int_pend[group] &= ~pinmask;
+    for (i = GPA0; i <= GPJ4; i++) {
+        if (s->int_pend[i]) {
+            return;
+        }
+    }
+
+    qemu_irq_lower(s->gpioint);
+}
+
+static void s5pc1xx_gpio_extended_irq_lower(S5pc1xxGPIOState *s,
+                                            unsigned int group, uint32_t pinmask)
+{
+    unsigned int i = 0;
+    s->ext_int_pend[group] &= ~pinmask;
+    for (i = 0; i < 4; i++)
+        if (s->ext_int_pend[i])
+            return;
+
+    qemu_irq_lower(s->extend);
+}
+
+static void s5pc1xx_gpio_irq_handler(void *opaque, int irq, int level)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    unsigned int group = GPIOINT_GROUP(irq);
+    unsigned int pin   = GPIOINT_PIN  (irq);
+
+    /* Special case of extended IRQs */
+    if (irq < IRQ_EXTEND_NUM) {
+        group = irq >> 3;
+        pin   = irq & 0x7;
+
+        /* FIXME: IRQs 0~15 are not supported */
+        if (irq < 16)
+            hw_error("s5pc1xx.gpio: "
+                     "extended IRQs 0-15 through GPIO are not supported");
+
+        if (s->ext_int_mask[group] & (1 << pin))
+            return;
+
+        if (s->ext_int_pend[group] & (1 << pin))
+            return;
+
+        if (level) {
+            s->ext_int_pend[group] |= (1 << pin);
+            qemu_irq_raise(s->extend);
+        } else {
+            s5pc1xx_gpio_extended_irq_lower(s, group, (1 << pin));
+        }
+
+        return;
+    }
+
+    if (GPIO_PIN_CONF(s, group, pin) != GIPIO_CONF_INT)
+        return;
+
+    if (s->int_mask[group] & (1 << pin))
+        return;
+
+    /* if the interrupt has already happened */
+    if (s->int_pend[group] & (1 << pin))
+        return;
+
+    if (level) {
+        s->int_pend[group] |= (1 << pin);
+        qemu_irq_raise(s->gpioint);
+    } else {
+        s5pc1xx_gpio_irq_lower(s, group, (1 << pin));
+    }
+}
+
+/* GPIO Read Function */
+static uint32_t s5pc1xx_gpio_read(void *opaque,
+                                  target_phys_addr_t offset)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    uint32_t value = 0, ret_val;
+    int index, device, instance, io_index;
+    unsigned int group, pin, n;
+    int gpio_case;
+
+    group = offset / STEP;
+
+    if ((group <= ETC2) || ((group >= GPH0) && (group <= GPH3))) {
+
+        if (offset % STEP == 0x00)
+            return s->con[group];
+
+        if (offset % STEP == 0x04) {
+            ret_val = 0;
+
+            for (pin = 0; pin < 8; pin++) {
+                index = (s->con[group] >> pin * 4) & 0xF;
+                switch (index) {
+                    case 0x1:
+                        /* Input port can't be read */
+                        break;
+                    case 0x0:
+                    case 0x2:
+                    case 0x3:
+                    case 0x4:
+                    case 0x5:
+                        gpio_case = s5pc1xx_gpio_case[group][pin][CASE(index)];
+                        if (gpio_case) {
+                            device   = gpio_case >> 11 & 0x1F;
+                            instance = gpio_case >>  6 & 0x1F;
+                            io_index = gpio_case >>  0 & 0x3F;
+
+                            if (!(gpio_io_mem_read[device]))
+                                device = 0;
+
+                            value = (gpio_io_mem_read[device])
+                                      (gpio_io_mem_opaque[device][instance],
+                                       io_index);
+                        }
+                        break;
+                    case 0xF:
+                        /* TODO: implement this case */
+                        break;
+                    default:
+                        hw_error("s5pc1xx.gpio: "
+                                 "bad pin configuration index 0x%X\n", index);
+                }
+                ret_val |= (value & 0x1) << pin;
+            }
+            return ret_val;
+        }
+    }
+
+    if (offset >= INT_CON_BASE && offset < INT_CON_BASE + INT_REGS_SIZE)
+        return s->int_con[GET_GROUP_INT_CON(offset)];
+
+    if (offset >= INT_FLTCON_BASE &&
+        offset < INT_FLTCON_BASE + INT_REGS_SIZE * 2)
+        return s->int_fltcon[GET_GROUP_INT_FLTCON(offset)]
+                            [(offset & 0x4) >> 2];
+
+    if (offset >= INT_MASK_BASE && offset < INT_MASK_BASE + INT_REGS_SIZE)
+        return s->int_mask[GET_GROUP_INT_MASK(offset)];
+
+    if (offset >= INT_PEND_BASE && offset < INT_PEND_BASE + INT_REGS_SIZE)
+        return s->int_pend[GET_GROUP_INT_PEND(offset)];
+
+    if (offset >= INT_FIXPRI_BASE && offset < INT_FIXPRI_BASE + INT_REGS_SIZE)
+        return s->int_fixpri[GET_GROUP_INT_FIXPRI(offset)];
+
+    switch (offset) {
+    case GPIO_INT_GRPPRI:
+        return s->int_grppri;
+    case GPIO_INT_PRIORITY:
+        return s->int_priority;
+    case GPIO_INT_SER:
+        return s->int_ser;
+    case GPIO_INT_SER_PEND:
+        return s->int_ser_pend;
+    case GPIO_INT_GRPFIXPRI:
+        return s->int_grpfixpri;
+    }
+
+    for (n = 0; n < 4; n++) {
+        /* EINT Mask Register */
+        if (offset == EXT_INT_MASK(n))
+            return s->ext_int_mask[n];
+        /* EINT Pend Register */
+        if (offset == EXT_INT_PEND(n))
+            return s->ext_int_pend[n];
+    }
+
+    return 0;
+}
+
+/* GPIO Write Function */
+static void s5pc1xx_gpio_write(void *opaque,
+                               target_phys_addr_t offset,
+                               uint32_t value)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    int index, device, instance, io_index;
+    unsigned int group, pin, n;
+    int gpio_case;
+
+    group = offset / STEP;
+
+    if ((group <= ETC2) || ((group >= GPH0) && (group <= GPH3))) {
+
+        if (offset % STEP == 0x00) {
+            for (pin = 0; pin < 8; pin++) {
+                int new_con = (value >> pin * 4) & 0xF;
+
+                gpio_case = s5pc1xx_gpio_case[group][pin][GPIO_CONF_CASE];
+                if (gpio_case) {
+                    device   = gpio_case >> 11 & 0x1F;
+                    instance = gpio_case >>  6 & 0x1F;
+                    io_index = gpio_case >>  0 & 0x3F;
+
+                    if (!(gpio_io_mem_conf[device]))
+                        device = 0;
+
+                    gpio_io_mem_conf[device](gpio_io_mem_opaque[device][instance],
+                                             io_index, new_con);
+                }
+            }
+            s->con[group] = value;
+            return;
+        }
+
+        if (offset % STEP == 0x04) {
+            for (pin = 0; pin < 8; pin++) {
+                index = (s->con[group] >> pin * 4) & 0xF;
+                switch (index) {
+                    case 0x0:
+                        /* Output port can't be written */
+                        break;
+                    case 0x1:
+                    case 0x2:
+                    case 0x3:
+                    case 0x4:
+                    case 0x5:
+                        gpio_case = s5pc1xx_gpio_case[group][pin][CASE(index)];
+                        if (gpio_case) {
+                            device   = gpio_case >> 11 & 0x1F;
+                            instance = gpio_case >>  6 & 0x1F;
+                            io_index = gpio_case >>  0 & 0x3F;
+
+                            if (!(gpio_io_mem_write[device]))
+                                device = 0;
+
+                            gpio_io_mem_write[device]
+                                (gpio_io_mem_opaque[device][instance],
+                                 io_index, (value >> pin) & 0x1);
+                        }
+                        break;
+                    case 0xF:
+                        /* TODO: implement this case */
+                        break;
+                    default:
+                        hw_error("s5pc1xx.gpio: "
+                                 "bad pin configuration index 0x%X\n", index);
+                }
+            }
+            return;
+        }
+    }
+
+    if (offset >= INT_CON_BASE && offset < INT_CON_BASE + INT_REGS_SIZE) {
+        s->int_con[GET_GROUP_INT_CON(offset)] = value;
+        return;
+    }
+
+    if (offset >= INT_FLTCON_BASE &&
+        offset < INT_FLTCON_BASE + INT_REGS_SIZE * 2) {
+        s->int_fltcon[GET_GROUP_INT_FLTCON(offset)][(offset & 0x4) >> 2] =
+            value;
+        return;
+    }
+
+    if (offset >= INT_MASK_BASE && offset < INT_MASK_BASE + INT_REGS_SIZE) {
+        s->int_mask[GET_GROUP_INT_MASK(offset)] = value;
+        return;
+    }
+
+    if (offset >= INT_PEND_BASE && offset < INT_PEND_BASE + INT_REGS_SIZE) {
+        group = GET_GROUP_INT_PEND(offset);
+        s5pc1xx_gpio_irq_lower(s, group, value);
+        return;
+    }
+
+    if (offset >= INT_FIXPRI_BASE && offset < INT_FIXPRI_BASE + INT_REGS_SIZE) {
+        s->int_fixpri[GET_GROUP_INT_FIXPRI(offset)] = value;
+        return;
+    }
+
+    switch (offset) {
+    case GPIO_INT_GRPPRI:
+        s->int_grppri = value;
+        return;
+    case GPIO_INT_PRIORITY:
+        s->int_priority = value;
+        return;
+    case GPIO_INT_SER:
+        s->int_ser = value;
+        return;
+    case GPIO_INT_SER_PEND:
+        s->int_ser_pend = value;
+        return;
+    case GPIO_INT_GRPFIXPRI:
+        s->int_grpfixpri = value;
+        return;
+    }
+
+    for (n = 0; n < 4; n++) {
+        /* EINT Mask Register */
+        if (offset == EXT_INT_MASK(n)) {
+            s->ext_int_mask[n] = value;
+            return;
+        }
+        /* EINT Pend Register */
+        if (offset == EXT_INT_PEND(n)) {
+            s5pc1xx_gpio_extended_irq_lower(s, group, value);
+            return;
+        }
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_gpio_readfn[] = {
+   s5pc1xx_gpio_read,
+   s5pc1xx_gpio_read,
+   s5pc1xx_gpio_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_gpio_writefn[] = {
+   s5pc1xx_gpio_write,
+   s5pc1xx_gpio_write,
+   s5pc1xx_gpio_write
+};
+
+static void s5pc1xx_gpio_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    int i;
+
+    for (i = GPA0; i <= GPH3; i = (i == ETC2) ? GPH0 : i + 1) {
+        qemu_put_be32s(f, s->con + i);
+        if (i <= GPJ4) {
+            qemu_put_be32s(f, s->int_con + i);
+            qemu_put_be32s(f, &s->int_fltcon[i][0]);
+            qemu_put_be32s(f, &s->int_fltcon[i][1]);
+
+            qemu_put_be32s(f, s->int_mask + i);
+            qemu_put_be32s(f, s->int_pend + i);
+            qemu_put_be32s(f, s->int_fixpri + i);
+        }
+    }
+
+    for (i = 0; i < 4; i++) {
+        qemu_put_be32s(f, s->ext_int_mask + i);
+        qemu_put_be32s(f, s->ext_int_pend + i);
+    }
+
+    qemu_put_be32s(f, &s->int_grppri);
+    qemu_put_be32s(f, &s->int_priority);
+    qemu_put_be32s(f, &s->int_ser);
+    qemu_put_be32s(f, &s->int_ser_pend);
+    qemu_put_be32s(f, &s->int_grpfixpri);
+}
+
+static int s5pc1xx_gpio_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (i = GPA0; i <= GPH3; i = (i == ETC2) ? GPH0 : i + 1) {
+        qemu_get_be32s(f, s->con + i);
+        if (i <= GPJ4) {
+            qemu_get_be32s(f, s->int_con + i);
+            qemu_get_be32s(f, &s->int_fltcon[i][0]);
+            qemu_get_be32s(f, &s->int_fltcon[i][1]);
+
+            qemu_get_be32s(f, s->int_mask + i);
+            qemu_get_be32s(f, s->int_pend + i);
+            qemu_get_be32s(f, s->int_fixpri + i);
+        }
+    }
+
+    for (i = 0; i < 4; i++) {
+        qemu_get_be32s(f, s->ext_int_mask + i);
+        qemu_get_be32s(f, s->ext_int_pend + i);
+    }
+
+    qemu_get_be32s(f, &s->int_grppri);
+    qemu_get_be32s(f, &s->int_priority);
+    qemu_get_be32s(f, &s->int_ser);
+    qemu_get_be32s(f, &s->int_ser_pend);
+    qemu_get_be32s(f, &s->int_grpfixpri);
+
+    return 0;
+}
+
+qemu_irq s5pc1xx_gpoint_irq(DeviceState *d, int irq_num)
+{
+    return qdev_get_gpio_in(d, irq_num);
+}
+
+/* GPIO Init Function */
+static int s5pc1xx_gpio_init(SysBusDevice *dev)
+{
+    S5pc1xxGPIOState *s = FROM_SYSBUS(S5pc1xxGPIOState, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->gpioint);
+    sysbus_init_irq(dev, &s->extend);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_gpio_readfn, s5pc1xx_gpio_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_GPIO_REG_MEM_SIZE, iomemtype);
+
+    /* First IRQ_EXTEND_NUM interrupts are for extended interrupts */
+    qdev_init_gpio_in(&dev->qdev, s5pc1xx_gpio_irq_handler,
+                      IRQ_EXTEND_NUM + MAX_PIN_IN_GROUP * (GPJ4 + 1));
+
+    s5pc1xx_gpio_register_io_memory(0, 0, s5pc1xx_empty_gpio_readfn,
+                                    s5pc1xx_empty_gpio_writefn,
+                                    s5pc1xx_empty_gpio_writefn, NULL);
+    s5pc1xx_gpio_reset(s);
+    qemu_register_reset(s5pc1xx_gpio_reset, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.gpio", -1, 1,
+                    s5pc1xx_gpio_save, s5pc1xx_gpio_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_gpio_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.gpio", sizeof(S5pc1xxGPIOState),
+                        s5pc1xx_gpio_init);
+}
+
+device_init(s5pc1xx_gpio_register_devices)
diff --git a/hw/s5pc1xx_gpio_regs.h b/hw/s5pc1xx_gpio_regs.h
new file mode 100644 (file)
index 0000000..93b43fb
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * S5C GPIO Controller Constants
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+
+/* Registers */
+
+#define STEP                0x20
+
+#define CON(group)          (0x000 + STEP * group)  /* R/W Configuration Register */
+#define DAT(group)          (0x004 + STEP * group)  /* R/W Data Register */
+#define PUD(group)          (0x008 + STEP * group)  /* R/W Pull-up/down Register */
+#define DRV(group)          (0x00C + STEP * group)  /* R/W Drive Strength Control Register */
+#define CONPDN(group)       (0x010 + STEP * group)  /* R/W Power Down Mode Configuration Register */
+#define PUDPDN(group)       (0x014 + STEP * group)  /* R/W Power Down Mode Pullup/down Register */
+
+/* General PIN configurations */
+#define GIPIO_CONF_INPUT    0x0
+#define GIPIO_CONF_OUTPUT   0x1
+#define GIPIO_CONF_INT      0xF
+
+/* Warning: the group GPI is absent in the registers below */
+
+/* inter - group without GPI */
+#define GROUP_TO_INT_NUM(group) ((group) - ((group) < 17 ? 0:1))
+#define INT_TO_GROUP_NUM(inter) ((inter) + ((inter) < 17 ? 0:1))
+
+#define INT_REGS_SIZE     0x58
+
+#define INT_CON_BASE     0x700
+/* FLTCON has double set of regesters. So its' size is 2*INT_REGS_SIZE */
+#define INT_FLTCON_BASE  0x800
+
+#define INT_MASK_BASE    0x900
+
+#define INT_PEND_BASE    0xA00
+#define INT_FIXPRI_BASE  0xB14
+
+/* R/W GPIO Interrupt Configuration Register */
+#define INT_CON(group)  (INT_CON_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_CON(addr) INT_TO_GROUP_NUM(((addr) - INT_CON_BASE) / 4)
+
+/* R/W GPIO Interrupt Filter Configuration Register 0 and 1 */
+#define INT_FLTCON(group) (INT_FLTCON_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_FLTCON(addr) INT_TO_GROUP_NUM(((addr) - INT_FLTCON_BASE) / 8)
+
+
+/* R/W GPIO Interrupt Mask Register */
+#define INT_MASK(group) (INT_MASK_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_MASK(addr) INT_TO_GROUP_NUM(((addr) - INT_MASK_BASE) / 4)
+
+/* R/W GPIO Interrupt Pending Register */
+#define INT_PEND(group) (INT_PEND_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_PEND(addr) INT_TO_GROUP_NUM(((addr) - INT_PEND_BASE) / 4)
+
+
+#define GPIO_INT_GRPPRI     0xB00   /* R/W GPIO Interrupt Group Priority Control Register 0x0 */
+#define GPIO_INT_PRIORITY   0xB04   /* R/W GPIO Interrupt Priority Control Register 0x00 */
+#define GPIO_INT_SER        0xB08   /* R Current Service Register 0x00 */
+#define GPIO_INT_SER_PEND   0xB0C   /* R Current Service Pending Register 0x00 */
+#define GPIO_INT_GRPFIXPRI  0xB10   /* R/W GPIO Interrupt Group Fixed Priority Control Register 0x00 */
+
+/* R/W GPIO Interrupt Fixed Priority Control Register */
+#define INT_FIXPRI(group) (INT_PEND_FIXPRI + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_FIXPRI(addr) INT_TO_GROUP_NUM(((addr)-INT_FIXPRI_BASE) / 4)
+
+/* R/W External Interrupt Configuration Register */
+#define EXT_INT_CON(n)      (0xE00 + 0x04 * n)
+
+/* R/W External Interrupt Filter Configuration Register 0 */
+#define EXT_INT_FLTCON0(n)  (0xE80 + 0x08 * n)
+
+/* R/W External Interrupt Filter Configuration Register 1 */
+#define EXT_INT_FLTCON1(n)  (0xE84 + 0x08 * n)
+
+/* R/W External Interrupt Mask Register */
+#define EXT_INT_MASK(n)     (0xF00 + 0x04 * n)
+
+/* R/W External Interrupt Pending Register */
+#define EXT_INT_PEND(n)     (0xF40 + 0x04 * n)
+
+/* R/W Power down mode Pad Configure Register */
+#define PDNEN               0xF80
+
+
+/* Groups */
+
+#define GPA0    0
+#define GPA1    1
+#define GPB     2
+#define GPC0    3
+#define GPC1    4
+#define GPD0    5
+#define GPD1    6
+#define GPE0    7
+#define GPE1    8
+#define GPF0    9
+#define GPF1    10
+#define GPF2    11
+#define GPF3    12
+#define GPG0    13
+#define GPG1    14
+#define GPG2    15
+#define GPG3    16
+#define GPI     17
+#define GPJ0    18
+#define GPJ1    19
+#define GPJ2    20
+#define GPJ3    21
+#define GPJ4    22
+
+#define MP0_1   23
+#define MP0_2   24
+#define MP0_3   25
+#define MP0_4   26
+#define MP0_5   27
+#define MP0_6   28
+#define MP0_7   29
+
+#define MP1_0   30
+#define MP1_1   31
+#define MP1_2   32
+#define MP1_3   33
+#define MP1_4   34
+#define MP1_5   35
+#define MP1_6   36
+#define MP1_7   37
+#define MP1_8   38
+
+#define MP2_0   39
+#define MP2_1   40
+#define MP2_2   41
+#define MP2_3   42
+#define MP2_4   43
+#define MP2_5   44
+#define MP2_6   45
+#define MP2_7   46
+#define MP2_8   47
+
+#define ETC0    48
+#define ETC1    49
+#define ETC2    50
+
+#define GPH0    96
+#define GPH1    97
+#define GPH2    98
+#define GPH3    99
+
+
+#define MAX_PIN_IN_GROUP            8
+#define IRQ_EXTEND_NUM              32
+
+/* GPIOINT IRQs */
+#define GPIOEXT_IRQ(irq)            (irq % 32)
+#define GPIOINT_IRQ(group, pin) \
+    ((group) * MAX_PIN_IN_GROUP + (pin) + IRQ_EXTEND_NUM)
+#define GPIOINT_GROUP(irq)          ((irq - IRQ_EXTEND_NUM) / MAX_PIN_IN_GROUP)
+#define GPIOINT_PIN(irq)            ((irq - IRQ_EXTEND_NUM) % MAX_PIN_IN_GROUP)
+
+
+/* GPIO device indexes and io_cases */
+
+/* CASE_ID has the structure: device_num [15:11];
+ *                            instance_num [10:6];
+ *                            event_num [5:0]
+ */
+#define CASE_ID(device, instance, event)    ((device << 11) | \
+                                             (instance << 6) | \
+                                             (event << 0))
+
+#define GPIO_IDX_PWM            1
+    #define GPIO_PWM_TOUT(n)        CASE_ID(GPIO_IDX_PWM, 0, n)     /* n = 0~3 */
+    #define PWM_MIE_MDNI            CASE_ID(GPIO_IDX_PWM, 0, 4)
+
+#define GPIO_IDX_UART           2
+    #define GPIO_UART_RXD(n)        CASE_ID(GPIO_IDX_UART, n, 1)    /* n = 0~3 */
+    #define GPIO_UART_TXD(n)        CASE_ID(GPIO_IDX_UART, n, 2)    /* n = 0~3 */
+    #define GPIO_UART_CTS(n)        CASE_ID(GPIO_IDX_UART, n, 3)    /* n = 0~2 */
+    #define GPIO_UART_RTS(n)        CASE_ID(GPIO_IDX_UART, n, 4)    /* n = 0~2 */
+    #define GPIO_UART_AUDIO_RXD     CASE_ID(GPIO_IDX_UART, 0, 5)
+    #define GPIO_UART_AUDIO_TXD     CASE_ID(GPIO_IDX_UART, 0, 6)
+
+#define GPIO_IDX_KEYIF          3
+    #define GPIO_KP_COL(n)          CASE_ID(GPIO_IDX_KEYIF, n, 1)   /* n = 0~7 */
+    #define GPIO_KP_ROW(n)          CASE_ID(GPIO_IDX_KEYIF, n, 2)   /* n = 0~13 */
+
+#define GPIO_IDX_LCD            4
+    #define LCD_FRM                 CASE_ID(GPIO_IDX_LCD, 0, 1)
+    #define LCD_HSYNC               CASE_ID(GPIO_IDX_LCD, 0, 2)
+    #define LCD_VSYNC               CASE_ID(GPIO_IDX_LCD, 0, 3)
+    #define LCD_VDEN                CASE_ID(GPIO_IDX_LCD, 0, 4)
+    #define LCD_VCLK                CASE_ID(GPIO_IDX_LCD, 0, 5)
+    #define LCD_VD(n)               CASE_ID(GPIO_IDX_LCD, n, 6)     /* n = 0~23 */
+
+#define GPIO_IDX_I2C            5
+    #define GPIO_IDX_I2C_SCL        1
+    #define GPIO_IDX_I2C_SDA        2
+    #define GPIO_I2C_SDA(n)         CASE_ID(GPIO_IDX_I2C, n, GPIO_IDX_I2C_SDA)
+    #define GPIO_I2C_SCL(n)         CASE_ID(GPIO_IDX_I2C, n, GPIO_IDX_I2C_SCL)
+
+#define GPIO_IDX_AC97           6
+    #define GPIO_AC97BITCLK         CASE_ID(GPIO_IDX_AC97, 0, 1)
+    #define GPIO_AC97RESETn         CASE_ID(GPIO_IDX_AC97, 0, 2)
+    #define GPIO_AC97SYNC           CASE_ID(GPIO_IDX_AC97, 0, 3)
+    #define GPIO_AC97SDI            CASE_ID(GPIO_IDX_AC97, 0, 4)
+    #define GPIO_AC97SDO            CASE_ID(GPIO_IDX_AC97, 0, 5)
+
+#define GPIO_IDX_PCM            7
+    #define PCM_SCLK(n)             CASE_ID(GPIO_IDX_PCM, n, 1)     /* n = 0~2 */
+    #define PCM_EXTCLK(n)           CASE_ID(GPIO_IDX_PCM, n, 2)     /* n = 0~2 */
+    #define PCM_FSYNC(n)            CASE_ID(GPIO_IDX_PCM, n, 3)     /* n = 0~2 */
+    #define PCM_SIN(n)              CASE_ID(GPIO_IDX_PCM, n, 4)     /* n = 0~2 */
+    #define PCM_SOUT(n)             CASE_ID(GPIO_IDX_PCM, n, 5)     /* n = 0~2 */
+
+#define GPIO_IDX_SPDIF          8
+    #define SPDIF_0_OUT             CASE_ID(GPIO_IDX_SPDIF, 0, 1)
+    #define SPDIF_EXTCLK            CASE_ID(GPIO_IDX_SPDIF, 0, 2)
+
+#define GPIO_IDX_MAX            9
+
+
+/* GPIO io_cases storage */
+
+/* Note: cases with indexes = 2~5 are stored in the array first */
+#define CASE(index)         (index > 1 ? index - 2 : 4 + index)
diff --git a/hw/s5pc1xx_hsmmc_regs.h b/hw/s5pc1xx_hsmmc_regs.h
new file mode 100644 (file)
index 0000000..85c3a5e
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * S5C HS-MMC Controller Constants
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on SMDK6400 MMC (hw/regs-hsmmc.h)
+ */
+
+#ifndef __ASM_ARCH_REGS_HSMMC_H
+#define __ASM_ARCH_REGS_HSMMC_H __FILE__
+
+/*
+ * HS MMC Interface
+ */
+
+#define S5C_HSMMC_REG(x)                    (x)
+
+/* R/W SDMA System Address register 0x0 */
+#define S5C_HSMMC_SYSAD                     0x00
+
+/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */
+#define S5C_HSMMC_BLKSIZE                   0x04
+#define S5C_HSMMC_MAKE_BLKSZ(dma, bblksz)   (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+/* R/W Blocks count for current transfer 0x0 */
+#define S5C_HSMMC_BLKCNT                    0x06
+/* R/W Command Argument Register 0x0 */
+#define S5C_HSMMC_ARGUMENT                  0x08
+
+/* R/W Transfer Mode Setting Register 0x0 */
+#define S5C_HSMMC_TRNMOD                    0x0C
+#define S5C_HSMMC_TRNS_DMA                  0x01
+#define S5C_HSMMC_TRNS_BLK_CNT_EN           0x02
+#define S5C_HSMMC_TRNS_ACMD12               0x04
+#define S5C_HSMMC_TRNS_READ                 0x10
+#define S5C_HSMMC_TRNS_MULTI                0x20
+
+#define S5C_HSMMC_TRNS_BOOTCMD              0x1000
+#define S5C_HSMMC_TRNS_BOOTACK              0x2000
+
+/* R/W Command Register 0x0 */
+#define S5C_HSMMC_CMDREG                    0x0E
+/* ROC Response Register 0 0x0 */
+#define S5C_HSMMC_RSPREG0                   0x10
+/* ROC Response Register 1 0x0 */
+#define S5C_HSMMC_RSPREG1                   0x14
+/* ROC Response Register 2 0x0 */
+#define S5C_HSMMC_RSPREG2                   0x18
+/* ROC Response Register 3 0x0 */
+#define S5C_HSMMC_RSPREG3                   0x1C
+#define S5C_HSMMC_CMD_RESP_MASK             0x03
+#define S5C_HSMMC_CMD_CRC                   0x08
+#define S5C_HSMMC_CMD_INDEX                 0x10
+#define S5C_HSMMC_CMD_DATA                  0x20
+#define S5C_HSMMC_CMD_RESP_NONE             0x00
+#define S5C_HSMMC_CMD_RESP_LONG             0x01
+#define S5C_HSMMC_CMD_RESP_SHORT            0x02
+#define S5C_HSMMC_CMD_RESP_SHORT_BUSY       0x03
+#define S5C_HSMMC_MAKE_CMD(c, f)            (((c & 0xFF) << 8) | (f & 0xFF))
+
+/* R/W Buffer Data Register 0x0 */
+#define S5C_HSMMC_BDATA                     0x20
+/* R/ROC Present State Register 0x000A0000 */
+#define S5C_HSMMC_PRNSTS                    0x24
+#define S5C_HSMMC_CMD_INHIBIT               0x00000001
+#define S5C_HSMMC_DATA_INHIBIT              0x00000002
+#define S5C_HSMMC_DOING_WRITE               0x00000100
+#define S5C_HSMMC_DOING_READ                0x00000200
+#define S5C_HSMMC_SPACE_AVAILABLE           0x00000400
+#define S5C_HSMMC_DATA_AVAILABLE            0x00000800
+#define S5C_HSMMC_CARD_PRESENT              0x00010000
+#define S5C_HSMMC_WRITE_PROTECT             0x00080000
+
+/* R/W Present State Register 0x0 */
+#define S5C_HSMMC_HOSTCTL                   0x28
+#define S5C_HSMMC_CTRL_LED                  0x01
+#define S5C_HSMMC_CTRL_4BITBUS              0x02
+#define S5C_HSMMC_CTRL_HIGHSPEED            0x04
+#define S5C_HSMMC_CTRL_1BIT                 0x00
+#define S5C_HSMMC_CTRL_4BIT                 0x02
+#define S5C_HSMMC_CTRL_8BIT                 0x20
+#define S5C_HSMMC_CTRL_SDMA                 0x00
+#define S5C_HSMMC_CTRL_ADMA2_32             0x10
+
+/* R/W Present State Register 0x0 */
+#define S5C_HSMMC_PWRCON                    0x29
+#define S5C_HSMMC_POWER_OFF                 0x00
+#define S5C_HSMMC_POWER_ON                  0x01
+#define S5C_HSMMC_POWER_180                 0x0A
+#define S5C_HSMMC_POWER_300                 0x0C
+#define S5C_HSMMC_POWER_330                 0x0E
+#define S5C_HSMMC_POWER_ON_ALL              0xFF
+
+/* R/W Block Gap Control Register 0x0 */
+#define S5C_HSMMC_BLKGAP                    0x2A
+/* R/W Wakeup Control Register 0x0 */
+#define S5C_HSMMC_WAKCON                    0x2B
+
+#define S5C_HSMMC_STAWAKEUP                 0x8
+
+/* R/W Command Register 0x0 */
+#define S5C_HSMMC_CLKCON                    0x2C
+#define S5C_HSMMC_DIVIDER_SHIFT             0x8
+#define S5C_HSMMC_CLOCK_EXT_STABLE          0x8
+#define S5C_HSMMC_CLOCK_CARD_EN             0x4
+#define S5C_HSMMC_CLOCK_INT_STABLE          0x2
+#define S5C_HSMMC_CLOCK_INT_EN              0x1
+
+/* R/W Timeout Control Register 0x0 */
+#define S5C_HSMMC_TIMEOUTCON                0x2E
+#define S5C_HSMMC_TIMEOUT_MAX               0x0E
+
+/* R/W Software Reset Register 0x0 */
+#define S5C_HSMMC_SWRST                     0x2F
+#define S5C_HSMMC_RESET_ALL                 0x01
+#define S5C_HSMMC_RESET_CMD                 0x02
+#define S5C_HSMMC_RESET_DATA                0x04
+
+/* ROC/RW1C Normal Interrupt Status Register 0x0 */
+#define S5C_HSMMC_NORINTSTS                 0x30
+#define S5C_HSMMC_NIS_ERR                   0x00008000
+#define S5C_HSMMC_NIS_CMDCMP                0x00000001
+#define S5C_HSMMC_NIS_TRSCMP                0x00000002
+#define S5C_HSMMC_NIS_DMA                   0x00000008
+#define S5C_HSMMC_NIS_INSERT                0x00000040
+#define S5C_HSMMC_NIS_REMOVE                0x00000080
+
+/* ROC/RW1C Error Interrupt Status Register 0x0 */
+#define S5C_HSMMC_ERRINTSTS                 0x32
+#define S5C_HSMMC_EIS_CMDTIMEOUT            0x00000001
+#define S5C_HSMMC_EIS_CMDERR                0x0000000E
+#define S5C_HSMMC_EIS_DATATIMEOUT           0x00000010
+#define S5C_HSMMC_EIS_DATAERR               0x00000060
+#define S5C_HSMMC_EIS_CMD12ERR              0x00000100
+#define S5C_HSMMC_EIS_ADMAERR               0x00000200
+#define S5C_HSMMC_EIS_STABOOTACKERR         0x00000400
+
+/* R/W Normal Interrupt Status Enable Register 0x0 */
+#define S5C_HSMMC_NORINTSTSEN               0x34
+/* R/W Error Interrupt Status Enable Register 0x0 */
+#define S5C_HSMMC_ERRINTSTSEN               0x36
+#define S5C_HSMMC_ENSTABOOTACKERR           0x400
+
+/* R/W Normal Interrupt Signal Enable Register 0x0 */
+#define S5C_HSMMC_NORINTSIGEN               0x38
+#define S5C_HSMMC_INT_MASK_ALL              0x00
+#define S5C_HSMMC_INT_RESPONSE              0x00000001
+#define S5C_HSMMC_INT_DATA_END              0x00000002
+#define S5C_HSMMC_INT_DMA_END               0x00000008
+#define S5C_HSMMC_INT_SPACE_AVAIL           0x00000010
+#define S5C_HSMMC_INT_DATA_AVAIL            0x00000020
+#define S5C_HSMMC_INT_CARD_INSERT           0x00000040
+#define S5C_HSMMC_INT_CARD_REMOVE           0x00000080
+#define S5C_HSMMC_INT_CARD_CHANGE           0x000000C0
+#define S5C_HSMMC_INT_CARD_INT              0x00000100
+#define S5C_HSMMC_INT_TIMEOUT               0x00010000
+#define S5C_HSMMC_INT_CRC                   0x00020000
+#define S5C_HSMMC_INT_END_BIT               0x00040000
+#define S5C_HSMMC_INT_INDEX                 0x00080000
+#define S5C_HSMMC_INT_DATA_TIMEOUT          0x00100000
+#define S5C_HSMMC_INT_DATA_CRC              0x00200000
+#define S5C_HSMMC_INT_DATA_END_BIT          0x00400000
+#define S5C_HSMMC_INT_BUS_POWER             0x00800000
+#define S5C_HSMMC_INT_ACMD12ERR             0x01000000
+#define S5C_HSMMC_INT_ADMAERR               0x02000000
+
+#define S5C_HSMMC_INT_NORMAL_MASK           0x00007FFF
+#define S5C_HSMMC_INT_ERROR_MASK            0xFFFF8000
+
+#define S5C_HSMMC_INT_CMD_MASK              (S5C_HSMMC_INT_RESPONSE | \
+                                             S5C_HSMMC_INT_TIMEOUT | \
+                                             S5C_HSMMC_INT_CRC | \
+                                             S5C_HSMMC_INT_END_BIT | \
+                                             S5C_HSMMC_INT_INDEX | \
+                                             S5C_HSMMC_NIS_ERR)
+#define S5C_HSMMC_INT_DATA_MASK             (S5C_HSMMC_INT_DATA_END | \
+                                             S5C_HSMMC_INT_DMA_END | \
+                                             S5C_HSMMC_INT_DATA_AVAIL | \
+                                             S5C_HSMMC_INT_SPACE_AVAIL | \
+                                             S5C_HSMMC_INT_DATA_TIMEOUT | \
+                                             S5C_HSMMC_INT_DATA_CRC | \
+                                             S5C_HSMMC_INT_DATA_END_BIT)
+
+/* R/W Error Interrupt Signal Enable Register 0x0 */
+#define S5C_HSMMC_ERRINTSIGEN               0x3A
+#define S5C_HSMMC_ENSIGBOOTACKERR           0x400
+
+/* ROC Auto CMD12 error status register 0x0 */
+#define S5C_HSMMC_ACMD12ERRSTS              0x3C
+
+/* HWInit Capabilities Register 0x05E80080 */
+#define S5C_HSMMC_CAPAREG                   0x40
+#define S5C_HSMMC_TIMEOUT_CLK_MASK          0x0000003F
+#define S5C_HSMMC_TIMEOUT_CLK_SHIFT         0x0
+#define S5C_HSMMC_TIMEOUT_CLK_UNIT          0x00000080
+#define S5C_HSMMC_CLOCK_BASE_MASK           0x00003F00
+#define S5C_HSMMC_CLOCK_BASE_SHIFT          0x8
+#define S5C_HSMMC_MAX_BLOCK_MASK            0x00030000
+#define S5C_HSMMC_MAX_BLOCK_SHIFT           0x10
+#define S5C_HSMMC_CAN_DO_DMA                0x00400000
+#define S5C_HSMMC_CAN_DO_ADMA2              0x00080000
+#define S5C_HSMMC_CAN_VDD_330               0x01000000
+#define S5C_HSMMC_CAN_VDD_300               0x02000000
+#define S5C_HSMMC_CAN_VDD_180               0x04000000
+
+/* HWInit Maximum Current Capabilities Register 0x0 */
+#define S5C_HSMMC_MAXCURR                   0x48
+
+/* For ADMA2 */
+
+/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */
+#define S5C_HSMMC_FEAER                     0x50
+/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */
+#define S5C_HSMMC_FEERR                     0x52
+
+/* R/W ADMA Error Status Register 0x00 */
+#define S5C_HSMMC_ADMAERR                   0x54
+#define S5C_HSMMC_ADMAERR_CONTINUE_REQUEST  (1 << 9)
+#define S5C_HSMMC_ADMAERR_INTRRUPT_STATUS   (1 << 8)
+#define S5C_HSMMC_ADMAERR_LENGTH_MISMATCH   (1 << 2)
+#define S5C_HSMMC_ADMAERR_STATE_ST_STOP     (0 << 0)
+#define S5C_HSMMC_ADMAERR_STATE_ST_FDS      (1 << 0)
+#define S5C_HSMMC_ADMAERR_STATE_ST_TFR      (3 << 0)
+
+/* R/W ADMA System Address Register 0x00 */
+#define S5C_HSMMC_ADMASYSADDR               0x58
+#define S5C_HSMMC_ADMA_ATTR_MSK             0x3F
+#define S5C_HSMMC_ADMA_ATTR_ACT_NOP         (0 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_RSV         (1 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_TRAN        (2 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_LINK        (3 << 4)
+#define S5C_HSMMC_ADMA_ATTR_INT             (1 << 2)
+#define S5C_HSMMC_ADMA_ATTR_END             (1 << 1)
+#define S5C_HSMMC_ADMA_ATTR_VALID           (1 << 0)
+
+/* R/W Control register 2 0x0 */
+#define S5C_HSMMC_CONTROL2                  0x80
+/* R/W FIFO Interrupt Control (Control Register 3) 0x7F5F3F1F */
+#define S5C_HSMMC_CONTROL3                  0x84
+/* R/W Control register 4 0x0 */
+#define S5C_HSMMC_CONTROL4                  0x8C
+
+
+/* Magic register which is used from kernel! */
+#define S5C_HSMMC_SLOT_INT_STATUS           0xFC
+
+
+/* HWInit Host Controller Version Register 0x0401 */
+#define S5C_HSMMC_HCVER                     0xFE
+#define S5C_HSMMC_VENDOR_VER_MASK           0xFF00
+#define S5C_HSMMC_VENDOR_VER_SHIFT          0x8
+#define S5C_HSMMC_SPEC_VER_MASK             0x00FF
+#define S5C_HSMMC_SPEC_VER_SHIFT            0x0
+
+#define S5C_HSMMC_REG_SIZE                  0x100
+
+#endif /* __ASM_ARCH_REGS_HSMMC_H */
diff --git a/hw/s5pc1xx_i2c.c b/hw/s5pc1xx_i2c.c
new file mode 100644 (file)
index 0000000..efe2899
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * I2C controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *                Alexey Merkulov <steelart@ispras.ru>
+ *
+ * Based on SMDK6400 I2C (hw/smdk6400/smdk_i2c.c)
+ */
+
+#include "i2c.h"
+#include "sysbus.h"
+
+
+#define I2CCON        0x00      /* I2C Control register */
+#define I2CSTAT       0x04      /* I2C Status register */
+#define I2CADD        0x08      /* I2C Slave Address register */
+#define I2CDS         0x0c      /* I2C Data Shift register */
+#define I2CLC         0x10      /* I2C Line Control register */
+
+#define SR_MODE       0x0       /* Slave Receive Mode */
+#define ST_MODE       0x1       /* Slave Transmit Mode */
+#define MR_MODE       0x2       /* Master Receive Mode */
+#define MT_MODE       0x3       /* Master Transmit Mode */
+
+
+#define S5PC1XX_IICCON_ACKEN        (1<<7)
+#define S5PC1XX_IICCON_TXDIV_16     (0<<6)
+#define S5PC1XX_IICCON_TXDIV_512    (1<<6)
+#define S5PC1XX_IICCON_IRQEN        (1<<5)
+#define S5PC1XX_IICCON_IRQPEND      (1<<4)
+
+#define S5PC1XX_IICSTAT_START       (1<<5)
+#define S5PC1XX_IICSTAT_BUSBUSY     (1<<5)
+#define S5PC1XX_IICSTAT_TXRXEN      (1<<4)
+#define S5PC1XX_IICSTAT_ARBITR      (1<<3)
+#define S5PC1XX_IICSTAT_ASSLAVE     (1<<2)
+#define S5PC1XX_IICSTAT_ADDR0       (1<<1)
+#define S5PC1XX_IICSTAT_LASTBIT     (1<<0)
+
+#define S5PC1XX_I2C_REG_MEM_SIZE    0x1000
+
+
+/* I2C Interface */
+typedef struct S5pc1xxI2CState {
+    SysBusDevice busdev;
+
+    i2c_bus *bus;
+    qemu_irq irq;
+
+    uint8_t control;
+    uint8_t status;
+    uint8_t address;
+    uint8_t datashift;
+    uint8_t line_ctrl;
+
+    uint8_t ibmr;
+    uint8_t data;
+} S5pc1xxI2CState;
+
+
+static void s5pc1xx_i2c_update(S5pc1xxI2CState *s)
+{
+    uint16_t level;
+    level = (s->status & S5PC1XX_IICSTAT_START) &&
+            (s->control & S5PC1XX_IICCON_IRQEN);
+
+    if (s->control & S5PC1XX_IICCON_IRQPEND)
+        level = 0;
+    qemu_set_irq(s->irq, !!level);
+}
+
+static int s5pc1xx_i2c_receive(S5pc1xxI2CState *s)
+{
+    int r;
+
+    r = i2c_recv(s->bus);
+    s5pc1xx_i2c_update(s);
+    return r;
+}
+
+static int s5pc1xx_i2c_send(S5pc1xxI2CState *s, uint8_t data)
+{
+    if (!(s->status & S5PC1XX_IICSTAT_LASTBIT)) {
+        /*s->status |= 1 << 7;*/
+        s->data = data;
+        i2c_send(s->bus, s->data);
+    }
+    s5pc1xx_i2c_update(s);
+    return 1;
+}
+
+/* I2C read function */
+static uint32_t s5pc1xx_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+    switch (offset) {
+    case I2CCON:
+        return s->control;
+    case I2CSTAT:
+        return s->status;
+    case I2CADD:
+        return s->address;
+    case I2CDS:
+        s->data = s5pc1xx_i2c_receive(s);
+        return s->data;
+    case I2CLC:
+        return s->line_ctrl;
+    default:
+        hw_error("s5pc1xx.i2c: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+    return 0;
+}
+
+/* I2C write function */
+static void s5pc1xx_i2c_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+    int mode;
+
+    qemu_irq_lower(s->irq);
+
+    switch (offset) {
+    case I2CCON:
+        s->control = value & 0xff;
+
+        if (value & S5PC1XX_IICCON_IRQEN)
+            s5pc1xx_i2c_update(s);
+        break;
+
+    case I2CSTAT:
+        s->status = value & 0xff;
+        mode = (s->status >> 6) & 0x3;
+        if (value & S5PC1XX_IICSTAT_TXRXEN) {
+            /* IIC-bus data output enable/disable bit */
+            switch(mode) {
+            case SR_MODE:
+                s->data = s5pc1xx_i2c_receive(s);
+                break;
+            case ST_MODE:
+                s->data = s5pc1xx_i2c_receive(s);
+                break;
+            case MR_MODE:
+                if (value & (1 << 5)) {
+                    /* START condition */
+                    s->status &= ~S5PC1XX_IICSTAT_LASTBIT;
+
+                    i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+                } else {
+                    i2c_end_transfer(s->bus);
+                    s->status |= S5PC1XX_IICSTAT_TXRXEN;
+                }
+                break;
+            case MT_MODE:
+                if (value & (1 << 5)) {
+                    /* START condition */
+                    s->status &= ~S5PC1XX_IICSTAT_LASTBIT;
+
+                    i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+                } else {
+                    i2c_end_transfer(s->bus);
+                    s->status |= S5PC1XX_IICSTAT_TXRXEN;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        s5pc1xx_i2c_update(s);
+        break;
+
+    case I2CADD:
+        s->address = value & 0xff;
+        break;
+
+    case I2CDS:
+        s5pc1xx_i2c_send(s, value & 0xff);
+        break;
+
+    case I2CLC:
+        s->line_ctrl = value & 0xff;
+        break;
+
+    default:
+        hw_error("s5pc1xx.i2c: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_i2c_readfn[] = {
+    s5pc1xx_i2c_read,
+    s5pc1xx_i2c_read,
+    s5pc1xx_i2c_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_i2c_writefn[] = {
+    s5pc1xx_i2c_write,
+    s5pc1xx_i2c_write,
+    s5pc1xx_i2c_write
+};
+
+static void s5pc1xx_i2c_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+    qemu_put_8s(f, &s->control);
+    qemu_put_8s(f, &s->status);
+    qemu_put_8s(f, &s->address);
+    qemu_put_8s(f, &s->datashift);
+    qemu_put_8s(f, &s->line_ctrl);
+    qemu_put_8s(f, &s->ibmr);
+    qemu_put_8s(f, &s->data);
+}
+
+static int s5pc1xx_i2c_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_8s(f, &s->control);
+    qemu_get_8s(f, &s->status);
+    qemu_get_8s(f, &s->address);
+    qemu_get_8s(f, &s->datashift);
+    qemu_get_8s(f, &s->line_ctrl);
+    qemu_get_8s(f, &s->ibmr);
+    qemu_get_8s(f, &s->data);
+
+    return 0;
+}
+
+/* I2C init */
+static int s5pc1xx_i2c_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxI2CState *s = FROM_SYSBUS(S5pc1xxI2CState, dev);
+
+    sysbus_init_irq(dev, &s->irq);
+    s->bus = i2c_init_bus(&dev->qdev, "i2c");
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_i2c_readfn, s5pc1xx_i2c_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_I2C_REG_MEM_SIZE, iomemtype);
+
+    register_savevm(&dev->qdev, "s5pc1xx.i2c", -1, 1,
+                    s5pc1xx_i2c_save, s5pc1xx_i2c_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_i2c_register(void)
+{
+    sysbus_register_dev("s5pc1xx.i2c", sizeof(S5pc1xxI2CState),
+                        s5pc1xx_i2c_init);
+}
+
+device_init(s5pc1xx_i2c_register)
diff --git a/hw/s5pc1xx_i2c_gpio.c b/hw/s5pc1xx_i2c_gpio.c
new file mode 100644 (file)
index 0000000..36e8529
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * I2C bus through GPIO pins
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "i2c.h"
+#include "sysbus.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "bitbang_i2c.h"
+
+
+//#define DEBUG
+#define SDA_PIN_NUM GPIO_IDX_I2C_SDA
+
+
+typedef struct S5pc1xxI2CGPIOState {
+    SysBusDevice busdev;
+
+    bitbang_i2c_interface *bitbang;
+    i2c_bus *bus;
+
+    int32_t sda;
+    int32_t scl;
+    uint32_t instance;
+} S5pc1xxI2CGPIOState;
+
+
+static void s5pc1xx_i2c_gpio_reset(void *opaque)
+{
+    S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+    s->sda = 1;
+    s->scl = 1;
+}
+
+static void s5pc1xx_i2c_bitbang_set_conf(void *opaque, int io_index,
+                                         uint32_t conf)
+{
+    S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+#ifdef DEBUG
+    fprintf(stderr, "QEMU BITBANG I2C set configuration: io_index = %s, "
+                    "conf = 0x%02X\n",
+            io_index == SDA_PIN_NUM ? "sda" : "scl", conf == GIPIO_CONF_INPUT);
+#endif
+
+    if (io_index == SDA_PIN_NUM) {
+        s->sda = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, conf == GIPIO_CONF_INPUT);
+    } else {
+        s->sda = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, conf == GIPIO_CONF_INPUT);
+        s->scl = (conf == GIPIO_CONF_INPUT);
+    }
+}
+
+static uint32_t s5pc1xx_i2c_bitbang_read(void *opaque, int io_index)
+{
+    S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+    uint32_t ret = io_index == SDA_PIN_NUM ? s->sda : s->scl;
+
+#ifdef DEBUG
+    fprintf(stderr, "QEMU BITBANG I2C read: io_index = %s, value = %d\n",
+            io_index == SDA_PIN_NUM ? "sda" : "scl", ret);
+#endif
+
+    return ret;
+}
+
+static void s5pc1xx_i2c_bitbang_write(void *opaque, int io_index, uint32_t value)
+{
+#ifdef DEBUG
+    fprintf(stderr, "QEMU BITBANG I2C write: io_index = %s, value = %u\n",
+            io_index == SDA_PIN_NUM ? "sda" : "scl", value);
+#endif
+}
+
+static void s5pc1xx_i2c_gpio_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+    /* FIXME: save bitbang? */
+    qemu_put_sbe32s(f, &s->sda);
+    qemu_put_sbe32s(f, &s->scl);
+}
+
+static int s5pc1xx_i2c_gpio_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_sbe32s(f, &s->sda);
+    qemu_get_sbe32s(f, &s->scl);
+
+    return 0;
+}
+
+DeviceState *s5pc1xx_i2c_gpio_init(int instance)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.i2c.gpio");
+    qdev_prop_set_uint32(dev, "instance", instance);
+    qdev_init_nofail(dev);
+    return dev;
+}
+
+/* I2C init */
+static int s5pc1xx_i2c_gpio_init1(SysBusDevice *dev)
+{
+    S5pc1xxI2CGPIOState *s = FROM_SYSBUS(S5pc1xxI2CGPIOState, dev);
+
+    s->bus = i2c_init_bus(&dev->qdev, "i2c");
+    s->bitbang = bitbang_i2c_init(s->bus);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_I2C, s->instance,
+                                    s5pc1xx_i2c_bitbang_read,
+                                    s5pc1xx_i2c_bitbang_write,
+                                    s5pc1xx_i2c_bitbang_set_conf, s);
+
+    s5pc1xx_i2c_gpio_reset(s);
+    qemu_register_reset(s5pc1xx_i2c_gpio_reset, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.i2c.gpio", s->instance, 1,
+                    s5pc1xx_i2c_gpio_save, s5pc1xx_i2c_gpio_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_i2c_gpio_info = {
+    .init = s5pc1xx_i2c_gpio_init1,
+    .qdev.name  = "s5pc1xx.i2c.gpio",
+    .qdev.size  = sizeof(S5pc1xxI2CGPIOState),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("instance",   S5pc1xxI2CGPIOState, instance, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_i2c_gpio_register(void)
+{
+    sysbus_register_withprop(&s5pc1xx_i2c_gpio_info);
+}
+
+device_init(s5pc1xx_i2c_gpio_register)
diff --git a/hw/s5pc1xx_i2s.c b/hw/s5pc1xx_i2s.c
new file mode 100644 (file)
index 0000000..e178b67
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * IIS Multi Audio Interface for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *
+ * TODO: make support of second channel!
+ */
+
+#include "sysbus.h"
+#include "i2c.h"
+#include "s5pc1xx.h"
+
+
+#define AUDIO_WORDS_SEND  0x800
+
+/*Magic size*/
+#define AUDIO_BUFFER_SIZE 0x100000
+
+#define S5PC1XX_I2S_IISCON    0x00
+#define S5PC1XX_I2S_IISMOD    0x04
+#define S5PC1XX_I2S_IISFIC    0x08
+#define S5PC1XX_I2S_IISPSR    0x0C
+#define S5PC1XX_I2S_IISTXD    0x10
+#define S5PC1XX_I2S_IISRXD    0x14
+#define S5PC1XX_I2S_IISFICS   0x18
+#define S5PC1XX_I2S_IISTXDS   0x1C
+#define S5PC1XX_I2S_IISAHB    0x20
+#define S5PC1XX_I2S_IISSTR    0x24
+#define S5PC1XX_I2S_IISSIZE   0x28
+#define S5PC1XX_I2S_IISTRNCNT 0x2C
+#define S5PC1XX_I2S_IISADDR0  0x30
+#define S5PC1XX_I2S_IISADDR1  0x34
+#define S5PC1XX_I2S_IISADDR2  0x38
+#define S5PC1XX_I2S_IISADDR3  0x3C
+#define S5PC1XX_I2S_IISSTR1   0x40
+
+#define S5PC1XX_I2S_REG_MEM_SIZE 0x44
+
+#define S5PC1XX_IISCON_I2SACTIVE    (0x1<< 0)
+#define S5PC1XX_IISCON_RXDMACTIVE   (0x1<< 1)
+#define S5PC1XX_IISCON_TXDMACTIVE   (0x1<< 2)
+#define S5PC1XX_IISCON_RXCHPAUSE    (0x1<< 3)
+#define S5PC1XX_IISCON_TXCHPAUSE    (0x1<< 4)
+#define S5PC1XX_IISCON_RXDMAPAUSE   (0x1<< 5)
+#define S5PC1XX_IISCON_TXDMAPAUSE   (0x1<< 6)
+#define S5PC1XX_IISCON_FRXFULL      (0x1<< 7) /*Read only*/
+#define S5PC1XX_IISCON_FTX0FULL     (0x1<< 8) /*Read only*/
+#define S5PC1XX_IISCON_FRXEMPT      (0x1<< 9) /*Read only*/
+#define S5PC1XX_IISCON_FTX0EMPT     (0x1<<10) /*Read only*/
+#define S5PC1XX_IISCON_LRI          (0x1<<11) /*Read only*/
+#define S5PC1XX_IISCON_FTX1FULL     (0x1<<12) /*Read only*/
+#define S5PC1XX_IISCON_FTX2FULL     (0x1<<13) /*Read only*/
+#define S5PC1XX_IISCON_FTX1EMPT     (0x1<<14) /*Read only*/
+#define S5PC1XX_IISCON_FTX2EMPT     (0x1<<15) /*Read only*/
+#define S5PC1XX_IISCON_FTXURINTEN   (0x1<<16)
+#define S5PC1XX_IISCON_FTXURSTATUS  (0x1<<17) /*Write to clear*/
+#define S5PC1XX_IISCON_TXSDMACTIVE  (0x1<<18)
+#define S5PC1XX_IISCON_TXSDMAPAUSE  (0x1<<20)
+#define S5PC1XX_IISCON_FTXSFULL     (0x1<<21) /*Read only*/
+#define S5PC1XX_IISCON_FTXSEMPT     (0x1<<22) /*Read only*/
+#define S5PC1XX_IISCON_FTXSURINTEN  (0x1<<23)
+#define S5PC1XX_IISCON_FTXSURSTAT   (0x1<<24) /*Write to clear*/
+#define S5PC1XX_IISCON_FRXOFINTEN   (0x1<<25)
+#define S5PC1XX_IISCON_FRXOFSTAT    (0x1<<26) /*Write to clear*/
+#define S5PC1XX_IISCON_SWRESET      (0x1<<31)
+
+#define S5PC1XX_IISCON_WRITE_MASK           (0x8295007F)
+#define S5PC1XX_IISCON_READ_MASK            (0x0060FF80)
+#define S5PC1XX_IISCON_WRITE_TO_CLEAR_MASK  (0x05020000)
+
+
+#define S5PC1XX_IISMOD_BFSMASK  (3<<1)
+#define S5PC1XX_IISMOD_32FS     (0<<1)
+#define S5PC1XX_IISMOD_48FS     (1<<1)
+#define S5PC1XX_IISMOD_16FS     (2<<1)
+#define S5PC1XX_IISMOD_24FS     (3<<1)
+
+#define S5PC1XX_IISMOD_RFSMASK  (3<<3)
+#define S5PC1XX_IISMOD_256FS    (0<<3)
+#define S5PC1XX_IISMOD_512FS    (1<<3)
+#define S5PC1XX_IISMOD_384FS    (2<<3)
+#define S5PC1XX_IISMOD_768FS    (3<<3)
+
+#define S5PC1XX_IISMOD_SDFMASK  (3<<5)
+#define S5PC1XX_IISMOD_IIS      (0<<5)
+#define S5PC1XX_IISMOD_MSB      (1<<5)
+#define S5PC1XX_IISMOD_LSB      (2<<5)
+
+#define S5PC1XX_IISMOD_LRP      (1<<7)
+
+#define S5PC1XX_IISMOD_TXRMASK  (3<<8)
+#define S5PC1XX_IISMOD_TX       (0<<8)
+#define S5PC1XX_IISMOD_RX       (1<<8)
+#define S5PC1XX_IISMOD_TXRX     (2<<8)
+
+#define S5PC1XX_IISMOD_TX_SET(r) (!((1<<8)&(r)))
+#define S5PC1XX_IISMOD_RX_SET(r) (((3<<8)&(r)) != 0)
+
+
+#define S5PC1XX_IISMOD_IMSMASK      (3<<10)
+#define S5PC1XX_IISMOD_MSTPCLK      (0<<10)
+#define S5PC1XX_IISMOD_MSTCLKAUDIO  (1<<10)
+#define S5PC1XX_IISMOD_SLVPCLK      (2<<10)
+#define S5PC1XX_IISMOD_SLVI2SCLK    (3<<10)
+
+#define S5PC1XX_IISMOD_CDCLKCON     (1<<12)
+
+#define S5PC1XX_IISMOD_BLCMASK      (3<<13)
+#define S5PC1XX_IISMOD_16BIT        (0<<13)
+#define S5PC1XX_IISMOD_8BIT         (1<<13)
+#define S5PC1XX_IISMOD_24BIT        (2<<13)
+
+#define S5PC1XX_IISMOD_SD1EN        (1<<16)
+#define S5PC1XX_IISMOD_SD2EN        (1<<17)
+
+#define S5PC1XX_IISMOD_CCD1MASK     (3<<18)
+#define S5PC1XX_IISMOD_CCD1ND       (0<<18)
+#define S5PC1XX_IISMOD_CCD11STD     (1<<18)
+#define S5PC1XX_IISMOD_CCD12NDD     (2<<18)
+
+#define S5PC1XX_IISMOD_CCD2MASK     (3<<20)
+#define S5PC1XX_IISMOD_CCD2ND       (0<<20)
+#define S5PC1XX_IISMOD_CCD21STD     (1<<20)
+#define S5PC1XX_IISMOD_CCD22NDD     (2<<20)
+
+#define S5PC1XX_IISMOD_BLCPMASK     (3<<24)
+#define S5PC1XX_IISMOD_P16BIT       (0<<24)
+#define S5PC1XX_IISMOD_P8BIT        (1<<24)
+#define S5PC1XX_IISMOD_P24BIT       (2<<24)
+#define S5PC1XX_IISMOD_BLCSMASK     (3<<26)
+#define S5PC1XX_IISMOD_S16BIT       (0<<26)
+#define S5PC1XX_IISMOD_S8BIT        (1<<26)
+#define S5PC1XX_IISMOD_S24BIT       (2<<26)
+#define S5PC1XX_IISMOD_TXSLP        (1<<28)
+#define S5PC1XX_IISMOD_OPMSK        (3<<30)
+#define S5PC1XX_IISMOD_OPCCO        (0<<30)
+#define S5PC1XX_IISMOD_OPCCI        (1<<30)
+#define S5PC1XX_IISMOD_OPBCO        (2<<30)
+#define S5PC1XX_IISMOD_OPPCLK       (3<<30)
+
+#define I2S_TOGGLE_BIT(old, new, bit) (((old) & (bit)) != ((new) & (bit)))
+
+
+/* I2C Interface */
+typedef struct S5pc1xxI2SState {
+    SysBusDevice busdev;
+
+    uint32_t iiscon;
+    uint32_t iismod;
+    uint32_t iisfic;
+    uint32_t iispsr;
+    uint32_t iisfics;
+    uint32_t iisahb;
+    uint32_t iisstr0;
+    uint32_t iissize;
+    uint32_t iistrncnt;
+    uint32_t iislvl0addr;
+    uint32_t iislvl1addr;
+    uint32_t iislvl2addr;
+    uint32_t iislvl3addr;
+    uint32_t iisstr1;
+
+    uint8_t  *buffer;
+    uint32_t buf_size;
+    uint32_t play_pos;
+    uint32_t last_free;
+
+    qemu_irq irq;
+    qemu_irq dma_irq_stop1;
+    qemu_irq dma_irq_stop2;
+    DeviceState *wm8994;
+} S5pc1xxI2SState;
+
+
+static void s5pc1xx_i2s_stop(S5pc1xxI2SState *s)
+{
+    qemu_irq_raise(s->dma_irq_stop1);
+    qemu_irq_raise(s->dma_irq_stop2);
+}
+
+static void s5pc1xx_i2s_resume(S5pc1xxI2SState *s)
+{
+    qemu_irq_lower(s->dma_irq_stop1);
+    qemu_irq_lower(s->dma_irq_stop2);
+}
+
+
+static void s5pc1xx_i2s_reset(void *opaque)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+    s->iiscon      = 0;
+    s->iismod      = 0;
+    s->iisfic      = 0;
+    s->iispsr      = 0;
+    s->iisfics     = 0;
+    s->iisahb      = 0;
+    s->iisstr0     = 0;
+    s->iissize     = 0x7FFF0000;
+    s->iistrncnt   = 0;
+    s->iislvl0addr = 0;
+    s->iislvl1addr = 0;
+    s->iislvl2addr = 0;
+    s->iislvl3addr = 0;
+    s->iisstr1     = 0;
+
+    s->play_pos    = 0;
+    s->last_free   = 0;
+}
+
+static int s5pc1xx_i2s_pause(S5pc1xxI2SState *s) {
+    return s->iiscon & S5PC1XX_IISCON_TXCHPAUSE;
+}
+
+static int s5pc1xx_i2s_transmit(S5pc1xxI2SState *s) {
+    return S5PC1XX_IISMOD_TX_SET(s->iismod);
+}
+
+static void s5pc1xx_i2s_audio_callback(void *opaque, int free_out)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+    int8_t *codec_buffer = NULL;
+    int block_size = 0;
+
+
+    if (free_out <= 0) {
+        return;
+    }
+    if (free_out > AUDIO_WORDS_SEND) {
+        free_out = AUDIO_WORDS_SEND;
+    }
+
+    block_size = 4 * free_out;
+
+    if (s5pc1xx_i2s_pause(s)) {
+        return;
+    }
+
+    if (s->play_pos > s->last_free &&
+        s->play_pos + block_size > s->buf_size &&
+        s->play_pos + block_size - s->buf_size > s->last_free) {
+        s5pc1xx_i2s_resume(s);
+        return;
+    }
+
+    if (s->play_pos <= s->last_free &&
+        s->play_pos + block_size > s->last_free) {
+        s5pc1xx_i2s_resume(s);
+        return;
+    }
+
+    codec_buffer = wm8994_dac_buffer(s->wm8994, block_size);
+    memcpy(codec_buffer, s->buffer + s->play_pos, block_size);
+    s->play_pos = (s->play_pos + block_size) % s->buf_size;
+
+    s5pc1xx_i2s_resume(s);
+
+    wm8994_dac_commit(s->wm8994);
+}
+
+/* I2S write function */
+static void s5pc1xx_i2s_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+    int use_buf = 0;
+
+    switch (offset) {
+    case S5PC1XX_I2S_IISCON:
+        if ((value & S5PC1XX_IISCON_SWRESET) &&
+            !(s->iiscon & S5PC1XX_IISCON_SWRESET)) {
+            s5pc1xx_i2s_reset(s);
+        }
+        if (I2S_TOGGLE_BIT(s->iiscon, value, S5PC1XX_IISCON_TXCHPAUSE)) {
+            if (value & S5PC1XX_IISCON_TXCHPAUSE) {
+                s5pc1xx_i2s_stop(s);
+            } else {
+                s5pc1xx_i2s_resume(s);
+            }
+        }
+
+        if (I2S_TOGGLE_BIT(s->iiscon, value, S5PC1XX_IISCON_TXSDMACTIVE)) {
+            /*TODO: stop dma if (value & S5PC1XX_IISCON_TXSDMACTIVE) is 0*/
+        }
+
+        s->iiscon = (s->iiscon & ~S5PC1XX_IISCON_WRITE_MASK) |
+                    (value & S5PC1XX_IISCON_WRITE_MASK);
+
+
+        if (!(s->iiscon & S5PC1XX_IISCON_I2SACTIVE)) {
+            s->play_pos = 0;
+            s->last_free = 0;
+            s5pc1xx_i2s_resume(s);
+        }
+
+        /* FIXME: Kernel wants this bit for synchronization. Fix this line */
+        s->iiscon |= S5PC1XX_IISCON_LRI;
+        break;
+    case S5PC1XX_I2S_IISMOD:
+        s->iismod = value;
+        break;
+    case S5PC1XX_I2S_IISFIC:
+        s->iisfic = value;
+        break;
+    case S5PC1XX_I2S_IISPSR:
+        s->iispsr = value;
+        break;
+    case S5PC1XX_I2S_IISTXDS:
+    case S5PC1XX_I2S_IISTXD:
+        if (!s5pc1xx_i2s_transmit(s))
+            break;
+
+        if ( (s->iismod & S5PC1XX_IISMOD_LRP) &&
+            ((s->iismod & S5PC1XX_IISMOD_BLCMASK) == S5PC1XX_IISMOD_16BIT ||
+             (s->iismod & S5PC1XX_IISMOD_BLCMASK) == S5PC1XX_IISMOD_8BIT)) {
+            *(uint32_t *)(s->buffer + s->last_free) =
+                (value << 16) | (value >> 16);
+        } else {
+            *(uint32_t *)(s->buffer + s->last_free) = value;
+        }
+
+        s->last_free = (s->last_free + sizeof(value)) % s->buf_size;
+
+        use_buf = s->last_free - s->play_pos;
+        if (use_buf < 0) {
+            use_buf = s->buf_size + use_buf;
+        }
+
+        if (use_buf >= AUDIO_WORDS_SEND*4) {
+            s5pc1xx_i2s_stop(s);
+        }
+        break;
+    case S5PC1XX_I2S_IISFICS:
+        s->iisfics = value;
+        break;
+    case S5PC1XX_I2S_IISAHB:
+        s->iisahb = value;
+        break;
+    case S5PC1XX_I2S_IISSTR:
+        s->iisstr0 = value;
+        break;
+    case S5PC1XX_I2S_IISSIZE:
+        s->iissize = value;
+        break;
+    case S5PC1XX_I2S_IISTRNCNT:
+        s->iistrncnt = value;
+        break;
+    case S5PC1XX_I2S_IISADDR0:
+        s->iislvl0addr = value;
+        break;
+    case S5PC1XX_I2S_IISADDR1:
+        s->iislvl1addr = value;
+        break;
+    case S5PC1XX_I2S_IISADDR2:
+        s->iislvl2addr = value;
+        break;
+    case S5PC1XX_I2S_IISADDR3:
+        s->iislvl3addr = value;
+        break;
+    case S5PC1XX_I2S_IISSTR1:
+        s->iisstr1 = value;
+        break;
+    default:
+        /* FIXME: all registers are accessible by byte */
+        hw_error("s5pc1xx.i2s: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* I2S read function */
+static uint32_t s5pc1xx_i2s_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+
+    switch (offset) {
+    case S5PC1XX_I2S_IISCON:
+        return s->iiscon;
+    case S5PC1XX_I2S_IISMOD:
+        return s->iismod;
+    case S5PC1XX_I2S_IISFIC:
+        return s->iisfic;
+    case S5PC1XX_I2S_IISPSR:
+        return s->iispsr;
+    case S5PC1XX_I2S_IISRXD:
+        /* TODO: Support receive data register */
+        return 0;
+    case S5PC1XX_I2S_IISFICS:
+        return s->iisfics;
+    case S5PC1XX_I2S_IISAHB:
+        return s->iisahb;
+    case S5PC1XX_I2S_IISSTR:
+        return s->iisstr0;
+    case S5PC1XX_I2S_IISSIZE:
+        return s->iissize;
+    case S5PC1XX_I2S_IISTRNCNT:
+        return s->iistrncnt;
+    case S5PC1XX_I2S_IISADDR0:
+        return s->iislvl0addr;
+    case S5PC1XX_I2S_IISADDR1:
+        return s->iislvl1addr;
+    case S5PC1XX_I2S_IISADDR2:
+        return s->iislvl2addr;
+    case S5PC1XX_I2S_IISADDR3:
+        return s->iislvl3addr;
+    case S5PC1XX_I2S_IISSTR1:
+        return s->iisstr1;
+    default:
+        /* FIXME: all registers are accessible by byte */
+        hw_error("s5pc1xx.i2s: bad write offset " TARGET_FMT_plx "\n", offset);
+        return 0;
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_i2s_readfn[] = {
+    s5pc1xx_i2s_read,
+    s5pc1xx_i2s_read,
+    s5pc1xx_i2s_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_i2s_writefn[] = {
+    s5pc1xx_i2s_write,
+    s5pc1xx_i2s_write,
+    s5pc1xx_i2s_write
+};
+
+static void s5pc1xx_i2s_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+    qemu_put_be32s(f, &s->iiscon);
+    qemu_put_be32s(f, &s->iismod);
+    qemu_put_be32s(f, &s->iisfic);
+    qemu_put_be32s(f, &s->iispsr);
+    qemu_put_be32s(f, &s->iisfics);
+    qemu_put_be32s(f, &s->iisahb);
+    qemu_put_be32s(f, &s->iisstr0);
+    qemu_put_be32s(f, &s->iissize);
+    qemu_put_be32s(f, &s->iistrncnt);
+    qemu_put_be32s(f, &s->iislvl0addr);
+    qemu_put_be32s(f, &s->iislvl1addr);
+    qemu_put_be32s(f, &s->iislvl2addr);
+    qemu_put_be32s(f, &s->iislvl3addr);
+    qemu_put_be32s(f, &s->iisstr1);
+
+    qemu_put_buffer(f, s->buffer, s->buf_size);
+
+    qemu_put_be32s(f, &s->buf_size);
+    qemu_put_be32s(f, &s->play_pos);
+    qemu_put_be32s(f, &s->last_free);
+}
+
+static int s5pc1xx_i2s_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->iiscon);
+    qemu_get_be32s(f, &s->iismod);
+    qemu_get_be32s(f, &s->iisfic);
+    qemu_get_be32s(f, &s->iispsr);
+    qemu_get_be32s(f, &s->iisfics);
+    qemu_get_be32s(f, &s->iisahb);
+    qemu_get_be32s(f, &s->iisstr0);
+    qemu_get_be32s(f, &s->iissize);
+    qemu_get_be32s(f, &s->iistrncnt);
+    qemu_get_be32s(f, &s->iislvl0addr);
+    qemu_get_be32s(f, &s->iislvl1addr);
+    qemu_get_be32s(f, &s->iislvl2addr);
+    qemu_get_be32s(f, &s->iislvl3addr);
+    qemu_get_be32s(f, &s->iisstr1);
+
+    qemu_get_buffer(f, s->buffer, s->buf_size);
+
+    qemu_get_be32s(f, &s->buf_size);
+    qemu_get_be32s(f, &s->play_pos);
+    qemu_get_be32s(f, &s->last_free);
+
+    return 0;
+}
+
+/* I2S init */
+DeviceState *s5pc1xx_i2s_init(target_phys_addr_t base, qemu_irq irq,
+                              DeviceState *wm8994_dev, qemu_irq dma_irq1,
+                              qemu_irq dma_irq2)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.i2s");
+
+    qdev_prop_set_ptr(dev, "wm8994", wm8994_dev);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 1, dma_irq1);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 2, dma_irq2);
+    return dev;
+}
+
+static int s5pc1xx_i2s_init1(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxI2SState *s = FROM_SYSBUS(S5pc1xxI2SState, dev);
+
+    sysbus_init_irq(dev, &s->irq);
+    sysbus_init_irq(dev, &s->dma_irq_stop1);
+    sysbus_init_irq(dev, &s->dma_irq_stop2);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_i2s_readfn, s5pc1xx_i2s_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_I2S_REG_MEM_SIZE, iomemtype);
+    s->buf_size = AUDIO_BUFFER_SIZE;
+    s->buffer = qemu_malloc(s->buf_size);
+
+    s5pc1xx_i2s_reset(s);
+    qemu_register_reset(s5pc1xx_i2s_reset, s);
+    wm8994_data_req_set(s->wm8994, s5pc1xx_i2s_audio_callback, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.i2s", -1, 1,
+                    s5pc1xx_i2s_save, s5pc1xx_i2s_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_i2s_info = {
+    .init = s5pc1xx_i2s_init1,
+    .qdev.name  = "s5pc1xx.i2s",
+    .qdev.size  = sizeof(S5pc1xxI2SState),
+    .qdev.props = (Property[]) {
+        {
+            .name   = "wm8994",
+            .info   = &qdev_prop_ptr,
+            .offset = offsetof(S5pc1xxI2SState, wm8994),
+        },
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_i2s_register_devices(void)
+{
+    sysbus_register_withprop(&s5pc1xx_i2s_info);
+}
+
+device_init(s5pc1xx_i2s_register_devices)
diff --git a/hw/s5pc1xx_jpeg.c b/hw/s5pc1xx_jpeg.c
new file mode 100644 (file)
index 0000000..7cdcdc7
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * JPEG codec for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "qemu-common.h"
+#include "sysbus.h"
+
+#ifdef CONFIG_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+/* Control registers */
+#define JPGMOD      0x000       /* R/W Specifies the Sub-sampling Mode Register 0x0000_0000 */
+    #define PROC_MODE           (1 << 3)
+
+#define JPGOPR      0x004       /* R Specifies the Operation Status Register 0x0000_0000 */
+#define QTBL        0x008       /* R/W Specifies the Quantization Table Number Register 0x0000_0000 */
+#define HTBL        0x00C       /* R/W Specifies the Huffman Table Number Register 0x0000_0000 */
+#define JPGDRI_U    0x010       /* R/W Specifies the MCU, which inserts RST marker(upper 8-bit) 0x0000_0000 */
+#define JPGDRI_L    0x014       /* R/W Specifies the MCU, which inserts RST marker (lower 8-bit) 0x0000_0000 */
+#define JPGY_U      0x018       /* R/W Specifies the Vertical Resolution (upper 8-bit) 0x0000_0000 */
+#define JPGY_L      0x01C       /* R/W Specifies the Vertical Resolution (lower 8-bit) 0x0000_0000 */
+#define JPGX_U      0x020       /* R/W Specifies the Horizontal Resolution (upper 8-bit) 0x0000_0000 */
+#define JPGX_L      0x024       /* R/W Specifies the Horizontal Resolution (lower 8-bit) 0x0000_0000 */
+#define JPGCNT_U    0x028       /* R Specifies the amount of the compressed data in bytes (upper 8- bit) 0x0000_0000 */
+#define JPGCNT_M    0x02C       /* R Specifies the amount of the compressed data in bytes (middle 8-bit) 0x0000_0000 */
+#define JPGCNT_L    0x030       /* R Specifies the amount of the compressed data in bytes (lower 8-bit) 0x0000_0000 */
+#define JPGINTSE    0x034       /* R/W Specifies the Interrupt Setting Register 0x0000_0000 */
+#define JPGINTST    0x038       /* R Specifies the Interrupt Status Register 0x0000_0000 */
+    #define RESULT_STAT         (1 << 6)
+    #define STREAM_STAT         (1 << 5)
+
+#define JPGCOM      0x04C       /* W Specifies the Command register 0x0000_0000 */
+    #define INT_RELEASE         (1 << 2)
+
+#define IMGADR      0x050       /* R/W Specifies the Source or Destination Image Address 0x0000_0000 */
+#define JPGADR      0x058       /* R/W Specifies the Source or Destination JPEG File Address 0x0000_0000 */
+#define COEF1       0x05C       /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define COEF2       0x060       /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define COEF3       0x064       /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define JPGCMOD     0x068       /* R/W Specifies the Mode Selection and Core Clock Setting 0x0000_0020 */
+#define JPGCLKCON   0x06C       /* R/W Specifies the Power On/ Off and Clock Down Control 0x0000_0002 */
+#define JSTART      0x070       /* W Specifies the Start Compression or Decompression 0x0000_0000 */
+#define SW_RESET    0x078       /* R/W Specifies the S/W Reset 0x0000_0000 */
+#define TIMER_SE    0x07C       /* R/W Specifies the Internal Timer Setting Register 0x7FFF_FFFF */
+    #define TIMER_INT_EN        (1 << 31)
+
+#define TIMER_ST    0x080       /* R/W Specifies the Internal Timer Status Register 0x7FFF_FFFF */
+    #define TIMER_INT_STAT      (1 << 31)
+
+#define COMSTAT     0x084       /* R Specifies the Command Status Register 0x0000_0000 */
+#define OUTFORM     0x088       /* R/W Specifies the Output Color Format of Decompression 0x0000_0000 */
+#define VERSION     0x08C       /* R Specifies the Version Register 0x0000_0003 */
+#define ENC_STREAM_INTSE 0x098  /* R/W Specifies the Compressed Stream Size Interrupt Setting Register 0x00FF_FFE0 */
+    #define ENC_STREAM_INT_EN   (1 << 24)
+
+#define ENC_STREAM_INTST 0x09C  /* R Specifies the Compressed Stream Size Interrupt Status Register 0x0000_0000 */
+    #define ENC_STREAM_INT_STAT (1 << 0)
+
+#define QTBL0(n)    (0x400 + (n << 2))   /* R/W Specifies the Quantization table 0 0x0000_0000 */
+#define QTBL1(n)    (0x500 + (n << 2))   /* R/W Specifies the Quantization table 1 0x0000_0000 */
+#define QTBL2(n)    (0x600 + (n << 2))   /* R/W Specifies the Quantization table 2 0x0000_0000 */
+#define QTBL3(n)    (0x700 + (n << 2))   /* R/W Specifies the Quantization table 3 0x0000_0000 */
+#define HDCTBL0(n)  (0x800 + (n << 2))   /* W Specifies the Huffman DC Table 0 - the number of code per code length 0x0000_0000 */
+#define HDCTBLG0(n) (0x840 + (n << 2))   /* W Specifies the Huffman DC Table 0 - Group number of the order for occurrence 0x0000_0000 */
+#define HACTBL0(n)  (0x880 + (n << 2))   /* W Specifies the Huffman AC Table 0 - the number of code per code length 0x0000_0000 */
+#define HACTBLG0(n) (0x8C0 + (n << 2))   /* W Specifies the Huffman AC Table 0 - Group number of the order for occurrence 0x0000_0000 */
+#define HDCTBL1(n)  (0xC00 + (n << 2))   /* W Specifies the Huffman DC Table 1 - the number of code per code length 0x0000_0000 */
+#define HDCTBLG1(n) (0xC40 + (n << 2))   /* W Specifies the Huffman DC Table 1 - Group number of the order for occurrence 0x0000_0000 */
+#define HACTBL1(n)  (0xC80 + (n << 2))   /* W Specifies the Huffman AC Table 1 - the number of code per code length 0x0000_0000 */
+#define HACTBLG1(n) (0xCC0 + (n << 2))   /* W Specifies the Huffman AC Table 1 - Group number of the order for occurrence 0x0000_0000 */
+
+#define S5PC1XX_JPEG_REG_MEM_SIZE   0xF48
+
+/* FIXME: jpeg header size (8192 bytes) is an experimental value */
+#define JPEG_HDR_SIZE   8192
+#define MAX_IMG_SIZE   (8192 * 8192 * 3)
+
+/* Control values */
+#define COMPR           0
+#define DECOMPR         1
+
+#define YCbCr_422    (((4 + 2 + 2) * 2) | (16 << 5) | (8  << 10))  /* YCbCr4:2:2, x2 bits a sample, MCU block size = 16x8 */
+#define RGB_565        (5 + 6 + 5)                                 /* RGB565, x1 bits a sample */
+
+#define YCbCr_444    (((4 + 4 + 4) * 2) | (8  << 5) | (8  << 10))
+#define YCbCr_420    (((4 + 2 + 0) * 2) | (16 << 5) | (16 << 10))
+#define GRAY         (((4 + 0 + 0) * 2) | (8  << 5) | (8  << 10))
+
+#define JUST_RAISE_IRQ   0
+#define RESULT_INT      (1 << 0)
+#define STREAM_INT      (1 << 1)
+#define TIMER_INT       (1 << 2)
+#define ENC_STREAM_INT  (1 << 3)
+
+#define LOW             1
+#define HIGH            0
+#define NONE            0
+
+/* Arithmetical macroses */
+#define SIZE_OUT(value, min)    ((((value) + (min) - 1) / (min)) * (min))   /* evaluate a length in 'min' units */
+#define ALL_BITS(b,a)           (((1 << ((b) - (a) + 1)) - 1) << (a))       /* all bits from 'b' to 'a' are high */
+
+
+#ifdef CONFIG_JPEG
+typedef struct jpeg_compress_struct CInfo;
+typedef struct jpeg_decompress_struct DInfo;
+
+typedef struct Dummy_CInfo {
+    JDIMENSION      image_width;
+    JDIMENSION      image_height;
+    J_COLOR_SPACE   in_color_space;
+    J_COLOR_SPACE   jpeg_color_space;
+    struct comp_info {
+        int h_samp_factor;
+        int v_samp_factor;
+    } comp_info[3];
+} Dummy_CInfo;
+
+typedef struct Dummy_DInfo {
+    JDIMENSION      image_width;
+    JDIMENSION      image_height;
+    J_COLOR_SPACE   out_color_space;
+} Dummy_DInfo;
+#endif
+
+typedef struct S5pc1xxJpegState {
+    SysBusDevice busdev;
+    qemu_irq     irq;
+    uint8_t      proc_mode, c1;
+    uint32_t     byte_cnt, src_len, dst_len;
+    uint32_t     mode_in, mode_out;
+    target_phys_addr_t src_addr, dst_addr;
+    char         *src_base, *dst_base;
+
+    /* Control registers */
+    uint32_t     jpgmod;
+    uint32_t     jpgopr;
+    uint32_t     qtbl;
+    uint32_t     htbl;
+    uint32_t     jpgdri_u;
+    uint32_t     jpgdri_l;
+    uint32_t     jpgy_u;
+    uint32_t     jpgy_l;
+    uint32_t     jpgx_u;
+    uint32_t     jpgx_l;
+    uint32_t     jpgintse;
+    uint32_t     jpgintst;
+    uint32_t     imgadr;
+    uint32_t     jpgadr;
+    uint32_t     coef1;
+    uint32_t     coef2;
+    uint32_t     coef3;
+    uint32_t     jpgcmod;
+    uint32_t     jpgclkcon;
+    uint32_t     sw_reset;
+    uint32_t     timer_se;
+    uint32_t     timer_st;
+    uint32_t     comstat;
+    uint32_t     outform;
+    uint32_t     version;
+    uint32_t     enc_stream_intse;
+    uint32_t     enc_stream_intst;
+    uint8_t      qtbl0[64];
+    uint8_t      qtbl1[64];
+    uint8_t      qtbl2[64];
+    uint8_t      qtbl3[64];
+    uint8_t      hdctbl0[28];
+    uint8_t      hactbl0[178];
+    uint8_t      hdctbl1[28];
+    uint8_t      hactbl1[178];
+
+#ifdef CONFIG_JPEG
+    /* Two structures below are used to protect corresponding values
+     * in CInfo and DInfo from overwriting */
+    Dummy_CInfo *dummy_cinfo;
+    Dummy_DInfo *dummy_dinfo;
+
+    CInfo *cinfo;
+    DInfo *dinfo;
+#endif
+} S5pc1xxJpegState;
+
+static void s5pc1xx_jpeg_irq(S5pc1xxJpegState *s,
+                             uint8_t irq_stat, short to_clear);
+
+////////// Special code (only when configured with libjpeg support) //////////
+
+#ifdef CONFIG_JPEG
+/* Combine image properties before compression */
+static void s5pc1xx_jpeg_pre_coding(S5pc1xxJpegState *s)
+{
+    uint8_t  src_color_mode, dst_color_mode;
+    uint16_t cols_out, rows_out;
+
+    Dummy_CInfo *dummy_cinfo = s->dummy_cinfo;
+
+    /* Input and output images addresses */
+    s->src_addr = s->imgadr;
+    s->dst_addr = s->jpgadr;
+
+    /* Input image area */
+    dummy_cinfo->image_width  = (s->jpgx_u << 8) | s->jpgx_l;
+    dummy_cinfo->image_height = (s->jpgy_u << 8) | s->jpgy_l;
+
+    /* Coeff for color mode converting */
+    s->c1 = (s->jpgcmod & 0x2) << 3;       /* 0 or 16 */
+
+    /* Input image color mode */
+    src_color_mode = (s->jpgcmod & ALL_BITS(7, 5)) >> 5;
+    switch (src_color_mode) {
+    case 0x1:
+        s->mode_in = YCbCr_422;
+        dummy_cinfo->in_color_space = JCS_YCbCr;
+        break;
+    case 0x2:
+        s->mode_in = RGB_565;
+        dummy_cinfo->in_color_space = JCS_RGB;
+        break;
+    default:
+        hw_error("s5pc1xx.jpeg: bad input color space (num = %u)\n",
+                 src_color_mode);
+    }
+
+    /* Input image size */
+    s->src_len = (dummy_cinfo->image_width * (s->mode_in & ALL_BITS(4, 0)) *
+                  dummy_cinfo->image_height) >> 3;
+
+    /* Output image color mode */
+    dst_color_mode = (s->jpgmod  & ALL_BITS(2, 0));
+    dummy_cinfo->comp_info[0].h_samp_factor = 2;
+    dummy_cinfo->comp_info[0].v_samp_factor = 2;
+    switch (dst_color_mode) {
+    case 0x1:
+        s->mode_out = YCbCr_422;
+        dummy_cinfo->jpeg_color_space = JCS_YCbCr;
+        dummy_cinfo->comp_info[1].h_samp_factor = 1;
+        dummy_cinfo->comp_info[1].v_samp_factor = 2;
+        dummy_cinfo->comp_info[2].h_samp_factor = 1;
+        dummy_cinfo->comp_info[2].v_samp_factor = 2;
+        break;
+    case 0x2:
+        s->mode_out = YCbCr_420;
+        dummy_cinfo->jpeg_color_space = JCS_YCbCr;
+        dummy_cinfo->comp_info[1].h_samp_factor = 1;
+        dummy_cinfo->comp_info[1].v_samp_factor = 1;
+        dummy_cinfo->comp_info[2].h_samp_factor = 1;
+        dummy_cinfo->comp_info[2].v_samp_factor = 1;
+        break;
+    default:
+        hw_error("s5pc1xx.jpeg: bad output color space (num = %u)\n",
+                 dst_color_mode);
+    }
+
+    /* Output image area */
+    cols_out = SIZE_OUT(dummy_cinfo->image_width,
+                        (s->mode_out >> 5) & ALL_BITS(4, 0));
+    rows_out = SIZE_OUT(dummy_cinfo->image_height,
+                        (s->mode_out >> 10) & ALL_BITS(4, 0));
+
+    /* Output image size */
+    s->dst_len = JPEG_HDR_SIZE +
+                 ((cols_out * (s->mode_out & ALL_BITS(4, 0)) * rows_out) >> 3);
+}
+
+/* Calculate JPEG data size */
+static uint32_t s5pc1xx_jpeg_data_size(S5pc1xxJpegState *s)
+{
+    target_phys_addr_t page_size = TARGET_PAGE_SIZE;
+    uint8_t *jpg_Buf = NULL;
+    int i, j;
+
+    s->dummy_dinfo->image_height =
+    s->dummy_dinfo->image_width  = 0;
+
+    /* Travel over all memory pages occupied by the image */
+    for (i = 0, j = 1; i < (MAX_IMG_SIZE + JPEG_HDR_SIZE); i++) {
+        /* Map the first memory page or map the next memory page if less
+         * than 9 bytes till the end of the current one remains */
+        if (!i || ((i % TARGET_PAGE_SIZE) > (TARGET_PAGE_SIZE - 9))) {
+            jpg_Buf = cpu_physical_memory_map(s->jpgadr, &page_size, 0);
+            if (!jpg_Buf || (page_size != TARGET_PAGE_SIZE * j)) {
+                fprintf(stderr,
+                        "s5pc1xx.jpeg: input memory can't be accessed\n");
+                /* Raise result interrupt as NOT OK */
+                s5pc1xx_jpeg_irq(s, STREAM_INT, HIGH);
+                return 0;
+            }
+            j++;
+            page_size = TARGET_PAGE_SIZE * j;
+        }
+
+        if (jpg_Buf[i] == 0xFF) {
+            if (jpg_Buf[i + 1] == 0xC0) {
+                s->dummy_dinfo->image_height =
+                    (jpg_Buf[i + 5] << 8) | jpg_Buf[i + 6];
+                s->dummy_dinfo->image_width =
+                    (jpg_Buf[i + 7] << 8) | jpg_Buf[i + 8];
+            }
+            if (jpg_Buf[i + 1] == 0xD9)
+                return (i + 2); /* Resulting size of the image */
+        }
+    }
+
+    fprintf(stderr, "s5pc1xx.jpeg: input image end can't be reached\n");
+    /* Raise result interrupt as NOT OK */
+    s5pc1xx_jpeg_irq(s, STREAM_INT, HIGH);
+    return 0;
+}
+
+/* Combine image properties before decompression */
+static void s5pc1xx_jpeg_pre_decoding(S5pc1xxJpegState *s)
+{
+    uint8_t  dst_color_mode;
+    uint16_t cols_out, rows_out;
+
+    Dummy_DInfo *dummy_dinfo = s->dummy_dinfo;
+
+    /* Input and output image addresses */
+    s->src_addr = s->jpgadr;
+    s->dst_addr = s->imgadr;
+
+    s->src_len = s5pc1xx_jpeg_data_size(s);
+
+    /* Output image color mode */
+    dst_color_mode = (s->outform & 0x1);
+    switch (dst_color_mode) {
+    case 0x0:
+        s->mode_out = YCbCr_422;
+        dummy_dinfo->out_color_space = JCS_YCbCr;
+        break;
+    case 0x1:
+        s->mode_out = YCbCr_420;
+        dummy_dinfo->out_color_space = JCS_YCbCr;
+        break;
+    default:
+        hw_error("s5pc1xx.jpeg: bad output color space (num = %u)\n",
+                 dst_color_mode);
+    }
+
+    /* Output image area */
+    cols_out = SIZE_OUT(dummy_dinfo->image_width,
+                        s->mode_out >> 5 & ALL_BITS(4, 0));
+    rows_out = SIZE_OUT(dummy_dinfo->image_height,
+                        s->mode_out >> 10 & ALL_BITS(4, 0));
+
+    /* Output image size */
+    s->dst_len = (cols_out * (s->mode_out & ALL_BITS(4, 0)) * rows_out) >> 3;
+}
+
+/* Get references to input and output data */
+static int s5pc1xx_jpeg_mem_map(S5pc1xxJpegState *s)
+{
+    target_phys_addr_t src_len, dst_len;
+
+    src_len = s->src_len;
+    dst_len = s->dst_len;
+
+    s->src_base = cpu_physical_memory_map(s->src_addr, &src_len, 0);
+    s->dst_base = cpu_physical_memory_map(s->dst_addr, &dst_len, 0);
+
+    if (!s->src_base || !s->dst_base) {
+        fprintf(stderr, "s5pc1xx.jpeg: bad image address\n");
+        /* Raise result interrupt as NOT OK */
+        s5pc1xx_jpeg_irq(s, JUST_RAISE_IRQ, NONE);
+        return 1;
+    }
+
+    if ((src_len == s->src_len) && (dst_len == s->dst_len))
+        return 0;
+
+    cpu_physical_memory_unmap(s->src_base, src_len, 0, 0);
+    cpu_physical_memory_unmap(s->dst_base, dst_len, 0, 0);
+    fprintf(stderr, "s5pc1xx.jpeg: not enough memory for image\n");
+    /* Raise result interrupt as NOT OK */
+    s5pc1xx_jpeg_irq(s, JUST_RAISE_IRQ, NONE);
+    return 1;
+}
+
+/* JPEG compression */
+static void s5pc1xx_jpeg_coding(S5pc1xxJpegState *s)
+{
+    int row_stride, i;
+    struct jpeg_error_mgr jerr;
+    JSAMPROW row_pointer[1];
+
+    CInfo *cinfo = s->cinfo;
+    Dummy_CInfo *dummy_cinfo = s->dummy_cinfo;
+
+    /* Allocate and initialize JPEG compression object */
+    cinfo->err = jpeg_std_error(&jerr);
+    jpeg_create_compress(cinfo);
+
+    cinfo->dest->next_output_byte = (JSAMPLE *) s->dst_base;
+    cinfo->dest->free_in_buffer   = s->dst_len;
+
+    /* RGB or YCbCr (3 components) can be used as compressor input color space
+     * according to s5pc1xx specification */
+    cinfo->input_components = 3;
+    cinfo->image_width      = dummy_cinfo->image_width;
+    cinfo->image_height     = dummy_cinfo->image_height;
+    cinfo->in_color_space   = dummy_cinfo->in_color_space;
+
+    jpeg_set_defaults(cinfo);
+
+    /* Note: using dummy_cinfo->jpeg_color_space instead of
+     * cinfo->jpeg_color_space because the second one is set by
+     * jpeg_set_defaults() and may be incorrect */
+    jpeg_set_colorspace(cinfo, dummy_cinfo->jpeg_color_space);
+
+#if JPEG_LIB_VERSION >= 61
+
+    for (i = 0; i < 4; i++) {
+        cinfo->comp_info[i].quant_tbl_no = (s->qtbl >> (i * 2)) & 0x3;
+        cinfo->comp_info[i].dc_tbl_no    = (s->htbl >> (i * 2)) & 0x1;
+        cinfo->comp_info[i].ac_tbl_no    = (s->htbl >> (i * 2 + 1)) & 0x1;
+    }
+
+    /* FIXME: force_baseline may be 'false' in the next four instructions */
+    jpeg_add_quant_table (cinfo, 0,
+                          (const unsigned int *) s->qtbl0,
+                          100, true);
+    jpeg_add_quant_table (cinfo, 1,
+                          (const unsigned int *) s->qtbl1,
+                          100, true);
+    jpeg_add_quant_table (cinfo, 2,
+                          (const unsigned int *) s->qtbl2,
+                          100, true);
+    jpeg_add_quant_table (cinfo, 3,
+                          (const unsigned int *) s->qtbl3,
+                          100, true);
+
+    cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL *) s->hdctbl0;
+    cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL *) s->hactbl0;
+
+    cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL *) s->hdctbl1;
+    cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL *) s->hactbl1;
+
+    jpeg_alloc_huff_table((j_common_ptr) cinfo);
+
+#endif
+
+    for (i = 0; i < 3; i++) {
+        cinfo->comp_info[i].h_samp_factor =
+            dummy_cinfo->comp_info[i].h_samp_factor;
+        cinfo->comp_info[i].v_samp_factor =
+            dummy_cinfo->comp_info[i].v_samp_factor;
+    }
+
+    jpeg_start_compress(cinfo, FALSE);
+
+    /* JSAMPLEs per row in input_buffer */
+    row_stride = cinfo->image_width * cinfo->input_components;
+
+    while (cinfo->next_scanline < cinfo->image_height) {
+        row_pointer[0] =
+            (JSAMPROW) &s->src_base[cinfo->next_scanline * row_stride];
+        (void) jpeg_write_scanlines(cinfo, row_pointer, 1);
+    }
+
+    /* Finish compression */
+    jpeg_finish_compress(cinfo);
+    jpeg_destroy_compress(cinfo);
+}
+
+/* JPEG decompression */
+static void s5pc1xx_jpeg_decoding(S5pc1xxJpegState *s)
+{
+    struct jpeg_error_mgr jerr;
+    JSAMPARRAY buffer;  /* Output row buffer */
+    int row_stride;     /* Physical row width in output buffer */
+    int i, count;
+
+    DInfo *dinfo = s->dinfo;
+    Dummy_DInfo *dummy_dinfo = s->dummy_dinfo;
+
+    /* Allocate and initialize JPEG decompression object */
+    dinfo->err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(dinfo);
+
+    dinfo->src->next_input_byte = (JSAMPLE *) s->src_base;
+    dinfo->src->bytes_in_buffer = s->src_len;
+
+    (void) jpeg_read_header(dinfo, TRUE);
+
+    dinfo->out_color_space = dummy_dinfo->out_color_space;
+
+    (void) jpeg_start_decompress(dinfo);
+
+    /* JSAMPLEs per row in output buffer */
+    row_stride = dinfo->output_width * dinfo->output_components;
+
+    /* Make a one-row-high sample array
+     * that will go away when done with image */
+    buffer = (*(dinfo->mem->alloc_sarray))
+        ((j_common_ptr) dinfo, JPOOL_IMAGE, row_stride, 1);
+
+    count = 0;
+    while (dinfo->output_scanline < dinfo->output_height) {
+        (void) jpeg_read_scanlines(dinfo, buffer, 1);
+
+        for (i = 0; i < row_stride; i++, count++)
+            s->dst_base[count] = (char) buffer[0][i];
+    }
+
+    /* Finish decompression */
+    (void) jpeg_finish_decompress(dinfo);
+    jpeg_destroy_decompress(dinfo);
+}
+#endif
+
+//////////////////////////// Common code //////////////////////////////////////
+
+/* Reset JPEG controller */
+static void s5pc1xx_jpeg_reset(void *opaque)
+{
+    S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+    int i;
+
+    s->jpgmod           = 0x1;
+    s->jpgopr           = 0x0;
+    s->qtbl             = 0x0;
+    s->htbl             = 0x0;
+    s->jpgdri_u         = 0x0;
+    s->jpgdri_l         = 0x0;
+    s->jpgy_u           = 0x0;
+    s->jpgy_l           = 0x0;
+    s->jpgx_u           = 0x0;
+    s->jpgx_l           = 0x0;
+    s->jpgintse         = 0x0;
+    s->jpgintst         = 0x0;
+    s->imgadr           = 0x0;
+    s->jpgadr           = 0x0;
+    s->coef1            = 0x0;
+    s->coef2            = 0x0;
+    s->coef3            = 0x0;
+    s->jpgcmod          = 0x00000020;
+    s->jpgclkcon        = 0x00000002;
+    s->sw_reset         = 0x0;
+    s->timer_se         = 0x7FFFFFFF;
+    s->timer_st         = 0x7FFFFFFF;
+    s->comstat          = 0x0;
+    s->outform          = 0x0;
+    s->version          = 0x00000003;
+    s->enc_stream_intse = 0x00FFFFE0;
+    s->enc_stream_intst = 0x0;
+
+    for(i = 0; i < 178; i++) {
+        s->hactbl0[i] = 0x0;
+        s->hactbl1[i] = 0x0;
+
+        if (i > 63)
+            continue;
+
+        s->qtbl0[i]    = 0x0;
+        s->qtbl1[i]    = 0x0;
+        s->qtbl2[i]    = 0x0;
+        s->qtbl3[i]    = 0x0;
+
+        if (i > 28)
+            continue;
+
+        s->hdctbl0[i]  = 0x0;
+        s->hdctbl1[i]  = 0x0;
+    }
+}
+
+/* Interrupts handler */
+static void s5pc1xx_jpeg_irq(S5pc1xxJpegState *s,
+                             uint8_t irq_stat, short to_clear)
+{
+    switch(irq_stat) {
+    case JUST_RAISE_IRQ:
+        qemu_irq_raise(s->irq);
+        return;
+    case RESULT_INT:
+        if (to_clear) {
+            s->jpgintst &= ~RESULT_STAT;
+            break;
+        }
+        s->jpgintst |= RESULT_STAT;
+        qemu_irq_raise(s->irq);
+        return;
+    case STREAM_INT:
+        if (to_clear) {
+            s->jpgintst &= ~STREAM_STAT;
+            break;
+        }
+        s->jpgintst |= STREAM_STAT;
+        qemu_irq_raise(s->irq);
+        return;
+    case TIMER_INT:
+        if (to_clear) {
+            s->timer_st &= ~TIMER_INT_STAT;
+            break;
+        }
+        s->timer_st |= TIMER_INT_STAT;
+        if (s->timer_se & TIMER_INT_EN)
+            qemu_irq_raise(s->irq);
+        return;
+    case ENC_STREAM_INT:
+        if (to_clear) {
+            s->enc_stream_intst &= ~ENC_STREAM_INT_STAT;
+            break;
+        }
+        s->enc_stream_intst |= ENC_STREAM_INT_STAT;
+        if (s->enc_stream_intse & ENC_STREAM_INT_EN)
+            qemu_irq_raise(s->irq);
+        return;
+    default:
+        return;
+    }
+
+    /* Lower irq if all states are clear */
+    if (!((s->jpgintst & RESULT_STAT) |
+          (s->jpgintst & STREAM_STAT) |
+          (s->timer_st & TIMER_INT_STAT) |
+          (s->enc_stream_intst & ENC_STREAM_INT_STAT)))
+        qemu_irq_lower(s->irq);
+}
+
+/* JPEG controller registers read */
+static uint32_t s5pc1xx_jpeg_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+    switch(offset) {
+    case JPGMOD:
+#ifdef CONFIG_JPEG
+        if ((s->proc_mode == DECOMPR) && !s->jpgopr) {
+            if (s->dinfo->jpeg_color_space == JCS_GRAYSCALE) {
+                s->jpgmod = (s->jpgmod & ALL_BITS(31, 3)) | 0x3;
+            } else {
+                /* FIXME: the value below must be different
+                 * depending on downsampling coefs */
+                s->jpgmod = (s->jpgmod & ALL_BITS(31, 3)) | 0x0;
+            }
+        }
+#endif
+        return s->jpgmod;
+    case JPGOPR:
+        return s->jpgopr;
+    case QTBL:
+        return s->qtbl;
+    case HTBL:
+        return s->htbl;
+    case JPGDRI_U:
+        return s->jpgdri_u;
+    case JPGDRI_L:
+        return s->jpgdri_l;
+    case JPGY_U:
+        return s->jpgy_u;
+    case JPGY_L:
+        return s->jpgy_l;
+    case JPGX_U:
+        return s->jpgx_u;
+    case JPGX_L:
+        return s->jpgx_l;
+    case JPGCNT_U:
+        if ((s->proc_mode & COMPR) && !s->jpgopr)
+            return (s->byte_cnt >> 16) & ALL_BITS(7, 0);
+        return 0;
+    case JPGCNT_M:
+        if ((s->proc_mode & COMPR) && !s->jpgopr)
+            return (s->byte_cnt >>  8) & ALL_BITS(7, 0);
+        return 0;
+    case JPGCNT_L:
+        if ((s->proc_mode & COMPR)  && !s->jpgopr)
+            return (s->byte_cnt >>  0) & ALL_BITS(7, 0);
+        return 0;
+    case JPGINTSE:
+        return s->jpgintse;
+    case JPGINTST:
+        /* Set successful status if no operation is going on */
+        if (!s->jpgopr)
+            s->jpgintst = 0x40;
+        return s->jpgintst;
+    case IMGADR:
+        return s->imgadr;
+    case JPGADR:
+        return s->jpgadr;
+    case COEF1:
+        return s->coef1;
+    case COEF2:
+        return s->coef2;
+    case COEF3:
+        return s->coef3;
+    case JPGCMOD:
+        return s->jpgcmod;
+    case JPGCLKCON:
+        return s->jpgclkcon;
+    case SW_RESET:
+        return s->sw_reset;
+    case TIMER_SE:
+        return s->timer_se;
+    case TIMER_ST:
+        return s->timer_st;
+    case COMSTAT:
+        return s->comstat;
+    case OUTFORM:
+        return s->outform;
+    case VERSION:
+        return s->version;
+    case ENC_STREAM_INTSE:
+        return s->enc_stream_intse;
+    case ENC_STREAM_INTST:
+        return s->enc_stream_intst;
+    case QTBL0(0) ... QTBL0(63):
+        return s->qtbl0[(offset - QTBL0(0)) >> 2];
+    case QTBL1(0) ... QTBL1(63):
+        return s->qtbl1[(offset - QTBL1(0)) >> 2];
+    case QTBL2(0) ... QTBL2(63):
+        return s->qtbl2[(offset - QTBL2(0)) >> 2];
+    case QTBL3(0) ... QTBL3(63):
+        return s->qtbl3[(offset - QTBL3(0)) >> 2];
+    default:
+        hw_error("s5pc1xx.jpeg: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+/* JPEG controller register write */
+static void s5pc1xx_jpeg_write(void *opaque, target_phys_addr_t offset,
+                               uint32_t value)
+{
+    uint32_t old_val;
+    S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+    switch(offset) {
+    case JPGMOD:
+        old_val      = s->jpgmod;
+        s->proc_mode = (value & PROC_MODE) ? DECOMPR : COMPR;
+        s->jpgmod    = (s->proc_mode == COMPR) ?
+                       value :
+                       ((value & ALL_BITS(31, 3)) | (old_val & ALL_BITS(2, 0)));
+        break;
+    case QTBL:
+        s->qtbl = value;
+        break;
+    case HTBL:
+        s->htbl = value;
+        break;
+    case JPGDRI_U:
+        s->jpgdri_u = value;
+        break;
+    case JPGDRI_L:
+        s->jpgdri_l = value;
+        break;
+    case JPGY_U:
+        s->jpgy_u = value;
+        break;
+    case JPGY_L:
+        s->jpgy_l = value;
+        break;
+    case JPGX_U:
+        s->jpgx_u = value;
+        break;
+    case JPGX_L:
+        s->jpgx_l = value;
+        break;
+    case JPGINTSE:
+        s->jpgintse = value;
+        break;
+    case JPGCOM:
+        if (value & INT_RELEASE) {
+            s5pc1xx_jpeg_irq(s, RESULT_INT, LOW);
+            s5pc1xx_jpeg_irq(s, STREAM_INT, LOW);
+        }
+        break;
+    case IMGADR:
+        s->imgadr = value;
+        break;
+    case JPGADR:
+        s->jpgadr = value;
+        break;
+    case COEF1:
+        s->coef1 = value;
+        break;
+    case COEF2:
+        s->coef2 = value;
+        break;
+    case COEF3:
+        s->coef3 = value;
+        break;
+    case JPGCMOD:
+        s->jpgcmod = value;
+        break;
+    case JPGCLKCON:
+        s->jpgclkcon = value;
+        break;
+    case JSTART:
+        if (!value)
+            break;
+        s->jpgopr = 0x1;
+#ifdef CONFIG_JPEG
+        switch(s->proc_mode) {
+        case COMPR:
+            s5pc1xx_jpeg_pre_coding(s);
+            if (s5pc1xx_jpeg_mem_map(s))
+                break;
+            s5pc1xx_jpeg_coding(s);
+            s->byte_cnt = s5pc1xx_jpeg_data_size(s);
+            /* Raise result interrupt as OK */
+            s5pc1xx_jpeg_irq(s, RESULT_INT, HIGH);
+            break;
+        case DECOMPR:
+            s5pc1xx_jpeg_pre_decoding(s);
+            if (!s->src_len)
+                break;
+            if (s5pc1xx_jpeg_mem_map(s))
+                break;
+            s5pc1xx_jpeg_decoding(s);
+            /* Raise result interrupt as OK */
+            s5pc1xx_jpeg_irq(s, RESULT_INT, HIGH);
+            break;
+        }
+#endif
+        s->jpgopr = 0x0;
+        break;
+    case SW_RESET:
+        if (value) {
+            s->sw_reset = 0x1;
+            s5pc1xx_jpeg_reset(s);
+        }
+        s->sw_reset = 0x0;
+        break;
+    case TIMER_SE:
+        s->timer_se = value;
+        break;
+    case TIMER_ST:
+        s->timer_st = value;
+        if (value & TIMER_INT_STAT)
+            s5pc1xx_jpeg_irq(s, TIMER_INT, LOW);
+        break;
+    case OUTFORM:
+        s->outform = value;
+        break;
+    case ENC_STREAM_INTSE:
+        s->enc_stream_intse = value;
+        break;
+    case ENC_STREAM_INTST:
+        s->enc_stream_intst = value;
+        if (value & ENC_STREAM_INT_STAT)
+            s5pc1xx_jpeg_irq(s, ENC_STREAM_INT, LOW);
+        break;
+    case QTBL0(0) ... QTBL0(63):
+        s->qtbl0[(offset - QTBL0(0)) >> 2] = value & 0xFF;
+        break;
+    case QTBL1(0) ... QTBL1(63):
+        s->qtbl1[(offset - QTBL1(0)) >> 2] = value & 0xFF;
+        break;
+    case QTBL2(0) ... QTBL2(63):
+        s->qtbl2[(offset - QTBL2(0)) >> 2] = value & 0xFF;
+        break;
+    case QTBL3(0) ... QTBL3(63):
+        s->qtbl3[(offset - QTBL3(0)) >> 2] = value & 0xFF;
+        break;
+    case HDCTBL0(0) ... HDCTBL0(15):
+        s->hdctbl0[(offset - HDCTBL0(0)) >> 2] = value & 0xFF;
+        break;
+    case HDCTBLG0(0) ... HDCTBLG0(11):
+        s->hdctbl0[((offset - HDCTBLG0(0)) >> 2) + 16] = value & 0xFF;
+        break;
+    case HACTBL0(0) ... HACTBL0(15):
+        s->hactbl0[(offset - HACTBL0(0)) >> 2] = value & 0xFF;
+        break;
+    case HACTBLG0(0) ... HACTBLG0(161):
+        s->hactbl0[((offset - HACTBLG0(0)) >> 2) + 16] = value & 0xFF;
+        break;
+    case HDCTBL1(0) ... HDCTBL1(15):
+        s->hdctbl1[(offset - HDCTBL1(0)) >> 2] = value & 0xFF;
+        break;
+    case HDCTBLG1(0) ... HDCTBLG1(11):
+        s->hdctbl1[((offset - HDCTBLG1(0)) >> 2) + 16] = value & 0xFF;
+        break;
+    case HACTBL1(0) ... HACTBL1(15):
+        s->hactbl1[(offset - HACTBL1(0)) >> 2] = value & 0xFF;
+        break;
+    case HACTBLG1(0) ... HACTBLG1(161):
+        s->hactbl1[((offset - HACTBLG1(0)) >> 2) + 16] = value & 0xFF;
+        break;
+    default:
+        hw_error("s5pc1xx.jpeg: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_jpeg_readfn[] = {
+   s5pc1xx_jpeg_read,
+   s5pc1xx_jpeg_read,
+   s5pc1xx_jpeg_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_jpeg_writefn[] = {
+   s5pc1xx_jpeg_write,
+   s5pc1xx_jpeg_write,
+   s5pc1xx_jpeg_write
+};
+
+static void s5pc1xx_jpeg_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+    if (s->jpgopr)
+        hw_error("s5pc1xx.jpeg: JPEG process is going on, "
+                 "saving snapshot is not possible\n");
+
+    qemu_put_8s(f, &s->proc_mode);
+
+    qemu_put_be32s(f, &s->jpgmod);
+    qemu_put_be32s(f, &s->jpgopr);
+    qemu_put_be32s(f, &s->qtbl);
+    qemu_put_be32s(f, &s->htbl);
+    qemu_put_be32s(f, &s->jpgdri_u);
+    qemu_put_be32s(f, &s->jpgdri_l);
+    qemu_put_be32s(f, &s->jpgy_u);
+    qemu_put_be32s(f, &s->jpgy_l);
+    qemu_put_be32s(f, &s->jpgx_u);
+    qemu_put_be32s(f, &s->jpgx_l);
+    qemu_put_be32s(f, &s->jpgintse);
+    qemu_put_be32s(f, &s->jpgintst);
+    qemu_put_be32s(f, &s->imgadr);
+    qemu_put_be32s(f, &s->jpgadr);
+    qemu_put_be32s(f, &s->coef1);
+    qemu_put_be32s(f, &s->coef2);
+    qemu_put_be32s(f, &s->coef3);
+    qemu_put_be32s(f, &s->jpgcmod);
+    qemu_put_be32s(f, &s->jpgclkcon);
+    qemu_put_be32s(f, &s->sw_reset);
+    qemu_put_be32s(f, &s->timer_se);
+    qemu_put_be32s(f, &s->timer_st);
+    qemu_put_be32s(f, &s->comstat);
+    qemu_put_be32s(f, &s->outform);
+    qemu_put_be32s(f, &s->version);
+    qemu_put_be32s(f, &s->enc_stream_intse);
+    qemu_put_be32s(f, &s->enc_stream_intst);
+
+    qemu_put_buffer(f, s->qtbl0, sizeof(s->qtbl0));
+    qemu_put_buffer(f, s->qtbl1, sizeof(s->qtbl1));
+    qemu_put_buffer(f, s->qtbl2, sizeof(s->qtbl2));
+    qemu_put_buffer(f, s->qtbl3, sizeof(s->qtbl3));
+    qemu_put_buffer(f, s->hdctbl0, sizeof(s->hdctbl0));
+    qemu_put_buffer(f, s->hactbl0, sizeof(s->hactbl0));
+    qemu_put_buffer(f, s->hdctbl1, sizeof(s->hdctbl1));
+    qemu_put_buffer(f, s->hactbl1, sizeof(s->hactbl1));
+}
+
+static int s5pc1xx_jpeg_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_8s(f, &s->proc_mode);
+
+    qemu_get_be32s(f, &s->jpgmod);
+    qemu_get_be32s(f, &s->jpgopr);
+    qemu_get_be32s(f, &s->qtbl);
+    qemu_get_be32s(f, &s->htbl);
+    qemu_get_be32s(f, &s->jpgdri_u);
+    qemu_get_be32s(f, &s->jpgdri_l);
+    qemu_get_be32s(f, &s->jpgy_u);
+    qemu_get_be32s(f, &s->jpgy_l);
+    qemu_get_be32s(f, &s->jpgx_u);
+    qemu_get_be32s(f, &s->jpgx_l);
+    qemu_get_be32s(f, &s->jpgintse);
+    qemu_get_be32s(f, &s->jpgintst);
+    qemu_get_be32s(f, &s->imgadr);
+    qemu_get_be32s(f, &s->jpgadr);
+    qemu_get_be32s(f, &s->coef1);
+    qemu_get_be32s(f, &s->coef2);
+    qemu_get_be32s(f, &s->coef3);
+    qemu_get_be32s(f, &s->jpgcmod);
+    qemu_get_be32s(f, &s->jpgclkcon);
+    qemu_get_be32s(f, &s->sw_reset);
+    qemu_get_be32s(f, &s->timer_se);
+    qemu_get_be32s(f, &s->timer_st);
+    qemu_get_be32s(f, &s->comstat);
+    qemu_get_be32s(f, &s->outform);
+    qemu_get_be32s(f, &s->version);
+    qemu_get_be32s(f, &s->enc_stream_intse);
+    qemu_get_be32s(f, &s->enc_stream_intst);
+
+    qemu_get_buffer(f, s->qtbl0, sizeof(s->qtbl0));
+    qemu_get_buffer(f, s->qtbl1, sizeof(s->qtbl1));
+    qemu_get_buffer(f, s->qtbl2, sizeof(s->qtbl2));
+    qemu_get_buffer(f, s->qtbl3, sizeof(s->qtbl3));
+    qemu_get_buffer(f, s->hdctbl0, sizeof(s->hdctbl0));
+    qemu_get_buffer(f, s->hactbl0, sizeof(s->hactbl0));
+    qemu_get_buffer(f, s->hdctbl1, sizeof(s->hdctbl1));
+    qemu_get_buffer(f, s->hactbl1, sizeof(s->hactbl1));
+
+    return 0;
+}
+
+/* JPEG initialization */
+static int s5pc1xx_jpeg_init(SysBusDevice *dev)
+{
+    int iomemtype;
+
+    S5pc1xxJpegState *s = FROM_SYSBUS(S5pc1xxJpegState, dev);
+    s5pc1xx_jpeg_reset(s);
+
+#ifdef CONFIG_JPEG
+    s->cinfo = (CInfo *) qemu_mallocz(sizeof(CInfo));
+    s->dinfo = (DInfo *) qemu_mallocz(sizeof(DInfo));
+    s->dummy_cinfo = (Dummy_CInfo *) qemu_mallocz(sizeof(Dummy_CInfo));
+    s->dummy_dinfo = (Dummy_DInfo *) qemu_mallocz(sizeof(Dummy_DInfo));
+#endif
+
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_jpeg_readfn, s5pc1xx_jpeg_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_JPEG_REG_MEM_SIZE, iomemtype);
+
+    qemu_register_reset(s5pc1xx_jpeg_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.jpeg", -1, 1,
+                    s5pc1xx_jpeg_save, s5pc1xx_jpeg_load, s);
+    return 0;
+}
+
+static void s5pc1xx_jpeg_register(void)
+{
+    sysbus_register_dev("s5pc1xx.jpeg", sizeof(S5pc1xxJpegState),
+                        s5pc1xx_jpeg_init);
+}
+
+device_init(s5pc1xx_jpeg_register)
diff --git a/hw/s5pc1xx_keyif.c b/hw/s5pc1xx_keyif.c
new file mode 100644 (file)
index 0000000..9c21905
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ * S5PC1XX Keypad Interface
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "console.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+
+#define KEY_RESERVED    0
+#define KEY_ESC         1
+#define KEY_1           2
+#define KEY_2           3
+#define KEY_3           4
+#define KEY_4           5
+#define KEY_5           6
+#define KEY_6           7
+#define KEY_7           8
+#define KEY_8           9
+#define KEY_9           10
+#define KEY_0           11
+#define KEY_MINUS       12
+#define KEY_EQUAL       13
+#define KEY_BACKSPACE   14
+#define KEY_TAB         15
+#define KEY_Q           16
+#define KEY_W           17
+#define KEY_E           18
+#define KEY_R           19
+#define KEY_T           20
+#define KEY_Y           21
+#define KEY_U           22
+#define KEY_I           23
+#define KEY_O           24
+#define KEY_P           25
+#define KEY_LEFTBRACE   26
+#define KEY_RIGHTBRACE  27
+#define KEY_ENTER       28
+#define KEY_LEFTCTRL    29
+#define KEY_A           30
+#define KEY_S           31
+#define KEY_D           32
+#define KEY_F           33
+#define KEY_G           34
+#define KEY_H           35
+#define KEY_J           36
+#define KEY_K           37
+#define KEY_L           38
+#define KEY_SEMICOLON   39
+#define KEY_APOSTROPHE  40
+#define KEY_GRAVE       41
+#define KEY_LEFTSHIFT   42
+#define KEY_BACKSLASH   43
+#define KEY_Z           44
+#define KEY_X           45
+#define KEY_C           46
+#define KEY_V           47
+#define KEY_B           48
+#define KEY_N           49
+#define KEY_M           50
+#define KEY_COMMA       51
+#define KEY_DOT         52
+#define KEY_SLASH       53
+#define KEY_RIGHTSHIFT  54
+#define KEY_KPASTERISK  55
+#define KEY_LEFTALT     56
+#define KEY_SPACE       57
+#define KEY_CAPSLOCK    58
+#define KEY_F1          59
+#define KEY_F2          60
+#define KEY_F3          61
+#define KEY_F4          62
+#define KEY_F5          63
+#define KEY_F6          64
+#define KEY_F7          65
+#define KEY_F8          66
+#define KEY_F9          67
+#define KEY_F10         68
+#define KEY_NUMLOCK     69
+#define KEY_SCROLLLOCK  70
+#define KEY_KP7         71
+#define KEY_KP8         72
+#define KEY_KP9         73
+#define KEY_KPMINUS     74
+#define KEY_KP4         75
+#define KEY_KP5         76
+#define KEY_KP6         77
+#define KEY_KPPLUS      78
+#define KEY_KP1         79
+#define KEY_KP2         80
+#define KEY_KP3         81
+#define KEY_KP0         82
+#define KEY_KPDOT       83
+
+#define KEY_ZENKAKUHANKAKU 85
+#define KEY_102ND       86
+#define KEY_F11         87
+#define KEY_F12         88
+#define KEY_RO          89
+#define KEY_KATAKANA    90
+#define KEY_HIRAGANA    91
+#define KEY_HENKAN      92
+#define KEY_KATAKANAHIRAGANA 93
+#define KEY_MUHENKAN    94
+#define KEY_KPJPCOMMA   95
+#define KEY_KPENTER     96
+#define KEY_RIGHTCTRL   97
+#define KEY_KPSLASH     98
+#define KEY_SYSRQ       99
+#define KEY_RIGHTALT    100
+#define KEY_LINEFEED    101
+#define KEY_HOME        102
+#define KEY_UP          103
+#define KEY_PAGEUP      104
+#define KEY_LEFT        105
+#define KEY_RIGHT       106
+#define KEY_END         107
+#define KEY_DOWN        108
+#define KEY_PAGEDOWN    109
+#define KEY_INSERT      110
+#define KEY_DELETE      111
+#define KEY_MACRO       112
+#define KEY_MUTE        113
+#define KEY_VOLUMEDOWN  114
+#define KEY_VOLUMEUP    115
+#define KEY_POWER       116 /* SC System Power Down */
+#define KEY_KPEQUAL     117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE       119
+#define KEY_SCALE       120 /* AL Compiz Scale (Expose) */
+
+#define KEY_KPCOMMA     121
+#define KEY_HANGEUL     122
+#define KEY_HANGUEL     KEY_HANGEUL
+#define KEY_HANJA       123
+#define KEY_YEN         124
+#define KEY_LEFTMETA    125
+#define KEY_RIGHTMETA   126
+#define KEY_COMPOSE     127
+
+#define ROW_NUM         14
+#define COLUMN_NUM      8
+#define ROW_INIT        0x3FFF
+#define KEY_MAX_NUMBER  128
+#define S5PC1XX_KEYIF_REG_MEM_SIZE 0x14
+
+struct keymap {
+    int column;
+    int row;
+};
+
+typedef struct S5pc1xxKeyIFState {
+    SysBusDevice busdev;
+
+    /* Specifies the KEYPAD interface control register */
+    union {
+        /* raw register data */
+        uint32_t v;
+
+        /* register bits */
+        struct keyifcon_bits {
+            unsigned int_f_en : 1;
+            unsigned int_r_en : 1;
+            unsigned df_en    : 1;
+            unsigned fc_en    : 1;
+            unsigned wakeupen : 1;
+        } b;
+    } keyifcon;
+
+    /* Specifies the KEYPAD interface status and clear register */
+    union {
+        /* raw register data */
+        uint32_t v;
+
+        /* register bits */
+        struct keyifstsclr_bits {
+            unsigned p_int         : 14;
+            unsigned reserved14_15 : 2;
+            unsigned r_int         : 14;
+        } b;
+    } keyifstsclr;
+
+    /* Specifies the KEYPAD interface column data output register */
+    union {
+        /* raw register data */
+        uint32_t v;
+
+        /* register bits */
+        struct keyifcol_bits {
+            unsigned keyifcol   : 8;
+            unsigned keyifcolen : 8;
+        } b;
+    } keyifcol;
+
+    /* Specifies the KEYPAD interface row data input register */
+    uint32_t keyifrow;
+
+    /* Specifies the KEYPAD interface debouncing filter clock
+     * division register */
+    uint32_t keyiffc;
+
+    qemu_irq irq_keypad;
+
+    /* The keypad supports 14 rows and 8 columns */
+    uint16_t keypad[COLUMN_NUM];
+
+    /* S5PC110 may have a shift of KEYIFCOL register values */
+    uint32_t shift;
+
+    /* Name of the keymap used */
+    char *keymapname;
+
+    /* Mapping from QEMU keycodes to keypad row-column; filled at device init */
+    struct keymap map[KEY_MAX_NUMBER];
+} S5pc1xxKeyIFState;
+
+
+/* Mapping variants for QEMU keycodes */
+static int msm_keycode[COLUMN_NUM][ROW_NUM] = {
+    {1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_KP4 /*KEY_LEFT*/, 64, 65, 66, 67, 68, 69},
+    {9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_KP6 /*KEY_RIGHT*/, 16, 70, 71, 72, 73, 74, 75},
+    {17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_KP8 /*KEY_UP*/, 76, 77, 78, 79, 80, 81},
+    {25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32, 82, 83, 84, 85, 86, 87},
+    {33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_KP2 /*KEY_DOWN*/, KEY_BACKSPACE, 88, 89, 90, 91, 92, 93},
+    {KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48, 94, 95, 96, 97, 98, 99},
+    {KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER, 100, 101, 102, 103, 104, 105},
+    {KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA, 106, 107, 108, 109, 110, 111}
+};
+
+static int int_keycode[COLUMN_NUM][ROW_NUM] = {
+    {1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_KP4 /*KEY_LEFT*/},
+    {9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_KP6 /*KEY_RIGHT*/, 16},
+    {17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_KP8 /*KEY_UP*/},
+    {25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32},
+    {33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_KP2 /*KEY_DOWN*/, KEY_BACKSPACE},
+    {KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48},
+    {KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER},
+    {KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA}
+};
+
+static int aquila_keycode[COLUMN_NUM][ROW_NUM] = {
+    {KEY_TAB /*KEY_CAMERA*/, KEY_ESC /*KEY_CONFIG*/},
+    {KEY_EQUAL /*KEY_VOLUMEUP*/, KEY_MINUS /*KEY_VOLUMEDOWN*/}
+};
+
+
+static void s5pc1xx_keyif_reset(DeviceState *d)
+{
+    S5pc1xxKeyIFState *s =
+        FROM_SYSBUS(S5pc1xxKeyIFState, sysbus_from_qdev(d));
+    int i = 0;
+
+    s->keyifcon.v    = 0x00000000;
+    s->keyifstsclr.v = 0x00000000;
+    s->keyifcol.v    = 0x00000000;
+    s->keyifrow      = 0x00000000;
+    s->keyiffc       = 0x00000000;
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        s->keypad[i] = ROW_INIT;
+    }
+}
+
+static int s5pc1xx_keypad_event(void *opaque, int keycode)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    struct keymap k = {0, 0};
+    int rel = 0;
+
+    rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
+    keycode &= ~(0x80); /* strip qemu key release bit */
+
+    assert(keycode < KEY_MAX_NUMBER);
+
+    k = s->map[keycode];
+
+    /* don't report unknown keypress */
+    if (k.column < 0 || k.row < 0) {
+        return -1;
+    }
+
+    if (rel) {
+        s->keypad[k.column] |=   1 << k.row;
+    } else {
+        s->keypad[k.column] &= ~(1 << k.row);
+    }
+
+    if (rel && s->keyifcon.b.int_r_en) {
+        qemu_irq_raise(s->irq_keypad);
+        s->keyifstsclr.b.r_int = 1;
+    }
+
+    if (!rel && s->keyifcon.b.int_f_en) {
+        qemu_irq_raise(s->irq_keypad);
+        s->keyifstsclr.b.p_int = 1;
+    }
+
+    return 0;
+}
+
+/* Read KEYIF by GPIO */
+static uint32_t s5pc1xx_keyif_gpio_read(void *opaque,
+                                        int io_index)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    int i;
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        if (io_index == GPIO_KP_COL(i)) {
+            return s->keyifcol.v >> i;
+        }
+    }
+
+    for (i = 0; i < ROW_NUM; i++) {
+        if (io_index == GPIO_KP_ROW(i)) {
+            return s->keyifrow >> i;
+        }
+    }
+
+    return 0;
+}
+
+/* Write KEYIF by GPIO */
+static void s5pc1xx_keyif_gpio_write(void *opaque,
+                                     int io_index,
+                                     uint32_t value)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    int i;
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        if (io_index == GPIO_KP_COL(i)) {
+            s->keyifcol.v |= value << i;
+        }
+    }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_keyif_gpio_readfn   = s5pc1xx_keyif_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_keyif_gpio_writefn = s5pc1xx_keyif_gpio_write;
+
+static uint32_t s5pc1xx_keyif_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+
+    switch (offset) {
+    case 0x00: /* KEYIFCON */
+        return s->keyifcon.v;
+    case 0x04: /* KEYIFSTSCLR */
+        return s->keyifstsclr.v;
+    case 0x08: /* KEYIFCOL */
+        return s->keyifcol.v << s->shift;
+    case 0x0C: /* KEYIFROW */
+        return s->keyifrow;
+    case 0x10: /* KEYIFFC */
+        return s->keyiffc;
+    default:
+        hw_error("s5pc1xx.keyif: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_keyif_write(void *opaque, target_phys_addr_t offset,
+                                uint32_t val)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    struct keyifstsclr_bits* v = NULL;
+    int i = 0;
+
+    switch (offset) {
+    case 0x00: /* KEYIFCON */
+        s->keyifcon.v =  val;
+        break;
+    case 0x04: /* KEYIFSTSCLR */
+        v = (struct keyifstsclr_bits*)&val;
+        if (v->p_int) {
+            s->keyifstsclr.b.p_int = 0;
+            qemu_irq_lower(s->irq_keypad);
+        }
+        if (v->r_int) {
+            s->keyifstsclr.b.r_int = 0;
+            qemu_irq_lower(s->irq_keypad);
+        }
+        break;
+    case 0x08: /* KEYIFCOL */
+        s->keyifcol.v = (val >> s->shift) & ~0xFF00;
+        s->keyifrow = ROW_INIT; /* 14 bit */
+        /* FIXME: implement keyifcolen handling */
+        for (i = 0; i < COLUMN_NUM; i++) {
+            if (!(s->keyifcol.b.keyifcol & (1 << i))) {
+                s->keyifrow &= s->keypad[i];
+            }
+        }
+        break;
+    case 0x0C: /* KEYIFROW */
+        /* Read-only */
+        break;
+    case 0x10: /* KEYIFFC */
+        s->keyiffc = val;
+        break;
+    default:
+        hw_error("s5pc1xx.keyif: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_keyif_mm_read[] = {
+    s5pc1xx_keyif_read,
+    s5pc1xx_keyif_read,
+    s5pc1xx_keyif_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_keyif_mm_write[] = {
+    s5pc1xx_keyif_write,
+    s5pc1xx_keyif_write,
+    s5pc1xx_keyif_write
+};
+
+static void s5pc1xx_keyif_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->keyifcon.v);
+    qemu_put_be32s(f, &s->keyifstsclr.v);
+    qemu_put_be32s(f, &s->keyifcol.v);
+
+    qemu_put_be32s(f, &s->keyifrow);
+    qemu_put_be32s(f, &s->keyiffc);
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        qemu_put_be16s(f, &s->keypad[i]);
+    }
+}
+
+static int s5pc1xx_keyif_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->keyifcon.v);
+    qemu_get_be32s(f, &s->keyifstsclr.v);
+    qemu_get_be32s(f, &s->keyifcol.v);
+
+    qemu_get_be32s(f, &s->keyifrow);
+    qemu_get_be32s(f, &s->keyiffc);
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        qemu_get_be16s(f, &s->keypad[i]);
+    }
+
+    return 0;
+}
+
+static void s5pc1xx_init_keymap(S5pc1xxKeyIFState *s)
+{
+    int i, j;
+    struct keymap init = {-1, -1};
+    int (*keypad_keycode)[COLUMN_NUM][ROW_NUM];
+
+    for (i = 0; i < KEY_MAX_NUMBER; i++) {
+        s->map[i] = init;
+    }
+
+    /* Look for the keymap with corresponding name */
+    if (s->keymapname == NULL || !strcmp(s->keymapname, "msm")) {
+        /* Default one */
+        keypad_keycode = &msm_keycode;
+    } else if (!strcmp(s->keymapname, "int")) {
+        keypad_keycode = &int_keycode;
+    } else if (!strcmp(s->keymapname, "aquila")) {
+        keypad_keycode = &aquila_keycode;
+    } else {
+        hw_error("s5pc1xx.keyif: unknown keymap '%s'", s->keymapname);
+    }
+
+    for (i = 0; i < COLUMN_NUM; i++) {
+        for (j = 0; j < ROW_NUM; j++) {
+            struct keymap k = {i, j};
+            s->map[(*keypad_keycode)[i][j]] = k;
+        }
+    }
+}
+
+DeviceState *s5pc1xx_keyif_init(target_phys_addr_t base, qemu_irq irq,
+                                const char *keymapname, uint32_t shift)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.keyif");
+
+    qdev_prop_set_uint32(dev, "shift", shift);
+    qdev_prop_set_string(dev, "keymap", (char *)keymapname);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
+
+static int s5pc1xx_keyif_init1(SysBusDevice *dev)
+{
+    S5pc1xxKeyIFState *s = FROM_SYSBUS(S5pc1xxKeyIFState, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->irq_keypad);
+    iomemtype = cpu_register_io_memory(s5pc1xx_keyif_mm_read,
+                                       s5pc1xx_keyif_mm_write, s,
+                                                                          DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_KEYIF_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_KEYIF, 0,
+                                    s5pc1xx_keyif_gpio_readfn,
+                                    s5pc1xx_keyif_gpio_writefn, NULL, s);
+    s5pc1xx_init_keymap(s);
+    qemu_add_kbd_event_handler(s5pc1xx_keypad_event, s, "Samsung Keypad");
+
+    s5pc1xx_keyif_reset(&s->busdev.qdev);
+
+    register_savevm(&dev->qdev, "s5pc1xx.keyif", -1, 1,
+                    s5pc1xx_keyif_save, s5pc1xx_keyif_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_keyif_info = {
+    .init = s5pc1xx_keyif_init1,
+    .qdev.name  = "s5pc1xx.keyif",
+    .qdev.size  = sizeof(S5pc1xxKeyIFState),
+    .qdev.reset = s5pc1xx_keyif_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_STRING("keymap", S5pc1xxKeyIFState, keymapname),
+        DEFINE_PROP_UINT32("shift", S5pc1xxKeyIFState, shift, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_keyif_register(void)
+{
+    sysbus_register_withprop(&s5pc1xx_keyif_info);
+}
+
+device_init(s5pc1xx_keyif_register)
diff --git a/hw/s5pc1xx_lcd.c b/hw/s5pc1xx_lcd.c
new file mode 100644 (file)
index 0000000..3428901
--- /dev/null
@@ -0,0 +1,1505 @@
+/*
+ * S5PC1XX LCD Controller
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+/*
+ * Known issues:
+ *    multiple windows blending - implemented but not tested
+ *    shadow registers - not implemented
+ *    i80 indirect interface - not implemented
+ *    dithering - not implemented
+ *    RTQoS - not implemented
+ */
+
+#include "console.h"
+#include "pixel_ops.h"
+#include "s5pc1xx.h"
+#include "sysbus.h"
+
+
+#define BISWP   0x8
+#define BYSWP   0x4
+#define HWSWP   0x2
+#define WSWP    0x1
+
+
+typedef struct {
+    uint8_t r, g, b;
+    uint32_t a;
+} rgba;
+
+struct DrawConfig;
+
+typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
+typedef void draw_line_func(struct DrawConfig *cfg, uint8_t *src,
+                            uint8_t *dst, uint8_t *ifb);
+typedef uint32_t coef_func(const struct DrawConfig *cfg, rgba pa, rgba pb);
+
+typedef struct DrawConfig {
+    pixel_to_rgb_func *pixel_to_rgb;
+    draw_line_func *draw_line;
+    int (*put_pixel)(rgba p, uint8_t *pixel);
+    int (*get_pixel)(uint8_t *src, rgba *p);
+    void (*blend)(struct DrawConfig *cfg, rgba p_old, rgba p_new, rgba *p);
+    coef_func *coef_p, *coef_q, *coef_a, *coef_b;
+    uint8_t is_palletized;
+    uint32_t bg_alpha[2], fg_alpha[2];
+    uint32_t color_key, color_mask, color_ctl;
+    uint8_t fg_alpha_pix, bg_alpha_pix;
+    int width;
+    int bpp;
+    uint32_t *palette;
+    uint8_t swap;
+    uint8_t fg_pixel_blending, bg_pixel_blending;
+    uint8_t fg_alpha_sel, bg_alpha_sel;
+} DrawConfig;
+
+typedef struct S5pc1xxLcdWindow {
+    uint32_t wincon;
+    uint32_t vidosd[4];
+    uint32_t buf_start[2];
+    uint32_t buf_end[2];
+    uint32_t buf_size;
+    uint32_t keycon[2];
+    uint32_t winmap;
+    uint32_t vidw_alpha[2];
+    uint32_t blendeq;
+    uint32_t palette[256];
+} S5pc1xxLcdWindow;
+
+typedef struct {
+    SysBusDevice busdev;
+
+    uint32_t shadowcon;
+    uint32_t vidcon[3];
+    uint32_t prtcon;
+    uint32_t vidtcon[3];
+    uint32_t vp1tcon[2];
+    uint32_t vidintcon[2];
+    uint32_t dithcon;
+    uint32_t wpalcon[2];
+    uint32_t trigcon;
+    uint32_t ituifcon;
+    uint32_t i80ifcon[4];
+    uint32_t ldi_cmdcon[2];
+    uint32_t sifccon[3];
+    uint32_t blendcon;
+    uint32_t ldi_cmd[12];
+    uint32_t dualrgb;
+
+    S5pc1xxLcdWindow window[5];
+    uint8_t *ifb;
+    uint8_t *valid_line;
+    uint8_t *valid_line_prev;
+    DisplayState *console;
+    uint8_t invalidate;
+    qemu_irq irq[3];
+} S5pc1xxLcdState;
+
+
+/* Palette/pixel to RGB conversion */
+
+#define DEF_PIXEL_TO_RGB(N,R,G,B,A) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+    p->b = (pixel & ((1 << (B)) - 1)) << (8 - (B)); \
+    pixel >>= (B); \
+    p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)); \
+    pixel >>= (G); \
+    p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)); \
+    pixel >>= (R); \
+    if (1 == (A)) { \
+        p->a = pixel & 1; \
+    } else if (8 == (A)) { \
+        p->a = pixel & 0xFF; \
+        p->a = (p->a << 16) | (p->a << 8) | p->a; \
+    } else { \
+        p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)); \
+    } \
+}
+
+DEF_PIXEL_TO_RGB(pixel_a232_to_rgb, 2, 3, 2, 1)
+DEF_PIXEL_TO_RGB(pixel_a444_to_rgb, 4, 4, 4, 1)
+DEF_PIXEL_TO_RGB(pixel_4444_to_rgb, 4, 4, 4, 4)
+DEF_PIXEL_TO_RGB(pixel_565_to_rgb,  5, 6, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_a555_to_rgb, 5, 5, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_555_to_rgb,  5, 5, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_666_to_rgb,  6, 6, 6, 0)
+DEF_PIXEL_TO_RGB(pixel_a666_to_rgb, 6, 6, 6, 1)
+DEF_PIXEL_TO_RGB(pixel_a665_to_rgb, 6, 6, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_888_to_rgb,  8, 8, 8, 0)
+DEF_PIXEL_TO_RGB(pixel_a888_to_rgb, 8, 8, 8, 1)
+DEF_PIXEL_TO_RGB(pixel_a887_to_rgb, 8, 8, 7, 1)
+DEF_PIXEL_TO_RGB(pixel_8888_to_rgb, 8, 8, 8, 8)
+
+/* Special case for (5+1,5+1,5+1) mode */
+static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
+{
+    uint8_t u = (pixel >> 15) & 1;
+    p->b = (((pixel & 0x1F) << 1) | u) << 2;
+    pixel >>= 5;
+    p->g = (((pixel & 0x3F) << 1) | u) << 2;
+    pixel >>= 6;
+    p->r = (((pixel & 0x1F) << 1) | u) << 2;
+}
+
+
+/* Write RGB to QEMU's GraphicConsole framebuffer */
+
+static int put_pixel8(rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
+    *(uint8_t *)d = pixel;
+    return 1;
+}
+
+static int put_pixel15(rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
+    *(uint16_t *)d = pixel;
+    return 2;
+}
+
+static int put_pixel16(rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
+    *(uint16_t *)d = pixel;
+    return 2;
+}
+
+static int put_pixel24(rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+    *(uint8_t *)d++ = (pixel >>  0) & 0xFF;
+    *(uint8_t *)d++ = (pixel >>  8) & 0xFF;
+    *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
+    return 3;
+}
+
+static int put_pixel32(rgba p, uint8_t *d)
+{
+    uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+    *(uint32_t *)d = pixel;
+    return 4;
+}
+
+
+/* Put/get pixel to/from internal LCD Controller framebuffer */
+
+static int put_rgba(rgba p, uint8_t *d)
+{
+    *(uint8_t *)d++ = p.r;
+    *(uint8_t *)d++ = p.g;
+    *(uint8_t *)d++ = p.b;
+    *(uint32_t *)d = p.a;
+    return 7;
+}
+
+static int get_rgba(uint8_t *s, rgba *p)
+{
+    p->r = *(uint8_t *)s++;
+    p->g = *(uint8_t *)s++;
+    p->b = *(uint8_t *)s++;
+    p->a = *(uint32_t *)s;
+    return 7;
+}
+
+
+/* Perform byte/halfword/word swap of data accrding to config */
+
+static inline uint64_t swap_data(const DrawConfig *cfg, uint64_t x)
+{
+    int i;
+    uint64_t res;
+
+    return x;
+
+    if (cfg->swap & BISWP) {
+        res = 0;
+        for (i = 0; i < 64; i++) {
+            if (x & (1ULL << (64 - i))) {
+                res |= (1ULL << i);
+            }
+        }
+        x = res;
+    }
+    if (cfg->swap & BYSWP) {
+        x = ((x & 0x00000000000000FFULL) << 56) |
+            ((x & 0x000000000000FF00ULL) << 40) |
+            ((x & 0x0000000000FF0000ULL) << 24) |
+            ((x & 0x00000000FF000000ULL) <<  8) |
+            ((x & 0x000000FF00000000ULL) >>  8) |
+            ((x & 0x0000FF0000000000ULL) >> 24) |
+            ((x & 0x00FF000000000000ULL) >> 40) |
+            ((x & 0xFF00000000000000ULL) >> 56);
+    }
+    if (cfg->swap & HWSWP) {
+        x = ((x & 0x000000000000FFFFULL) << 48) |
+            ((x & 0x00000000FFFF0000ULL) << 16) |
+            ((x & 0x0000FFFF00000000ULL) >> 16) |
+            ((x & 0xFFFF000000000000ULL) >> 48);
+    }
+    if (cfg->swap & WSWP) {
+        x = ((x & 0x00000000FFFFFFFFULL) << 32) |
+            ((x & 0xFFFFFFFF00000000ULL) >> 32);
+    }
+    return x;
+}
+
+
+/* Coefficient extraction functions */
+
+static uint32_t coef_zero(const DrawConfig *cfg,
+                          rgba pa, rgba pb)
+{
+    return 0;
+}
+
+static uint32_t coef_one(const DrawConfig *cfg,
+                         rgba pa, rgba pb)
+{
+    return 0xFFFFFF;
+}
+
+static uint32_t coef_alphaa(const DrawConfig *cfg,
+                            rgba pa, rgba pb)
+{
+    if (!cfg->fg_pixel_blending) {
+        pa.a = cfg->fg_alpha_sel;
+    }
+    if (cfg->fg_alpha_pix) {
+        return pa.a;
+    } else {
+        return cfg->fg_alpha[pa.a];
+    }
+}
+
+static uint32_t coef_one_minus_alphaa(const DrawConfig *cfg,
+                                      rgba pa, rgba pb)
+{
+    if (!cfg->fg_pixel_blending) {
+        pa.a = cfg->fg_alpha_sel;
+    }
+    if (cfg->fg_alpha_pix) {
+        return 0xFFFFFF - pa.a;
+    } else {
+        return 0xFFFFFF - cfg->fg_alpha[pa.a];
+    }
+}
+
+static uint32_t coef_alphab(const DrawConfig *cfg,
+                            rgba pa, rgba pb)
+{
+    if (!cfg->bg_pixel_blending) {
+        pb.a = cfg->bg_alpha_sel;
+    }
+    if (cfg->bg_alpha_pix) {
+        return pb.a;
+    } else {
+        return cfg->bg_alpha[pb.a];
+    }
+}
+
+static uint32_t coef_one_minus_alphab(const DrawConfig *cfg,
+                                      rgba pa, rgba pb)
+{
+    if (!cfg->bg_pixel_blending) {
+        pb.a = cfg->bg_alpha_sel;
+    }
+    if (cfg->bg_alpha_pix) {
+        return 0xFFFFFF - pb.a;
+    } else {
+        return 0xFFFFFF - cfg->bg_alpha[pb.a];
+    }
+}
+
+static uint32_t coef_a(const DrawConfig *cfg,
+                       rgba pa, rgba pb)
+{
+    return (pa.r << 16) | (pa.g << 8) | pa.b;
+}
+
+static uint32_t coef_one_minus_a(const DrawConfig *cfg,
+                                 rgba pa, rgba pb)
+{
+    return 0xFFFFFF - ((pa.r << 16) | (pa.g << 8) | pa.b);
+}
+
+static uint32_t coef_b(const DrawConfig *cfg,
+                       rgba pa, rgba pb)
+{
+    return (pb.r << 16) | (pb.g << 8) | pb.b;
+}
+
+static uint32_t coef_one_minus_b(const DrawConfig *cfg,
+                                 rgba pa, rgba pb)
+{
+    return 0xFFFFFF - ((pb.r << 16) | (pb.g << 8) | pb.b);
+}
+
+
+/* Blending functions */
+
+static void blend_alpha(const DrawConfig *cfg,
+                        rgba p_bg, rgba p_fg, rgba *res)
+{
+    uint32_t pl, ql, al, bl;
+    uint32_t p, q, a, b, fg, bg, fga, bga;
+
+    pl = cfg->coef_p(cfg, p_fg, p_bg);
+    ql = cfg->coef_q(cfg, p_fg, p_bg);
+    al = cfg->coef_a(cfg, p_fg, p_bg);
+    bl = cfg->coef_b(cfg, p_fg, p_bg);
+    res->a = 0;
+    /* B */
+    p = pl & 0xFF;
+    pl >>= 8;
+    q = ql & 0xFF;
+    ql >>= 8;
+    a = al & 0xFF;
+    al >>= 8;
+    b = bl & 0xFF;
+    bl >>= 8;
+    fg = p_fg.b;
+    bg = p_bg.b;
+    if (cfg->fg_pixel_blending) {
+        if (cfg->fg_alpha_pix) {
+            fga = p_fg.a & 0xFF;
+        } else {
+            fga = cfg->fg_alpha[p_fg.a] & 0xFF;
+        }
+    } else {
+        fga = cfg->fg_alpha[cfg->fg_alpha_sel] & 0xFF;
+    }
+    if (cfg->bg_pixel_blending) {
+        if (cfg->bg_alpha_pix) {
+            bga = p_bg.a & 0xFF;
+        } else {
+            bga = cfg->bg_alpha[p_bg.a] & 0xFF;
+        }
+    } else {
+        bga = cfg->bg_alpha[cfg->bg_alpha_sel] & 0xFF;
+    }
+    bg = (bg * b + fg * a) / 0xFF;
+    if (bg > 0xFF) {
+        res->b = 0xFF;
+    } else {
+        res->b = bg;
+    }
+    bga = (bga * p + fga * q) / 0xFF;
+    if (bga > 0xFF) {
+        res->a |= 0xFF;
+    } else {
+        res->a |= bga;
+    }
+    /* G */
+    p = pl & 0xFF;
+    pl >>= 8;
+    q = ql & 0xFF;
+    ql >>= 8;
+    a = al & 0xFF;
+    al >>= 8;
+    b = bl & 0xFF;
+    bl >>= 8;
+    fg = p_fg.g;
+    bg = p_bg.g;
+    if (cfg->fg_pixel_blending) {
+        if (cfg->fg_alpha_pix) {
+            fga = (p_fg.a >> 8) & 0xFF;
+        } else {
+            fga = (cfg->fg_alpha[p_fg.a] >> 8) & 0xFF;
+        }
+    } else {
+        fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 8) & 0xFF;
+    }
+    if (cfg->bg_pixel_blending) {
+        if (cfg->bg_alpha_pix) {
+            bga = (p_bg.a >> 8) & 0xFF;
+        } else {
+            bga = (cfg->bg_alpha[p_bg.a] >> 8) & 0xFF;
+        }
+    } else {
+        bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 8) & 0xFF;
+    }
+    bg = (bg * b + fg * a) / 0xFF;
+    if (bg > 0xFF) {
+        res->g = 0xFF;
+    } else {
+        res->g = bg;
+    }
+    bga = (bga * p + fga * q) / 0xFF;
+    if (bga > 0xFF) {
+        res->a |= 0xFF << 8;
+    } else {
+        res->a |= bga << 8;
+    }
+    /* R */
+    p = pl & 0xFF;
+    pl >>= 8;
+    q = ql & 0xFF;
+    ql >>= 8;
+    a = al & 0xFF;
+    al >>= 8;
+    b = bl & 0xFF;
+    bl >>= 8;
+    fg = p_fg.r;
+    bg = p_bg.r;
+    if (cfg->fg_pixel_blending) {
+        if (cfg->fg_alpha_pix) {
+            fga = (p_fg.a >> 16) & 0xFF;
+        } else {
+            fga = (cfg->fg_alpha[p_fg.a] >> 16) & 0xFF;
+        }
+    } else {
+        fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 16) & 0xFF;
+    }
+    if (cfg->bg_pixel_blending) {
+        if (cfg->bg_alpha_pix) {
+            bga = (p_bg.a >> 16) & 0xFF;
+        } else {
+            bga = (cfg->bg_alpha[p_bg.a] >> 16) & 0xFF;
+        }
+    } else {
+        bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 16) & 0xFF;
+    }
+    bg = (bg * b + fg * a) / 0xFF;
+    if (bg > 0xFF) {
+        res->r = 0xFF;
+    } else {
+        res->r = bg;
+    }
+    bga = (bga * p + fga * q) / 0xFF;
+    if (bga > 0xFF) {
+        res->a |= 0xFF << 16;
+    } else {
+        res->a |= bga << 16;
+    }
+}
+
+static void blend_colorkey(DrawConfig *cfg,
+                           rgba p_bg, rgba p_fg, rgba *p)
+{
+    uint8_t r, g, b;
+
+    if (cfg->color_ctl & 2) {
+        blend_alpha(cfg, p_bg, p_fg, p);
+        return ;
+    }
+    r = ((cfg->color_key & ~cfg->color_mask) >> 16) & 0xFF;
+    g = ((cfg->color_key & ~cfg->color_mask) >>  8) & 0xFF;
+    b = ((cfg->color_key & ~cfg->color_mask) >>  0) & 0xFF;
+    if (cfg->color_ctl & 1) {
+        if ((p_fg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+                (p_fg.g & ~((cfg->color_mask >>  8) & 0xFF)) == g &&
+                (p_fg.b & ~((cfg->color_mask >>  0) & 0xFF)) == b) {
+            if (cfg->color_ctl & 4) {
+                p_fg.a = 1;
+                cfg->fg_pixel_blending = 0;
+                blend_alpha(cfg, p_bg, p_fg, p);
+            } else {
+                *p = p_bg;
+            }
+        } else {
+            if (cfg->color_ctl & 4) {
+                p_fg.a = 0;
+                cfg->fg_pixel_blending = 0;
+                blend_alpha(cfg, p_bg, p_fg, p);
+            } else {
+                *p = p_fg;
+            }
+        }
+    } else {
+        if ((p_bg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+                (p_bg.g & ~((cfg->color_mask >>  8) & 0xFF)) == g &&
+                (p_bg.b & ~((cfg->color_mask >>  0) & 0xFF)) == b) {
+            if (cfg->color_ctl & 4) {
+                p_fg.a = 1;
+                cfg->fg_pixel_blending = 0;
+                blend_alpha(cfg, p_bg, p_fg, p);
+            } else {
+                *p = p_fg;
+            }
+        } else {
+            if (cfg->color_ctl & 4) {
+                p_fg.a = 0;
+                cfg->fg_pixel_blending = 0;
+                blend_alpha(cfg, p_bg, p_fg, p);
+            } else {
+                *p = p_bg;
+            }
+        }
+    }
+}
+
+
+/* Draw line functions */
+
+#define DEF_DRAW_LINE(N) \
+static void glue(draw_line, N)(DrawConfig *cfg, uint8_t *src, \
+                               uint8_t *dst, uint8_t *ifb) \
+{ \
+    rgba p, p_old; \
+    uint64_t data; \
+    int width = cfg->width; \
+    int i; \
+    do { \
+        data = ldq_raw((void *)src); \
+        src += 8; \
+        data = swap_data(cfg, data); \
+        for (i = 0; i < (64 / (N)); i++) { \
+            cfg->pixel_to_rgb(cfg->is_palletized ? \
+                                  cfg->palette[data & ((1ULL << (N)) - 1)] : \
+                                  data & ((1ULL << (N)) - 1), &p); \
+            if (cfg->blend) { \
+                ifb += cfg->get_pixel(ifb, &p_old); \
+                cfg->blend(cfg, p_old, p, &p); \
+            } \
+            dst += cfg->put_pixel(p, dst); \
+            data >>= (N); \
+        } \
+        width -= (64 / (N)); \
+    } while (width > 0); \
+}
+
+DEF_DRAW_LINE(1)
+DEF_DRAW_LINE(2)
+DEF_DRAW_LINE(4)
+DEF_DRAW_LINE(8)
+DEF_DRAW_LINE(16)
+DEF_DRAW_LINE(32)
+
+static void draw_line_copy(DrawConfig *cfg, uint8_t *src, uint8_t *dst,
+                           uint8_t *ifb)
+{
+    rgba p;
+    int width = cfg->width;
+
+    do {
+        src += cfg->get_pixel(src, &p);
+        dst += cfg->put_pixel(p, dst);
+        width--;
+    } while (width > 0);
+}
+
+
+/* LCD Functions */
+
+static void s5pc1xx_lcd_update_irq(S5pc1xxLcdState *s)
+{
+    if (!(s->vidintcon[0] & 1)) {
+        qemu_irq_lower(s->irq[0]);
+        qemu_irq_lower(s->irq[1]);
+        qemu_irq_lower(s->irq[2]);
+        return;
+    }
+    if ((s->vidintcon[0] & 2) && (s->vidintcon[1] & 1)) {
+        qemu_irq_raise(s->irq[0]);
+    } else {
+        qemu_irq_lower(s->irq[0]);
+    }
+    if ((s->vidintcon[0] & (1 << 12)) && (s->vidintcon[1] & 2)) {
+        qemu_irq_raise(s->irq[1]);
+    } else {
+        qemu_irq_lower(s->irq[1]);
+    }
+    if ((s->vidintcon[0] & (1 << 17)) && (s->vidintcon[1] & 4)) {
+        qemu_irq_raise(s->irq[2]);
+    } else {
+        qemu_irq_lower(s->irq[2]);
+    }
+}
+
+static void s5pc1xx_lcd_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t val)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    int w, i;
+
+    if (offset & 3) {
+        hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    switch (offset) {
+        case 0x000 ... 0x008:
+            s->vidcon[(offset - 0x000) >> 2] = val;
+            break;
+        case 0x00C:
+            s->prtcon = val;
+            break;
+        case 0x010 ... 0x018:
+            s->vidtcon[(offset - 0x010) >> 2] = val;
+            break;
+        case 0x020 ... 0x030:
+            s->window[(offset - 0x020) >> 2].wincon = val;
+            break;
+        case 0x034:
+            s->shadowcon = val;
+            break;
+        case 0x040 ... 0x088:
+            w = (offset - 0x040) >> 4;
+            i = ((offset - 0x040) & 0xF) >> 2;
+            if (i < 2) {
+                s->window[w].vidosd[i] = val;
+            } else if (i == 3) {
+                if (w != 1 && w != 2) {
+                    hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+                             offset);
+                } else {
+                    s->window[w].vidosd[i] = val;
+                }
+            } else {
+                if (w == 0) {
+                    i++;
+                }
+                s->window[w].vidosd[i] = val;
+            }
+            break;
+        case 0x0A0 ... 0x0C0:
+            w = (offset - 0x0A0) >> 3;
+            i = ((offset - 0x0A0) >> 2) & 1;
+            if (i == 1 && w >= 2) {
+                hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+                         offset);
+            }
+            s->window[w].buf_start[i] = val;
+            break;
+        case 0x0D0 ... 0x0F0:
+            w = (offset - 0x0D0) >> 3;
+            i = ((offset - 0x0D0) >> 2) & 1;
+            if (i == 1 && w >= 2) {
+                hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+                         offset);
+            }
+            s->window[w].buf_end[i] = val;
+            break;
+        case 0x100 ... 0x110:
+            s->window[(offset - 0x100) >> 2].buf_size = val;
+            break;
+        case 0x118 ... 0x11C:
+            s->vp1tcon[(offset - 0x118)] = val;
+            break;
+        case 0x130:
+            s->vidintcon[0] = val;
+        case 0x134:
+            s->vidintcon[1] &= ~(val & 7);
+            s5pc1xx_lcd_update_irq(s);
+            break;
+        case 0x140 ... 0x15C:
+            w = ((offset - 0x140) >> 3) + 1;
+            i = ((offset - 0x140) >> 2) & 1;
+            s->window[w].keycon[i] = val;
+            break;
+        case 0x170:
+            s->dithcon = val;
+            break;
+        case 0x180 ... 0x190:
+            s->window[(offset - 0x180) >> 2].winmap = val;
+            break;
+        case 0x19C ... 0x1A0:
+            s->wpalcon[(offset - 0x19C) >> 2] = val;
+            break;
+        case 0x1A4:
+            s->trigcon = val;
+            break;
+        case 0x1A8:
+            s->ituifcon = val;
+            break;
+        case 0x1B0 ... 0x1BC:
+            s->i80ifcon[(offset - 0x1B0) >> 2] = val;
+            break;
+        case 0x1D0 ... 0x1D4:
+            s->ldi_cmdcon[(offset - 0x1D0) >> 2] = val;
+            break;
+        case 0x1E0 ... 0x1E8:
+            i = (offset - 0x1E0) >> 2;
+            if (i == 2) {
+                hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+                         offset);
+            }
+            s->sifccon[i] = val;
+            break;
+        case 0x200 ... 0x224:
+            w = ((offset - 0x200) >> 3);
+            i = ((offset - 0x200) >> 2) & 1;
+            s->window[w].vidw_alpha[i] = val;
+            break;
+        case 0x244 ... 0x250:
+            s->window[(offset - 0x244) >> 2].blendeq = val;
+            break;
+        case 0x260:
+            s->blendcon = val;
+            break;
+        case 0x27C:
+            s->dualrgb = val;
+            break;
+        case 0x280 ... 0x2AC:
+            s->ldi_cmd[(offset - 0x280) >> 2] = val;
+            break;
+        case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
+            w = (offset - 0x2400) >> 10;
+            i = ((offset - 0x2400) >> 2) & 0xFF;
+            s->window[w].palette[i] = val;
+            break;
+        default:
+            hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+static uint32_t s5pc1xx_lcd_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    int w, i;
+
+    if (offset & 3) {
+        hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    switch (offset) {
+        case 0x000 ... 0x008:
+            return s->vidcon[(offset - 0x000) >> 2];
+        case 0x00C:
+            return s->prtcon;
+        case 0x010 ... 0x018:
+            return s->vidtcon[(offset - 0x010) >> 2];
+        case 0x020 ... 0x030:
+            return s->window[(offset - 0x020) >> 2].wincon;
+        case 0x034:
+            return s->shadowcon;
+        case 0x040 ... 0x088:
+            w = (offset - 0x040) >> 4;
+            i = ((offset - 0x040) & 0xF) >> 2;
+            if (i < 2) {
+                return s->window[w].vidosd[i];
+            } else if (i == 3) {
+                if (w != 1 && w != 2) {
+                    hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+                             offset);
+                } else {
+                    return s->window[w].vidosd[i];
+                }
+            } else {
+                if (w == 0) {
+                    i++;
+                }
+                return s->window[w].vidosd[i];
+            }
+        case 0x0A0 ... 0x0C0:
+            w = (offset - 0x0A0) >> 3;
+            i = ((offset - 0x0A0) >> 2) & 1;
+            if (i == 1 && w >= 2) {
+                hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+                         offset);
+            }
+            return s->window[w].buf_start[i];
+        case 0x0D0 ... 0x0F0:
+            w = (offset - 0x0D0) >> 3;
+            i = ((offset - 0x0D0) >> 2) & 1;
+            if (i == 1 && w >= 2) {
+                hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+                         offset);
+            }
+            return s->window[w].buf_end[i];
+        case 0x100 ... 0x110:
+            return s->window[(offset - 0x100) >> 2].buf_size;
+        case 0x118 ... 0x11C:
+            return s->vp1tcon[(offset - 0x118)];
+        case 0x130 ... 0x134:
+            return s->vidintcon[(offset - 0x130) >> 2];
+        case 0x140 ... 0x15C:
+            w = ((offset - 0x140) >> 3) + 1;
+            i = ((offset - 0x140) >> 2) & 1;
+            return s->window[w].keycon[i];
+        case 0x170:
+            return s->dithcon;
+        case 0x180 ... 0x190:
+            return s->window[(offset - 0x180) >> 2].winmap;
+        case 0x19C ... 0x1A0:
+            return s->wpalcon[(offset - 0x19C) >> 2];
+        case 0x1A4:
+            return s->trigcon;
+        case 0x1A8:
+            return s->ituifcon;
+        case 0x1B0 ... 0x1BC:
+            return s->i80ifcon[(offset - 0x1B0) >> 2];
+        case 0x1D0 ... 0x1D4:
+            return s->ldi_cmdcon[(offset - 0x1D0) >> 2];
+        case 0x1E0 ... 0x1E8:
+            i = (offset - 0x1E0) >> 2;
+            return s->sifccon[i];
+        case 0x200 ... 0x224:
+            w = ((offset - 0x200) >> 3);
+            i = ((offset - 0x200) >> 2) & 1;
+            return s->window[w].vidw_alpha[i];
+        case 0x244 ... 0x250:
+            return s->window[(offset - 0x244) >> 2].blendeq;
+        case 0x260:
+            return s->blendcon;
+        case 0x27C:
+            return s->dualrgb;
+        case 0x280 ... 0x2AC:
+            return s->ldi_cmd[(offset - 0x280) >> 2];
+        case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
+            w = (offset - 0x2400) >> 10;
+            i = ((offset - 0x2400) >> 2) & 0xFF;
+            return s->window[w].palette[i];
+        default:
+            hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+static CPUReadMemoryFunc *s5pc1xx_lcd_readfn[] = {
+    s5pc1xx_lcd_read,
+    s5pc1xx_lcd_read,
+    s5pc1xx_lcd_read
+};
+
+static CPUWriteMemoryFunc *s5pc1xx_lcd_writefn[] = {
+    s5pc1xx_lcd_write,
+    s5pc1xx_lcd_write,
+    s5pc1xx_lcd_write
+};
+
+static void s5pc1xx_update_resolution(S5pc1xxLcdState *s)
+{
+    uint32_t width, height;
+    /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
+    width = (s->vidtcon[2] & 0x7FF) + 1;
+    height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+    if (s->ifb == NULL || ds_get_width(s->console) != width ||
+        ds_get_height(s->console) != height) {
+
+        qemu_console_resize(s->console, width, height);
+        s->ifb = qemu_realloc(s->ifb, width * height * 7);
+        s->valid_line =
+            qemu_realloc(s->valid_line, (height >> 3) * sizeof(uint8_t));
+        s->valid_line_prev =
+            qemu_realloc(s->valid_line_prev, (height >> 3) * sizeof(uint8_t));
+        memset(s->ifb, 0, width * height * 7);
+        s->invalidate = 1;
+    }
+}
+
+/* Returns WxPAL for given window number WINDOW */
+static uint32_t s5pc1xx_wxpal(S5pc1xxLcdState *s, int window)
+{
+    switch (window) {
+    case 0:
+        return s->wpalcon[1] & 0x7;
+    case 1:
+        return (s->wpalcon[1] >> 3) & 0x7;
+    case 2:
+        return ((s->wpalcon[0] >> 8) & 0x6) | ((s->wpalcon[1] >> 6) & 0x1);
+    case 3:
+        return ((s->wpalcon[0] >> 12) & 0x6) | ((s->wpalcon[1] >> 7) & 0x1);
+    case 4:
+        return ((s->wpalcon[0] >> 16) & 0x6) | ((s->wpalcon[1] >> 8) & 0x1);
+    default:
+        hw_error("s5pc1xx.lcd: incorrect window number %d\n", window);
+    }
+}
+
+/* Parse BPPMODE_F bits and setup known DRAW_CONFIG fields accordingly.
+   BPPMODE_F = WINCON1[5:2] */
+static void s5pc1xx_parse_win_bppmode(S5pc1xxLcdState *s,
+                                      DrawConfig *cfg, int window)
+{
+    switch ((s->window[window].wincon >> 2) & 0xF) {
+    case 0:
+        cfg->draw_line = draw_line1;
+        cfg->is_palletized = 1;
+        cfg->bpp = 1;
+        break;
+    case 1:
+        cfg->draw_line = draw_line2;
+        cfg->is_palletized = 1;
+        cfg->bpp = 2;
+        break;
+    case 2:
+        cfg->draw_line = draw_line4;
+        cfg->is_palletized = 1;
+        cfg->bpp = 4;
+        break;
+    case 3:
+        cfg->draw_line = draw_line8;
+        cfg->is_palletized = 1;
+        cfg->bpp = 8;
+        break;
+    case 4:
+        cfg->draw_line = draw_line8;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_a232_to_rgb;
+        cfg->bpp = 8;
+        break;
+    case 5:
+        cfg->draw_line = draw_line16;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_565_to_rgb;
+        cfg->bpp = 16;
+        break;
+    case 6:
+        cfg->draw_line = draw_line16;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_a555_to_rgb;
+        cfg->bpp = 16;
+        break;
+    case 7:
+        cfg->draw_line = draw_line16;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_1555_to_rgb;
+        cfg->bpp = 16;
+        break;
+    case 8:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_666_to_rgb;
+        cfg->bpp = 32;
+        break;
+    case 9:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_a665_to_rgb;
+        cfg->bpp = 32;
+        break;
+    case 10:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_a666_to_rgb;
+        cfg->bpp = 32;
+        break;
+    case 11:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_888_to_rgb;
+        cfg->bpp = 32;
+        break;
+    case 12:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_a887_to_rgb;
+        cfg->bpp = 32;
+        break;
+    case 13:
+        cfg->draw_line = draw_line32;
+        cfg->is_palletized = 0;
+        if ((s->window[window].wincon & (1 << 6)) &&
+            (s->window[window].wincon & 2)) {
+            cfg->pixel_to_rgb = pixel_8888_to_rgb;
+            cfg->fg_alpha_pix = 1;
+        } else {
+            cfg->pixel_to_rgb = pixel_a888_to_rgb;
+        }
+        cfg->bpp = 32;
+        break;
+    case 14:
+        cfg->draw_line = draw_line16;
+        cfg->is_palletized = 0;
+        if ((s->window[window].wincon & (1 << 6)) &&
+            (s->window[window].wincon & 2)) {
+            cfg->pixel_to_rgb = pixel_4444_to_rgb;
+            cfg->fg_alpha_pix = 1;
+        } else {
+            cfg->pixel_to_rgb = pixel_a444_to_rgb;
+        }
+        cfg->bpp = 16;
+        break;
+    case 15:
+        cfg->draw_line = draw_line16;
+        cfg->is_palletized = 0;
+        cfg->pixel_to_rgb = pixel_555_to_rgb;
+        cfg->bpp = 16;
+        break;
+    }
+}
+
+pixel_to_rgb_func *wxpal_to_rgb[8] = {
+    [0] = pixel_565_to_rgb,
+    [1] = pixel_a555_to_rgb,
+    [2] = pixel_666_to_rgb,
+    [3] = pixel_a665_to_rgb,
+    [4] = pixel_a666_to_rgb,
+    [5] = pixel_888_to_rgb,
+    [6] = pixel_a888_to_rgb,
+    [7] = pixel_8888_to_rgb
+};
+
+static inline uint32_t unpack_by_4(uint32_t x)
+{
+    return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
+}
+
+static coef_func *coef_decode(uint32_t x)
+{
+    switch (x) {
+    case 0:
+        return coef_zero;
+    case 1:
+        return coef_one;
+    case 2:
+        return coef_alphaa;
+    case 3:
+        return coef_one_minus_alphaa;
+    case 4:
+        return coef_alphab;
+    case 5:
+        return coef_one_minus_alphab;
+    case 10:
+        return coef_a;
+    case 11:
+        return coef_one_minus_a;
+    case 12:
+        return coef_b;
+    case 13:
+        return coef_one_minus_b;
+    default:
+        hw_error("s5pc1xx.lcd: illegal value\n");
+    }
+}
+
+static inline void putpixel_by_bpp(DrawConfig *cfg, int bpp)
+{
+    switch (bpp) {
+    case 8:
+        cfg->put_pixel = put_pixel8;
+        break;
+    case 15:
+        cfg->put_pixel = put_pixel15;
+        break;
+    case 16:
+        cfg->put_pixel = put_pixel16;
+        break;
+    case 24:
+        cfg->put_pixel = put_pixel24;
+        break;
+    case 32:
+        cfg->put_pixel = put_pixel32;
+        break;
+    default:
+        hw_error("s5pc1xx.lcd: unsupported BPP (%d)", bpp);
+    }
+}
+
+static void s5pc1xx_lcd_update(void *opaque)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    DrawConfig cfg;
+    int i, dirty[2], x;
+    int line;
+    target_phys_addr_t scanline, newline, map_len, pd, inc_size;
+    uint8_t *mapline, *startline, *valid_line_tmp;
+    int lefttop_x, lefttop_y, rightbottom_x, rightbottom_y;
+    int ext_line_size;
+    int width, height;
+    uint32_t tmp;
+    int buf_id;
+    int need_redraw;
+    int global_width, global_height;
+    int bpp;
+    uint8_t *d;
+    uint8_t is_first_window;
+
+    if (!s || !s->console || !ds_get_bits_per_pixel(s->console)) {
+        return;
+    }
+
+    if (! (s->vidcon[0] & 2)) {
+        return;
+    }
+
+    memset(&cfg, 0, sizeof(cfg));
+
+    s5pc1xx_update_resolution(s);
+
+    /* First we will mark lines of the display which need to be redrawn */
+    memset(s->valid_line, 0xFF,
+           ((((s->vidtcon[2] >> 11) & 0x7FF) + 1 + 7) >> 3) * sizeof(uint8_t));
+    for (i = 0; i < 5; i++) {
+        if (s->window[i].wincon & 1) {
+            lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
+            lefttop_y = (s->window[i].vidosd[0] >>  0) & 0x7FF;
+            rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
+            rightbottom_y = (s->window[i].vidosd[1] >>  0) & 0x7FF;
+            height = rightbottom_y - lefttop_y + 1;
+            width = rightbottom_x - lefttop_x + 1;
+            ext_line_size = s->window[i].buf_size & 0x1FFF;
+            buf_id = 0;
+            if (i <= 1) {
+                buf_id = (s->window[i].wincon >> 20) & 1;
+            }
+            /* According to documentation framebuffer is always located in
+               single bank of DRAM. Bits [31:24] of BUF_START encode bank
+               number, and [23:0] - address of the buffer in bank. We will
+               assume that DRAM Controller uses linear memory mapping so
+               BUF_START will be just address of the framebuffer. In the
+               other case framebuffer will be dispersed all over the system
+               memory so it is unclear how such system will work.
+
+               Moreover, we will ignore absence of carry bit bitween bits 23
+               and 24 while incrementing address in the hope that no
+               programmer will use such hack. */
+            scanline = s->window[i].buf_start[buf_id];
+            inc_size = (s->window[i].buf_size & 0x1FFF) +
+                       ((s->window[i].buf_size >> 13) & 0x1FFF);
+            cpu_physical_sync_dirty_bitmap(scanline,
+                                           scanline + height * inc_size);
+            pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
+                 (scanline & ~TARGET_PAGE_MASK);
+            dirty[0] = dirty[1] =
+                cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
+            cpu_physical_memory_reset_dirty(scanline, scanline, VGA_DIRTY_FLAG);
+            for (line = 0; line < height; line++) {
+                newline = scanline + ext_line_size;
+                for (x = scanline;
+                     x < newline;
+                     x += TARGET_PAGE_SIZE) {
+                    pd = (cpu_get_physical_page_desc(x) & TARGET_PAGE_MASK) +
+                         (x & ~TARGET_PAGE_MASK);
+                    dirty[1] = cpu_physical_memory_get_dirty(pd, VGA_DIRTY_FLAG);
+                    dirty[0] |= dirty[1];
+                }
+                if (dirty[0]) {
+                    tmp = line + lefttop_y;
+                    s->valid_line[tmp >> 3] &= ~(1 << (tmp & 0x7));
+                }
+                dirty[0] = dirty[1] = 0;
+                scanline += (s->window[i].buf_size & 0x1FFF) +
+                    ((s->window[i].buf_size >> 13) & 0x1FFF);
+            }
+            scanline = s->window[i].buf_start[buf_id];
+            pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
+                 (scanline & ~TARGET_PAGE_MASK);
+            cpu_physical_memory_reset_dirty(pd, pd + inc_size * height,
+                                            VGA_DIRTY_FLAG);
+        }
+    }
+
+    need_redraw = 0;
+    is_first_window = 1;
+    for (i = 0; i < 5; i++) {
+        if (s->window[i].wincon & 1) {
+            cfg.fg_alpha_pix = 0;
+            s5pc1xx_parse_win_bppmode(s, &cfg, i);
+            /* If we have mode with palletized color then we need to parse
+               palette color mode and set pixel-to-rgb conversion function
+               accordingly. */
+            if (cfg.is_palletized) {
+                tmp = s5pc1xx_wxpal(s, i);
+                /* Different windows have different mapping WxPAL to palette
+                   pixel format. This transform happens to unify them all. */
+                if (i < 2 && tmp < 7) {
+                    tmp = 6 - tmp;
+                }
+                cfg.pixel_to_rgb = wxpal_to_rgb[tmp];
+                if (tmp == 7) {
+                    cfg.fg_alpha_pix = 1;
+                }
+            }
+            cfg.put_pixel = put_rgba;
+            cfg.get_pixel = get_rgba;
+            cfg.bg_alpha_pix = 1;
+            cfg.color_mask = s->window[i].keycon[0] & 0xFFFFFF;
+            cfg.color_key = s->window[i].keycon[1];
+            cfg.color_ctl = (s->window[i].keycon[0] >> 24) & 7;
+            if (i == 0) {
+                cfg.fg_alpha[0] = s->window[i].vidw_alpha[0];
+                cfg.fg_alpha[1] = s->window[i].vidw_alpha[1];
+            } else {
+                cfg.fg_alpha[0] =
+                    unpack_by_4((s->window[i].vidosd[3] & 0xFFF000) >> 12) |
+                    (s->window[i].vidw_alpha[0] & 0xF0F0F);
+                cfg.fg_alpha[1] =
+                    unpack_by_4(s->window[i].vidosd[3] & 0xFFF) |
+                    (s->window[i].vidw_alpha[0] & 0xF0F0F);
+            }
+            cfg.bg_pixel_blending = 1;
+            cfg.fg_pixel_blending = s->window[i].wincon & (1 << 6);
+            cfg.fg_alpha_sel = (s->window[i].wincon >> 1) & 1;
+            cfg.palette = s->window[i].palette;
+            cfg.swap = (s->window[i].wincon >> 15) & 0xF;
+            cfg.coef_q = coef_decode((s->window[i].blendeq >> 18) & 0xF);
+            cfg.coef_p = coef_decode((s->window[i].blendeq >> 12) & 0xF);
+            cfg.coef_b = coef_decode((s->window[i].blendeq >>  6) & 0xF);
+            cfg.coef_a = coef_decode((s->window[i].blendeq >>  0) & 0xF);
+            if (is_first_window) {
+                cfg.blend = NULL;
+            } else {
+                cfg.blend = blend_colorkey;
+            }
+            is_first_window = 0;
+            /* At this point CFG is fully set up except WIDTH. We can proceed
+               with drawing. */
+            lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
+            lefttop_y = (s->window[i].vidosd[0] >>  0) & 0x7FF;
+            rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
+            rightbottom_y = (s->window[i].vidosd[1] >>  0) & 0x7FF;
+            height = rightbottom_y - lefttop_y + 1;
+            width = rightbottom_x - lefttop_x + 1;
+            cfg.width = width;
+            ext_line_size = (width * cfg.bpp) >> 3;
+            buf_id = 0;
+            if (i <= 1) {
+                buf_id = (s->window[i].wincon >> 20) & 1;
+            }
+            scanline = s->window[i].buf_start[buf_id];
+            global_width = (s->vidtcon[2] & 0x7FF) + 1;
+            global_height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+            /* See comment above about DRAM Controller memory mapping. */
+            map_len = ((s->window[i].buf_size & 0x1FFF) +
+                    ((s->window[i].buf_size >> 13) & 0x1FFF)) * height;
+            mapline = cpu_physical_memory_map(scanline, &map_len, 0);
+            if (!mapline) {
+                return;
+            }
+            startline = mapline;
+            for (line = 0; line < height; line++) {
+                tmp = line + lefttop_y;
+                if (s->invalidate ||
+                    !(s->valid_line[tmp >> 3] & (1 << (tmp & 7))) ||
+                    !(s->valid_line_prev[tmp >> 3] & (1 << (tmp & 7)))) {
+                    need_redraw = 1;
+                    cfg.draw_line(&cfg, mapline,
+                                  s->ifb + lefttop_x * 7 +
+                                      (lefttop_y + line) * global_width * 7,
+                                  s->ifb + lefttop_x * 7 +
+                                      (lefttop_y + line) * global_width * 7);
+                }
+                mapline += (s->window[i].buf_size & 0x1FFF) +
+                    ((s->window[i].buf_size >> 13) & 0x1FFF);
+            }
+            cpu_physical_memory_unmap(startline, map_len, 0, 0);
+        }
+    }
+    /* Last pass: copy resulting image to QEMU_CONSOLE. */
+    if (need_redraw) {
+        width = (s->vidtcon[2] & 0x7FF) + 1;
+        height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+        cfg.get_pixel = get_rgba;
+        bpp = ds_get_bits_per_pixel(s->console);
+        putpixel_by_bpp(&cfg, bpp);
+        bpp = (bpp + 1) >> 3;
+        d = ds_get_data(s->console);
+        for (line = 0; line < height; line++) {
+            draw_line_copy(&cfg, s->ifb + width * line * 7,
+                           d + width * line * bpp, NULL);
+        }
+        dpy_update(s->console, 0, 0, width, height);
+    }
+    valid_line_tmp = s->valid_line;
+    s->valid_line = s->valid_line_prev;
+    s->valid_line_prev = valid_line_tmp;
+    s->invalidate = 0;
+    s->vidintcon[1] |= 2;
+    s5pc1xx_lcd_update_irq(s);
+}
+
+static void s5pc1xx_lcd_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    int i, j, width, height;
+
+    width = (s->vidtcon[2] & 0x7FF) + 1;
+    height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+
+    qemu_put_be32s(f, &s->shadowcon);
+    qemu_put_be32s(f, &s->prtcon);
+    qemu_put_be32s(f, &s->dithcon);
+    qemu_put_be32s(f, &s->trigcon);
+    qemu_put_be32s(f, &s->ituifcon);
+    qemu_put_be32s(f, &s->blendcon);
+    qemu_put_be32s(f, &s->dualrgb);
+
+    for (i = 0; i < 2; i++) {
+        qemu_put_be32s(f, &s->vp1tcon[i]);
+        qemu_put_be32s(f, &s->vidintcon[i]);
+        qemu_put_be32s(f, &s->wpalcon[i]);
+        qemu_put_be32s(f, &s->ldi_cmdcon[i]);
+    }
+
+    for (i = 0; i < 3; i++) {
+        qemu_put_be32s(f, &s->vidcon[i]);
+        qemu_put_be32s(f, &s->vidtcon[i]);
+        qemu_put_be32s(f, &s->sifccon[i]);
+    }
+
+    for (i = 0; i < 4; i++) {
+        qemu_put_be32s(f, &s->i80ifcon[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_put_be32s(f, &s->ldi_cmd[i]);
+    }
+
+    //qemu_put_buffer(f, s->ifb, width * height * 7);
+
+    /* Not saving valid_line because the whole screen will be redrawn anyway */
+
+    for (i = 0; i < 5; i++) {
+        qemu_put_be32s(f, &s->window[i].wincon);
+        qemu_put_be32s(f, &s->window[i].buf_size);
+        qemu_put_be32s(f, &s->window[i].winmap);
+        qemu_put_be32s(f, &s->window[i].blendeq);
+
+        for (j = 0; j < 2; j++) {
+            qemu_put_be32s(f, &s->window[i].buf_start[j]);
+            qemu_put_be32s(f, &s->window[i].buf_end[j]);
+            qemu_put_be32s(f, &s->window[i].keycon[j]);
+            qemu_put_be32s(f, &s->window[i].vidw_alpha[j]);
+        }
+
+        for (j = 0; j < 4; j++) {
+            qemu_put_be32s(f, &s->window[i].vidosd[j]);
+        }
+
+        for (j = 0; j < 256; j++) {
+            qemu_put_be32s(f, &s->window[i].palette[j]);
+        }
+    }
+}
+
+static int s5pc1xx_lcd_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    int i, j, width, height;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->shadowcon);
+    qemu_get_be32s(f, &s->prtcon);
+    qemu_get_be32s(f, &s->dithcon);
+    qemu_get_be32s(f, &s->trigcon);
+    qemu_get_be32s(f, &s->ituifcon);
+    qemu_get_be32s(f, &s->blendcon);
+    qemu_get_be32s(f, &s->dualrgb);
+
+    for (i = 0; i < 2; i++) {
+        qemu_get_be32s(f, &s->vp1tcon[i]);
+        qemu_get_be32s(f, &s->vidintcon[i]);
+        qemu_get_be32s(f, &s->wpalcon[i]);
+        qemu_get_be32s(f, &s->ldi_cmdcon[i]);
+    }
+
+    for (i = 0; i < 3; i++) {
+        qemu_get_be32s(f, &s->vidcon[i]);
+        qemu_get_be32s(f, &s->vidtcon[i]);
+        qemu_get_be32s(f, &s->sifccon[i]);
+    }
+
+    width = (s->vidtcon[2] & 0x7FF) + 1;
+    height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+
+    for (i = 0; i < 4; i++) {
+        qemu_get_be32s(f, &s->i80ifcon[i]);
+    }
+
+    for (i = 0; i < 12; i++) {
+        qemu_get_be32s(f, &s->ldi_cmd[i]);
+    }
+
+    //qemu_get_buffer(f, s->ifb, width * height * 7);
+
+    for (i = 0; i < 5; i++) {
+        qemu_get_be32s(f, &s->window[i].wincon);
+        qemu_get_be32s(f, &s->window[i].buf_size);
+        qemu_get_be32s(f, &s->window[i].winmap);
+        qemu_get_be32s(f, &s->window[i].blendeq);
+
+        for (j = 0; j < 2; j++) {
+            qemu_get_be32s(f, &s->window[i].buf_start[j]);
+            qemu_get_be32s(f, &s->window[i].buf_end[j]);
+            qemu_get_be32s(f, &s->window[i].keycon[j]);
+            qemu_get_be32s(f, &s->window[i].vidw_alpha[j]);
+        }
+
+        for (j = 0; j < 4; j++) {
+            qemu_get_be32s(f, &s->window[i].vidosd[j]);
+        }
+
+        for (j = 0; j < 256; j++) {
+            qemu_get_be32s(f, &s->window[i].palette[j]);
+        }
+    }
+
+    s5pc1xx_update_resolution(s);
+    /* Redraw the whole screen */
+    s->invalidate = 1;
+    return 0;
+}
+
+static void s5pc1xx_lcd_invalidate(void *opaque)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    s->invalidate = 1;
+}
+
+static void s5pc1xx_window_reset(S5pc1xxLcdWindow *s)
+{
+    memset(s, 0, sizeof(*s));
+    s->blendeq = 0xC2;
+}
+
+static void s5pc1xx_lcd_reset(void *opaque)
+{
+    S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+    int i;
+
+    memset((uint8_t *)s + sizeof(SysBusDevice),
+            0, offsetof(S5pc1xxLcdState, window));
+    for (i = 0; i < 5; i++) {
+        s5pc1xx_window_reset(&s->window[i]);
+    }
+    if (s->ifb != NULL) {
+        qemu_free(s->ifb);
+    }
+    s->ifb = NULL;
+    if (s->valid_line != NULL) {
+        qemu_free(s->valid_line);
+    }
+    s->valid_line = NULL;
+    if (s->valid_line_prev != NULL) {
+        qemu_free(s->valid_line_prev);
+    }
+    s->valid_line_prev = NULL;
+}
+
+static int s5pc1xx_lcd_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxLcdState *s = FROM_SYSBUS(S5pc1xxLcdState, dev);
+
+    s->ifb = NULL;
+    s->valid_line = NULL;
+    s->valid_line_prev = NULL;
+    s5pc1xx_lcd_reset(s);
+
+    sysbus_init_irq(dev, &s->irq[0]);
+    sysbus_init_irq(dev, &s->irq[1]);
+    sysbus_init_irq(dev, &s->irq[2]);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_lcd_readfn, s5pc1xx_lcd_writefn, s,
+                                                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, 0x3800, iomemtype);
+
+    s->console = graphic_console_init(s5pc1xx_lcd_update,
+                                      s5pc1xx_lcd_invalidate, NULL, NULL, s);
+
+    qemu_register_reset(s5pc1xx_lcd_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.lcd", -1, 1,
+                    s5pc1xx_lcd_save, s5pc1xx_lcd_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_lcd_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.lcd", sizeof(S5pc1xxLcdState),
+                        s5pc1xx_lcd_init);
+}
+
+device_init(s5pc1xx_lcd_register_devices)
diff --git a/hw/s5pc1xx_mmc.c b/hw/s5pc1xx_mmc.c
new file mode 100644 (file)
index 0000000..e1de090
--- /dev/null
@@ -0,0 +1,725 @@
+/*
+ * MMC controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on SMDK6400 MMC (hw/smdk6400/smdk_mmc.c)
+ */
+
+#include "hw.h"
+#include "sd.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_hsmmc_regs.h"
+#include "block_int.h"
+#include "sysbus.h"
+
+#include "qemu-timer.h"
+
+/*#define DEBUG_MMC*/
+
+#ifdef DEBUG_MMC
+#define DPRINTF(fmt, args...) \
+do { fprintf(stderr, "QEMU SD/MMC: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define CMD_RESPONSE  (3 << 0)
+
+#define INSERTION_DELAY (get_ticks_per_sec())
+
+typedef struct S5pc1xxMMCState {
+    SysBusDevice busdev;
+
+    SDState *card;
+    uint8_t dma_transcpt;
+    uint32_t cmdarg;
+    uint32_t respcmd;
+    uint32_t response[4];
+    qemu_irq irq;
+
+    uint32_t sysad;
+    uint16_t blksize;
+    uint16_t blkcnt;
+    uint32_t argument;
+    uint16_t trnmod;
+    uint16_t cmdreg;
+    uint32_t prnsts;
+    uint8_t hostctl;
+    uint8_t pwrcon;
+    uint16_t clkcon;
+    uint8_t timeoutcon;
+    uint8_t swrst;
+    uint16_t norintsts;
+    uint16_t errintsts;
+    uint16_t norintstsen;
+    uint16_t errintstsen;
+    uint16_t norintsigen;
+    uint16_t errintsigen;
+    uint32_t control2;
+    uint32_t control3;
+
+    QEMUTimer *response_timer; /* command response. */
+    QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
+
+    qemu_irq eject;
+} S5pc1xxMMCState;
+
+
+static void mmc_dmaInt(S5pc1xxMMCState *s)
+{
+    if (s->dma_transcpt == 1)
+        s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+    else
+        s->norintsts |= S5C_HSMMC_NIS_DMA;
+    qemu_set_irq(s->irq, 1);
+}
+
+static void mmc_fifo_push(S5pc1xxMMCState *s, uint32_t pos, uint32_t value)
+{
+    cpu_physical_memory_write(s->sysad + pos, (uint8_t *)(&value), 4);
+}
+
+static uint32_t mmc_fifo_pop(S5pc1xxMMCState *s, uint32_t pos)
+{
+    uint32_t value = 0;
+
+    cpu_physical_memory_read(s->sysad + pos, (uint8_t *)(&value), 4);
+    return value;
+}
+
+static void s5pc1xx_mmc_raise_end_command_irq(void *opaque)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    DPRINTF("raise IRQ response\n");
+    qemu_irq_raise(s->irq);
+    s->norintsts |= S5C_HSMMC_NIS_CMDCMP;
+}
+
+static void mmc_send_command(S5pc1xxMMCState *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+    int rlen;
+
+    s->errintsts = 0;
+    qemu_mod_timer(s->response_timer, qemu_get_clock(vm_clock));
+    if (!s->card)
+        return;
+
+    request.cmd = s->cmdreg >> 8;
+    request.arg = s->cmdarg;
+    DPRINTF("Command %d %08x\n", request.cmd, request.arg);
+    rlen = sd_do_command(s->card, &request, response);
+    if (rlen < 0)
+        goto error;
+    if ((s->cmdreg & CMD_RESPONSE) != 0) {
+#define RWORD(n) ((n >= 0 ? (response[n] << 24) : 0) \
+                  | (response[n + 1] << 16) \
+                  | (response[n + 2] << 8) \
+                  |  response[n + 3])
+
+        if (rlen == 0)
+            goto error;
+        if (rlen != 4 && rlen != 16)
+            goto error;
+        s->response[0] = RWORD(0);
+        if (rlen == 4) {
+            s->response[1] = s->response[2] = s->response[3] = 0;
+        } else {
+            s->response[0] = RWORD(11);
+            s->response[1] = RWORD(7);
+            s->response[2] = RWORD(3);
+            s->response[3] = RWORD(-1);
+        }
+        DPRINTF("Response received\n");
+#undef RWORD
+    } else {
+        DPRINTF("Command sent\n");
+    }
+    return;
+
+error:
+    DPRINTF("Timeout\n");
+    s->errintsts |= S5C_HSMMC_EIS_CMDTIMEOUT;
+}
+
+/* Transfer data between the card and the FIFO.  This is complicated by
+   the FIFO holding 32-bit words and the card taking data in single byte
+   chunks.  FIFO bytes are transferred in little-endian order.  */
+static void mmc_fifo_run(S5pc1xxMMCState *s)
+{
+    uint32_t value;
+    int n;
+    uint32_t pos;
+    int is_read;
+    uint32_t datacnt, boundary_chk, boundary_count;
+    uint8_t dma_buf_boundary, dma_mask_flag;
+
+    is_read = (s->trnmod & S5C_HSMMC_TRNS_READ) != 0;
+
+    if (s->blkcnt != 0 && (!is_read || sd_data_ready(s->card))) {
+        n = 0;
+        value = 0;
+
+        if (s->blkcnt > 1) {
+            /* multi block */
+            if (s->norintstsen & 0x8)
+                dma_mask_flag = 1;  /* DMA enable */
+            else
+                dma_mask_flag = 0;  /* DMA disable */
+
+            dma_buf_boundary = (s->blksize & 0xf000) >> 12;
+            boundary_chk = 1 << (dma_buf_boundary+12);
+            boundary_count = boundary_chk - (s->sysad % boundary_chk);
+            while (s->blkcnt) {
+                datacnt = s->blksize & 0x0fff;
+                pos = 0;
+                while (datacnt) {
+                    if (is_read) {
+                        value |= (uint32_t)sd_read_data(s->card) << (n * 8);
+                        n++;
+                        if (n == 4) {
+                            mmc_fifo_push(s, pos, value);
+                            value = 0;
+                            n = 0;
+                            pos += 4;
+                        }
+                    } else {
+                        if (n == 0) {
+                            value = mmc_fifo_pop(s, pos);
+                            n = 4;
+                            pos += 4;
+                        }
+                        sd_write_data(s->card, value & 0xff);
+                        value >>= 8;
+                        n--;
+                    }
+                    datacnt--;
+                }
+                s->sysad += s->blksize & 0x0fff;
+                boundary_count -= s->blksize & 0x0fff;
+                s->blkcnt--;
+
+                if ((boundary_count == 0) && dma_mask_flag)
+                    break;
+            }
+            if (s->blkcnt == 0)
+                s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+            else
+                s->norintsts |= S5C_HSMMC_NIS_DMA;
+        } else {
+            /* single block */
+            datacnt = s->blksize & 0x0fff;
+            pos = 0;
+            while (datacnt) {
+                if (is_read) {
+                    value |=
+                        (uint32_t)sd_read_data(s->card) << (n * 8);
+                    n++;
+                    if (n == 4) {
+                        mmc_fifo_push(s, pos, value);
+                        value = 0;
+                        n = 0;
+                        pos += 4;
+                    }
+                } else {
+                    if (n == 0) {
+                        value = mmc_fifo_pop(s, pos);
+                        n = 4;
+                        pos += 4;
+                    }
+                sd_write_data(s->card, value & 0xff);
+                    value >>= 8;
+                    n--;
+                }
+                datacnt--;
+            }
+            s->blkcnt--;
+            s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+        }
+    }
+}
+
+/* MMC read (byte) function */
+static uint32_t mmc_readb(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    switch (offset) {
+    case (S5C_HSMMC_RSPREG0 + 0x3):
+        return (uint8_t)(s->response[0] >> 24);
+    case (S5C_HSMMC_RSPREG1 + 0x3):
+        return (uint8_t)(s->response[1] >> 24);
+    case (S5C_HSMMC_RSPREG2 + 0x3):
+        return (uint8_t)(s->response[2] >> 24);
+    case S5C_HSMMC_HOSTCTL:
+        return s->hostctl;
+    case S5C_HSMMC_PWRCON:
+        return s->pwrcon;
+    case S5C_HSMMC_BLKGAP:
+        return 0;
+    case S5C_HSMMC_WAKCON:
+        return 0;
+    case S5C_HSMMC_TIMEOUTCON:
+        return s->timeoutcon;
+    case S5C_HSMMC_SWRST:
+        return 0;
+    default:
+        hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* MMC read (word) function */
+static uint32_t mmc_readw(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    switch (offset) {
+    case S5C_HSMMC_BLKSIZE:
+        return s->blksize;
+    case S5C_HSMMC_BLKCNT:
+        return s->blkcnt;
+    case S5C_HSMMC_TRNMOD:
+        return s->trnmod;
+    case S5C_HSMMC_CLKCON:
+        return s->clkcon;
+    case S5C_HSMMC_SWRST:
+        return s->swrst;
+    case S5C_HSMMC_NORINTSTS:
+        qemu_set_irq(s->irq, 0);
+        return s->norintsts;
+    case S5C_HSMMC_NORINTSTSEN:
+        return s->norintstsen;
+    case S5C_HSMMC_ACMD12ERRSTS:
+        return 0;
+    case S5C_HSMMC_SLOT_INT_STATUS:
+        return 0;
+    case S5C_HSMMC_HCVER:
+        return 0x2401;
+    default:
+        hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* MMC read (doubleword) function */
+static uint32_t mmc_readl(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    switch (offset) {
+    case S5C_HSMMC_SYSAD:
+        return s->sysad;
+    case S5C_HSMMC_ARGUMENT:
+        return s->cmdarg;
+    case S5C_HSMMC_RSPREG0:
+        return s->response[0];
+    case S5C_HSMMC_RSPREG1:
+        return s->response[1];
+    case S5C_HSMMC_RSPREG2:
+        return s->response[2];
+    case S5C_HSMMC_RSPREG3:
+        return s->response[3];
+    case S5C_HSMMC_PRNSTS:
+        return s->prnsts;
+    case S5C_HSMMC_NORINTSTS:
+        qemu_set_irq(s->irq, 0);
+        return (s->errintsts << 16) | (s->norintsts);
+    case S5C_HSMMC_NORINTSTSEN:
+        return (s->errintstsen << 16) | s->norintstsen;
+    case S5C_HSMMC_NORINTSIGEN:
+        return (s->errintsigen << 16) | s->norintsigen;
+    case S5C_HSMMC_CAPAREG:
+        return 0x05E80080;
+    case S5C_HSMMC_MAXCURR:
+        return 0;
+    case S5C_HSMMC_CONTROL2:
+        return s->control2;
+    case S5C_HSMMC_CONTROL3:
+        return s->control3;
+    default:
+        hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* MMC write (byte) function */
+static void mmc_writeb(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    switch (offset) {
+    case S5C_HSMMC_HOSTCTL:
+        s->hostctl = value;
+        break;
+    case S5C_HSMMC_PWRCON:
+        s->pwrcon = value;
+        break;
+    case S5C_HSMMC_TIMEOUTCON:
+        s->timeoutcon = value;
+        break;
+    case S5C_HSMMC_SWRST:
+        s->swrst = value;
+        break;
+    default:
+        hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* MMC write (word) function */
+static void mmc_writew(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    switch (offset) {
+    case S5C_HSMMC_BLKSIZE:
+        s->blksize = value;
+        break;
+    case S5C_HSMMC_BLKCNT:
+        s->blkcnt = value;
+        break;
+    case S5C_HSMMC_TRNMOD:
+        s->trnmod = value;
+        break;
+    case S5C_HSMMC_CMDREG: /* Command */
+        s->cmdreg = value;
+        mmc_send_command(s);
+        mmc_fifo_run(s);
+        if (s->errintsts)
+            s->norintsts |= S5C_HSMMC_NIS_ERR;
+        s->norintsts |= S5C_HSMMC_NIS_CMDCMP;
+        if (s->norintsts & S5C_HSMMC_NIS_TRSCMP)
+            s->norintsts &= ~S5C_HSMMC_NIS_CMDCMP;
+        if (s->norintsts & S5C_HSMMC_NIS_DMA)
+            s->norintsts &= ~S5C_HSMMC_NIS_CMDCMP;
+        break;
+    case S5C_HSMMC_CLKCON:
+        s->clkcon = value;
+        if (S5C_HSMMC_CLOCK_INT_EN & s->clkcon)
+            s->clkcon |= S5C_HSMMC_CLOCK_INT_STABLE;
+        else
+            s->clkcon &= ~S5C_HSMMC_CLOCK_INT_STABLE;
+        if (S5C_HSMMC_CLOCK_CARD_EN & s->clkcon)
+            s->clkcon |= S5C_HSMMC_CLOCK_EXT_STABLE;
+        else
+            s->clkcon &= ~S5C_HSMMC_CLOCK_EXT_STABLE;
+        break;
+    case S5C_HSMMC_NORINTSTS:
+        s->norintsts &= ~value;
+        s->norintsts &= ~0x8100;
+        s->norintsts |= value & 0x8100;
+        break;
+    case S5C_HSMMC_NORINTSTSEN:
+        s->norintstsen = value;
+        break;
+    case S5C_HSMMC_NORINTSIGEN:
+        s->norintsigen = value;
+        break;
+    default:
+        hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* MMC write (doubleword) function */
+static void mmc_writel(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    int datacnt;
+    int is_read;
+    int value1, n;
+    uint32_t pos;
+    uint32_t boundary_chk, boundary_count;
+    uint32_t dma_buf_boundary;
+    uint8_t dma_mask_flag;
+
+    switch (offset) {
+    case S5C_HSMMC_SYSAD:
+        s->sysad = value;
+        if (s->blkcnt != 0) {
+            n = 0;
+            value1 = 0;
+            is_read = (s->trnmod & S5C_HSMMC_TRNS_READ) != 0;
+            dma_buf_boundary = (s->blksize & 0xf000) >> 12;
+            boundary_chk = 1 << (dma_buf_boundary + 12);
+
+            if (s->norintstsen & 0x8)
+                dma_mask_flag = 1; /* DMA enable */
+            else
+                dma_mask_flag = 0; /* DMA disable */
+
+            boundary_count = boundary_chk - (s->sysad % boundary_chk);
+            while (s->blkcnt) {
+                pos = 0;
+                datacnt = s->blksize & 0x0fff;
+                while (datacnt) {
+                    if (is_read) {
+                        value1 |= (uint32_t)sd_read_data(s->card) << (n * 8);
+                        n++;
+                        if (n == 4) {
+                            mmc_fifo_push(s, pos, value1);
+                            value1 = 0;
+                            n = 0;
+                            pos += 4;
+                        }
+                    } else {
+                        if (n == 0) {
+                            value1 = mmc_fifo_pop(s, pos);
+                            n = 4;
+                            pos += 4;
+                        }
+                        sd_write_data(s->card, value1 & 0xff);
+                        value1 >>= 8;
+                        n--;
+                    }
+                    datacnt--;
+                }
+                s->sysad += s->blksize & 0x0fff;
+                boundary_count -= s->blksize & 0x0fff;
+                s->blkcnt--;
+                if ((boundary_count == 0) && dma_mask_flag)
+                    break;
+            }
+            if (s->blkcnt == 0) {
+                s->dma_transcpt = 1;
+                mmc_dmaInt(s);
+            } else {
+                s->dma_transcpt = 0;
+                mmc_dmaInt(s);
+            }
+        }
+        break;
+    case S5C_HSMMC_ARGUMENT:
+        s->argument = value;
+        s->cmdarg = value;
+        break;
+    case S5C_HSMMC_NORINTSTS:
+        s->norintsts &= ~value;
+        s->norintsts &= ~0x8100;
+        s->norintsts |= value & 0x8100;
+        s->errintsts &= ~(value >> 16);
+
+        if (s->errintsts == 0) {
+            s->norintsts &= ~S5C_HSMMC_NIS_ERR; /* Error Interrupt clear */
+        }
+        break;
+    case S5C_HSMMC_NORINTSTSEN:
+        s->norintstsen = (uint16_t)value;
+        s->errintstsen = (uint16_t)(value >> 16);
+        break;
+    case S5C_HSMMC_NORINTSIGEN:
+        s->norintsigen = (uint16_t)value;
+        s->errintsigen = (uint16_t)(value >> 16);
+        break;
+    case S5C_HSMMC_CONTROL2:
+        s->control2 = value;
+        break;
+    case S5C_HSMMC_CONTROL3:
+        s->control3 = value;
+        break;
+    case S5C_HSMMC_CONTROL4:
+        /* Nothing for QENU emulation */
+        break;
+    default:
+        hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *mmc_readfn[] = {
+   mmc_readb,
+   mmc_readw,
+   mmc_readl
+};
+
+static CPUWriteMemoryFunc *mmc_writefn[] = {
+   mmc_writeb,
+   mmc_writew,
+   mmc_writel
+};
+
+static void s5pc1xx_mmc_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    int i;
+
+    /* FIXME: must the structure below be saved?
+     * SDState *card; */
+
+    qemu_put_8s   (f, &s->dma_transcpt);
+    qemu_put_be32s(f, &s->cmdarg);
+    qemu_put_be32s(f, &s->respcmd);
+
+    for(i = 0; i < 4; i++) {
+        qemu_put_be32s(f, &s->response[i]);
+    }
+
+    qemu_put_be32s(f, &s->sysad);
+    qemu_put_be16s(f, &s->blksize);
+    qemu_put_be16s(f, &s->blkcnt);
+    qemu_put_be32s(f, &s->argument);
+    qemu_put_be16s(f, &s->trnmod);
+    qemu_put_be16s(f, &s->cmdreg);
+    qemu_put_be32s(f, &s->prnsts);
+    qemu_put_8s   (f, &s->hostctl);
+    qemu_put_8s   (f, &s->pwrcon);
+    qemu_put_be16s(f, &s->clkcon);
+    qemu_put_8s   (f, &s->timeoutcon);
+    qemu_put_8s   (f, &s->swrst);
+    qemu_put_be16s(f, &s->norintsts);
+    qemu_put_be16s(f, &s->errintsts);
+    qemu_put_be16s(f, &s->norintstsen);
+    qemu_put_be16s(f, &s->errintstsen);
+    qemu_put_be16s(f, &s->norintsigen);
+    qemu_put_be16s(f, &s->errintsigen);
+    qemu_put_be32s(f, &s->control2);
+    qemu_put_be32s(f, &s->control3);
+
+    qemu_put_timer(f, s->response_timer);
+    qemu_put_timer(f, s->insert_timer);
+}
+
+static int s5pc1xx_mmc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    /* FIXME: SDState *card; */
+    qemu_get_8s   (f, &s->dma_transcpt);
+    qemu_get_be32s(f, &s->cmdarg);
+    qemu_get_be32s(f, &s->respcmd);
+
+    for(i = 0; i < 4; i++) {
+        qemu_get_be32s(f, &s->response[i]);
+    }
+
+    qemu_get_be32s(f, &s->sysad);
+    qemu_get_be16s(f, &s->blksize);
+    qemu_get_be16s(f, &s->blkcnt);
+    qemu_get_be32s(f, &s->argument);
+    qemu_get_be16s(f, &s->trnmod);
+    qemu_get_be16s(f, &s->cmdreg);
+    qemu_get_be32s(f, &s->prnsts);
+    qemu_get_8s   (f, &s->hostctl);
+    qemu_get_8s   (f, &s->pwrcon);
+    qemu_get_be16s(f, &s->clkcon);
+    qemu_get_8s   (f, &s->timeoutcon);
+    qemu_get_8s   (f, &s->swrst);
+    qemu_get_be16s(f, &s->norintsts);
+    qemu_get_be16s(f, &s->errintsts);
+    qemu_get_be16s(f, &s->norintstsen);
+    qemu_get_be16s(f, &s->errintstsen);
+    qemu_get_be16s(f, &s->norintsigen);
+    qemu_get_be16s(f, &s->errintsigen);
+    qemu_get_be32s(f, &s->control2);
+    qemu_get_be32s(f, &s->control3);
+
+    qemu_get_timer(f, s->response_timer);
+    qemu_get_timer(f, s->insert_timer);
+
+    return 0;
+}
+
+static void mmc_reset(void *opaque)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+    if (s->card) {
+        s->prnsts = 0x1ff0000;
+        s->norintsts |= 0x0040;
+    } else {
+        s->prnsts = 0x1fa0000;
+        s->norintsts = 0;
+    }
+    s->cmdarg      = 0;
+    s->respcmd     = 0;
+    s->response[0] = 0;
+    s->response[1] = 0;
+    s->response[2] = 0;
+    s->response[3] = 0;
+}
+
+static void s5pc1xx_mmc_raise_insertion_irq(void *opaque)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    DPRINTF("raise IRQ response\n");
+
+    if (s->norintsts & S5C_HSMMC_NIS_REMOVE) {
+        DPRINTF("s5pc1xx.mmc: raise_insertion_irq - set timer!\n");
+        qemu_mod_timer(s->insert_timer,
+                       qemu_get_clock(vm_clock) + INSERTION_DELAY);
+    } else {
+        DPRINTF("s5pc1xx.mmc: raise_insertion_irq - raise irq!\n");
+        s->norintsts |= S5C_HSMMC_NIS_INSERT;
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static void s5pc1xx_mmc_insert_eject(void *opaque, int irq, int level)
+{
+    S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+    DPRINTF("change card state: %s!\n", level ? "insert" : "eject");
+
+    if (s->norintsts & S5C_HSMMC_NIS_REMOVE) {
+        if (level) {
+            DPRINTF("change card state: timer set!\n");
+            qemu_mod_timer(s->insert_timer,
+                           qemu_get_clock(vm_clock) + INSERTION_DELAY);
+        }
+    } else {
+        s->norintsts |= level ? S5C_HSMMC_NIS_INSERT : S5C_HSMMC_NIS_REMOVE;
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static int s5pc1xx_mmc_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxMMCState *s = FROM_SYSBUS(S5pc1xxMMCState, dev);
+    BlockDriverState *bd;
+
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype = cpu_register_io_memory(mmc_readfn, mmc_writefn, s,
+                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5C_HSMMC_REG_SIZE, iomemtype);
+
+    bd = qdev_init_bdrv(&dev->qdev, IF_SD);
+
+    if ((bd == NULL)) {
+        s->card = NULL;
+        DPRINTF("s->card = NULL\n");
+    } else {
+        s->eject = qemu_allocate_irqs(s5pc1xx_mmc_insert_eject, s, 1)[0];
+
+        DPRINTF("name = %s, sectors = %ld\n",
+                bd->device_name, bd->total_sectors);
+
+        s->card = sd_init(bd, 0);
+        sd_set_cb(s->card, NULL, s->eject);
+    }
+
+    qemu_register_reset(mmc_reset, s);
+    mmc_reset(s);
+
+    s->response_timer =
+        qemu_new_timer(vm_clock, s5pc1xx_mmc_raise_end_command_irq, s);
+
+    s->insert_timer =
+        qemu_new_timer(vm_clock, s5pc1xx_mmc_raise_insertion_irq, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.mmc", -1, 1,
+                    s5pc1xx_mmc_save, s5pc1xx_mmc_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_mmc_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.mmc", sizeof(S5pc1xxMMCState),
+                        s5pc1xx_mmc_init);
+}
+
+device_init(s5pc1xx_mmc_register_devices)
diff --git a/hw/s5pc1xx_nand.c b/hw/s5pc1xx_nand.c
new file mode 100644 (file)
index 0000000..242c2bd
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * S5PC1XX NAND controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "hw.h"
+#include "flash.h"
+#include "sysbus.h"
+
+
+#define S5PC1XX_NAND_REG_MEM_SIZE 0x44
+
+
+typedef struct S5pc1xxNFConState {
+    SysBusDevice busdev;
+
+    uint32_t config;
+    uint32_t control;
+    /* TODO: how does this status and nand chip status corellate? */
+    uint32_t status;
+
+    NANDFlashState *flash;
+} S5pc1xxNFConState;
+
+
+static uint32_t nfcon_read32(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t x, res, i;
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    switch (offset) {
+    case 0x00: /* NFCONF */
+        return s->config;
+    case 0x04: /* NFCONT */
+        return s->control;
+    case 0x08: /* NFCMMD */
+    case 0x0C: /* NFADDR */
+        return 0x0;
+    case 0x10: /* NFDATA */
+        res = 0;
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        for (i = 0; i < 4; i++) {
+            x = nand_getio(s->flash);
+            res = res | (x << (8 * i));
+        }
+        return res;
+    case 0x14: /* NFMECCD0 */
+    case 0x18: /* NFMECCD1 */
+    case 0x1C: /* NFSECCD */
+        return 0;
+    case 0x20: /* NFSBLK */
+    case 0x24: /* NFEBLK */
+        /* TODO: implement this */
+        return 0;
+    case 0x28: /* NFSTAT */
+        return s->status;
+    case 0x2C: /* NFECCERR0 */
+    case 0x30: /* NFECCERR1 */
+    case 0x34: /* NFMECC0 */
+    case 0x38: /* NFMECC1 */
+    case 0x3C: /* NFSECC */
+    case 0x40: /* NFMLCBITPT */
+        return 0;
+    default:
+        hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static uint32_t nfcon_read16(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t x, res, i;
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    if (offset == 0x10) {
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        res = 0;
+        for (i = 0; i < 2; i++) {
+            x = nand_getio(s->flash);
+            res = res | (x << (8 * i));
+        }
+        return res;
+    } else {
+        hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static uint32_t nfcon_read8(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    if (offset == 0x10) {
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        return nand_getio(s->flash);
+    } else {
+        hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void nfcon_write32(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    switch (offset) {
+    case 0x00: /* NFCONF */
+        s->config = val;
+        break;
+    case 0x04: /* NFCONT */
+        s->control = val;
+        break;
+    case 0x08: /* NFCMMD */
+    case 0x0C: /* NFADDR */
+    case 0x10: /* NFDATA */
+        switch (offset) {
+        case 0x08:
+            nand_setpins(s->flash, 1, 0, 0, 1, 0);
+            break;
+        case 0x0C:
+            nand_setpins(s->flash, 0, 1, 0, 1, 0);
+            break;
+        case 0x10:
+            nand_setpins(s->flash, 0, 0, 0, 1, 0);
+            break;
+        }
+        nand_setio(s->flash, (val >>  0) & 0xff);
+        nand_setio(s->flash, (val >>  8) & 0xff);
+        nand_setio(s->flash, (val >> 16) & 0xff);
+        nand_setio(s->flash, (val >> 24) & 0xff);
+        break;
+    case 0x14: /* NFMECCD0 */
+    case 0x18: /* NFMECCD1 */
+    case 0x1C: /* NFSECCD */
+        break;
+    case 0x20: /* NFSBLK */
+    case 0x24: /* NFEBLK */
+        /* TODO: implement this */
+        break;
+    case 0x28: /* NFSTAT */
+        /* Ignore written value. Documentation states that this register is
+           R/W, but it describes states of the input pins. So what does write
+           to it suppose to do? */
+        break;
+    case 0x2C: /* NFECCERR0 */
+    case 0x30: /* NFECCERR1 */
+    case 0x34: /* NFMECC0 */
+    case 0x38: /* NFMECC1 */
+    case 0x3C: /* NFSECC */
+    case 0x40: /* NFMLCBITPT */
+        break;
+    default:
+        hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void nfcon_write16(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    switch (offset) {
+        case 0x08:
+            nand_setpins(s->flash, 1, 0, 0, 1, 0);
+            break;
+        case 0x0C:
+            nand_setpins(s->flash, 0, 1, 0, 1, 0);
+            break;
+        case 0x10:
+            nand_setpins(s->flash, 0, 0, 0, 1, 0);
+            break;
+        default:
+            hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+    nand_setio(s->flash, (val >>  0) & 0xff);
+    nand_setio(s->flash, (val >>  8) & 0xff);
+}
+
+static void nfcon_write8(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    switch (offset) {
+        case 0x08:
+            nand_setpins(s->flash, 1, 0, 0, 1, 0);
+            break;
+        case 0x0C:
+            nand_setpins(s->flash, 0, 1, 0, 1, 0);
+            break;
+        case 0x10:
+            nand_setpins(s->flash, 0, 0, 0, 1, 0);
+            break;
+        default:
+            hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+    nand_setio(s->flash, (val >>  0) & 0xff);
+}
+
+static CPUReadMemoryFunc * const nfcon_readfn[] = {
+    nfcon_read8,
+    nfcon_read16,
+    nfcon_read32
+};
+
+static CPUWriteMemoryFunc * const nfcon_writefn[] = {
+    nfcon_write8,
+    nfcon_write16,
+    nfcon_write32
+};
+
+static void s5pc1xx_nand_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    qemu_put_be32s(f, &s->config);
+    qemu_put_be32s(f, &s->control);
+    qemu_put_be32s(f, &s->status);
+}
+
+static int s5pc1xx_nand_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->config);
+    qemu_get_be32s(f, &s->control);
+    qemu_get_be32s(f, &s->status);
+
+    return 0;
+}
+
+static void s5pc1xx_nand_reset(void *opaque)
+{
+    S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+    s->config  = 0x00001000;
+    s->control = 0x000100C6;
+    s->status  = 0xF0800F0D;
+}
+
+static int s5pc1xx_nand_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxNFConState *s = FROM_SYSBUS(S5pc1xxNFConState, dev);
+
+    s->flash = nand_init(NAND_MFR_SAMSUNG, 0xA2);
+    s5pc1xx_nand_reset(s);
+
+    iomemtype = cpu_register_io_memory(nfcon_readfn, nfcon_writefn, s,
+                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_NAND_REG_MEM_SIZE, iomemtype);
+
+    qemu_register_reset(s5pc1xx_nand_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.nand", -1, 1,
+                    s5pc1xx_nand_save, s5pc1xx_nand_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_nand_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.nand", sizeof(S5pc1xxNFConState),
+                        s5pc1xx_nand_init);
+}
+
+device_init(s5pc1xx_nand_register_devices)
diff --git a/hw/s5pc1xx_onedram.c b/hw/s5pc1xx_onedram.c
new file mode 100644 (file)
index 0000000..2ca0faf
--- /dev/null
@@ -0,0 +1,1112 @@
+/*
+ * S5PC1XX OneDRAM controller.
+ *
+ * Contributed by Kefeng Li <li.kefeng@samsung.com>
+ */
+
+#include "s5pc1xx_onedram.h"
+
+#define TICK_COUNTDOWN  50
+
+uint32_t sem_retry = 50;
+
+
+/* Command handler */
+static int onedram_req_active_handler(S5pc1xxOneDRAMState *s)
+{
+    uint16_t cmd = INT_COMMAND(INT_MASK_CMD_RES_ACTIVE);
+
+    onedram_send_cmd_to_pda(s, cmd);
+    return COMMAND_SUCCESS;
+}
+
+static int onedram_smp_req_handler(S5pc1xxOneDRAMState *s)
+{
+    uint16_t cmd;
+
+    onedram_put_authority(s);
+
+    cmd = INT_COMMAND(INT_MASK_CMD_SMP_REP);
+    onedram_send_cmd_to_pda(s, cmd);
+
+    return COMMAND_SUCCESS;
+}
+
+
+/* timer for waiting the semaphore for sem_retry times try */
+static void onedram_wait_semaphore(void *opaque)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+    int64_t timeout;
+
+    if(sem_retry <= 0) {
+        fprintf(stderr, "time out to wait semaphore from AP\n");
+        qemu_del_timer(s->sem_timer);
+        sem_retry = 100;
+    } else if(onedram_read_sem(s)) {
+        sem_retry--;
+        timeout = get_ticks_per_sec();
+        qemu_mod_timer(s->sem_timer, qemu_get_clock(vm_clock) + TICK_COUNTDOWN);
+    } else {
+        sem_retry = 100;
+        qemu_del_timer(s->sem_timer);
+        onedram_fmt_send_cmd(s);
+    }
+}
+
+/* try to see if we have semaphore for sending, if not, request it from AP */
+static int onedram_fmt_try_send_cmd(S5pc1xxOneDRAMState *s)
+{
+    if(onedram_read_sem(s)) {
+        fprintf(stderr, "onedram_fmt_try_send_cmd - can't get anthority\n");
+        qemu_mod_timer(s->sem_timer, qemu_get_clock(vm_clock) + TICK_COUNTDOWN);
+    } else {
+        onedram_fmt_send_cmd(s);
+    }
+    return 1;
+}
+
+/* the real sending function of fmt */
+static int onedram_fmt_send_cmd(S5pc1xxOneDRAMState *s)
+{
+    int psrc = -1;
+    uint32_t len;
+    int ret = 0;
+
+    onedram_disable_interrupt(s);
+    onedram_disable_write(s);
+
+    do {
+        psrc = onedram_read_fmt(s, &len);
+
+        if (psrc < 0) {
+            ret = -1;
+            break;
+        }
+        if (len == 0) { /* read done */
+            ret = 0;
+            break;
+        }
+        if (!onedram_insert_socket(s, psrc, len))
+            break;
+    } while (1);
+
+    onedram_socket_push(s);
+
+    onedram_enable_interrupt(s);
+    onedram_enable_write(s);
+    return ret;
+}
+
+
+static void onedram_data_handler_fmt_autonomous(S5pc1xxOneDRAMState *s)
+{
+    uint16_t non_cmd = 0;
+    uint32_t in_head = 0, in_tail = 0;
+
+    if (onedram_can_access_shm(s)) {
+        in_head = onedram_read_inhead(s);
+        in_tail = onedram_read_intail(s);
+
+        if (in_head != in_tail) {
+            non_cmd |= INT_MASK_SEND_FMT;
+            fprintf(stderr, "formated partition has head-tail mis-match\n");
+        }
+    } else {
+        fprintf(stderr,
+                "onedram_data_handler_fmt_autonomous - can't access shm\n");
+    }
+
+    if (non_cmd & INT_MASK_SEND_FMT)
+        onedram_fmt_try_send_cmd(s);
+}
+
+/*static*/ void onedram_command_handler(S5pc1xxOneDRAMState *s,
+                                        uint32_t data)
+{
+    uint8_t cmd = (uint8_t) (data & 0xff);
+
+    onedram_data_handler_fmt_autonomous(s);
+
+    switch (cmd) {
+    case INT_MASK_CMD_NONE:
+        return;
+    case INT_MASK_CMD_REQ_ACTIVE:
+        onedram_req_active_handler(s);
+        return;
+    case INT_MASK_CMD_RES_ACTIVE:
+        return;
+    case INT_MASK_CMD_INIT_START:
+        return;
+    case INT_MASK_CMD_INIT_END:
+        return;
+    case INT_MASK_CMD_ERR_DISPLAY:
+        return;
+    case INT_MASK_CMD_PHONE_START:
+        return;
+    case INT_MASK_CMD_REQ_TIME_SYNC:
+        return;
+    case INT_MASK_CMD_PHONE_DEEP_SLEEP:
+        return;
+    case INT_MASK_CMD_NV_REBUILDING:
+        return;
+    case INT_MASK_CMD_EMER_DOWN:
+        return;
+    case INT_MASK_CMD_SMP_REQ:
+        onedram_smp_req_handler(s);
+        return;
+    case INT_MASK_CMD_SMP_REP:
+        return;
+    default:
+        fprintf(stderr, "command_handler: Unknown command.. %x\n", cmd);
+        return;
+    }
+}
+
+/*static*/ void onedram_data_handler(S5pc1xxOneDRAMState *s,
+                                     uint16_t non_cmd)
+{
+    if (non_cmd & INT_MASK_SEND_FMT)
+        onedram_fmt_try_send_cmd(s);
+}
+
+/* Shared Memory R/W */
+static uint32_t onedram_can_access_shm(S5pc1xxOneDRAMState *s)
+{
+    return !(onedram_io_readl(s, ONEDRAM_SEM));
+}
+
+static int onedram_read_shm(S5pc1xxOneDRAMState *s, uint8_t *buf,
+                            uint32_t offset, uint32_t size)
+{
+    uint8_t *src_base;
+    target_phys_addr_t phy_base, src_len;
+    phy_base = ONEDRAM_SHARED_BASE + offset;
+    src_len = size;
+
+    if (!onedram_can_access_shm(s)) {
+        fprintf(stderr,
+                "onedram_read_shm : can't access to shm\n");
+        return 0;
+    }
+
+    if (size > (ONEDRAM_SHARED_SIZE - offset)){
+        fprintf(stderr,
+                "onedram_read_shm : size exceed the maximum\n");
+        return 0;
+    }
+
+    src_base = cpu_physical_memory_map(phy_base, &src_len, 0);
+
+    if (!src_base) {
+        fprintf(stderr,
+                "onedram_read_shm : src_base is NULL\n");
+        return 0;
+    }
+
+    memcpy(buf, src_base, src_len);
+
+    cpu_physical_memory_unmap(src_base, src_len, 0, src_len);
+
+    return 1;
+}
+
+static int onedram_write_shm(S5pc1xxOneDRAMState *s,
+                             const uint8_t *buf, uint32_t offset,
+                             uint32_t size)
+{
+    uint8_t *src_base;
+    target_phys_addr_t phy_base, src_len;
+    phy_base = ONEDRAM_SHARED_BASE + offset;
+    src_len = size;
+
+    if (!onedram_can_access_shm(s)) {
+        fprintf(stderr,
+                "onedram_write_shm : can't access to fmt\n");
+        return 0;
+    }
+
+    if (size > ONEDRAM_IN_FMT_SIZE){
+        fprintf(stderr,
+                "onedram_write_shm : size exceeds the maximum\n");
+        return 0;
+    }
+
+    src_base = cpu_physical_memory_map(phy_base, &src_len, 1);
+
+    if (!src_base) {
+        fprintf(stderr,
+                "onedram_write_shm : src_base is NULL\n");
+        return 0;
+    }
+
+    memcpy(src_base, buf, size);
+
+    cpu_physical_memory_unmap(src_base, src_len, 1, src_len);
+
+    return 1;
+}
+
+/* Formatted Shared Memory Operation */
+/*static*/ uint32_t onedram_read_outhead(S5pc1xxOneDRAMState *s)
+{
+    uint32_t head = 0;
+
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_read_shm(s, (uint8_t *)&head,
+                         s->fmt_info->out_head_addr,
+                         s->fmt_info->ptr_size);
+    }
+    return head;
+}
+
+/*static*/ uint32_t onedram_read_inhead(S5pc1xxOneDRAMState *s)
+{
+    uint32_t head = 0;
+
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_read_shm(s, (uint8_t *)&head,
+                         s->fmt_info->in_head_addr,
+                         s->fmt_info->ptr_size);
+    }
+    return head;
+}
+
+/*static*/ uint32_t onedram_read_outtail(S5pc1xxOneDRAMState *s)
+{
+    uint32_t tail = 0;
+
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_read_shm(s, (uint8_t *)&tail,
+                         s->fmt_info->out_tail_addr,
+                         s->fmt_info->ptr_size);
+    }
+    return tail;
+}
+
+/*static*/ uint32_t onedram_read_intail(S5pc1xxOneDRAMState *s)
+{
+    uint32_t tail = 0;
+
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_read_shm(s, (uint8_t *)&tail,
+                         s->fmt_info->in_tail_addr,
+                         s->fmt_info->ptr_size);
+    }
+    return tail;
+}
+
+/*static*/ uint32_t onedram_write_outhead(S5pc1xxOneDRAMState *s,
+                                          uint32_t head)
+{
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_write_shm(s, (uint8_t *)&head,
+                          s->fmt_info->out_head_addr,
+                          s->fmt_info->ptr_size);
+    }
+    return head;
+}
+
+/*static*/ uint32_t onedram_write_inhead(S5pc1xxOneDRAMState *s,
+                                         uint32_t head)
+{
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_write_shm(s, (uint8_t *)&head,
+                          s->fmt_info->in_head_addr,
+                          s->fmt_info->ptr_size);
+    }
+    return head;
+}
+
+/*static*/ uint32_t onedram_write_outtail(S5pc1xxOneDRAMState *s,
+                                          uint32_t tail)
+{
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_write_shm(s, (uint8_t *)&tail,
+                          s->fmt_info->out_tail_addr,
+                          s->fmt_info->ptr_size);
+    }
+    return tail;
+}
+
+/*static*/ uint32_t onedram_write_intail(S5pc1xxOneDRAMState *s,
+                                         uint32_t tail)
+{
+    if (!s->fmt_info) {
+        fprintf(stderr, "err\n");
+    } else {
+        onedram_write_shm(s, (uint8_t *)&tail,
+                          s->fmt_info->in_tail_addr,
+                          s->fmt_info->ptr_size);
+    }
+    return tail;
+}
+
+/*static*/ int onedram_read_fmt(S5pc1xxOneDRAMState *s,
+                                uint32_t *len)
+{
+    int psrc = -1;
+    uint32_t head = 0, tail = 0;
+    uint32_t new_tail = 0;
+
+    head = onedram_read_outhead(s);
+    tail = onedram_read_outtail(s);
+
+    if (head >= s->fmt_info->out_buff_size ||
+        tail >= s->fmt_info->out_buff_size) {
+        fprintf(stderr, "head(%d) or tail(%d) is out of bound\n", head, tail);
+        goto done_onedram_read_fmt;
+    }
+
+    if (head == tail) {
+        psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+        *len = 0;
+        goto done_onedram_read_fmt;
+    }
+
+    if (head > tail) {
+        /* ------- tail ++++++++++++ head -------- */
+        psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+        *len = (head - tail);
+    } else {
+        /* +++++++ head ------------ tail ++++++++ */
+        psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+        *len = (s->fmt_info->out_buff_size - tail);
+    }
+
+    /* new tail */
+    new_tail = (uint32_t)((tail + *len) % s->fmt_info->out_buff_size);
+    onedram_write_outtail(s, new_tail);
+
+done_onedram_read_fmt:
+    return psrc;
+}
+
+static int onedram_insert_socket(S5pc1xxOneDRAMState *s,
+                                 uint32_t psrc, uint16_t size)
+{
+    uint8_t *buf;
+
+    if ((s->socket_len + size) >= SOCKET_BUFFER_MAX_SIZE) {
+        fprintf(stderr, "the socket buffer is overflow!\n");
+
+        onedram_read_shm(s, (s->socket_buffer + s->socket_len), psrc,
+                         SOCKET_BUFFER_MAX_SIZE - s->socket_len);
+        s->socket_len = SOCKET_BUFFER_MAX_SIZE;
+        return 0;
+    } else {
+        buf = s->socket_buffer + s->socket_len;
+        onedram_read_shm(s, buf, psrc, size);
+        s->socket_len += size;
+        return 1;
+    }
+}
+
+void onedram_socket_push(S5pc1xxOneDRAMState *s)
+{
+    onedram_tcp_write(s, s->socket_buffer, s->socket_len);
+    s->socket_len = 0;
+}
+
+int onedram_write_fmt(S5pc1xxOneDRAMState *s, const uint8_t *buf,
+                      uint32_t len)
+{
+    int ret = FAIL;
+    uint32_t size = 0;
+    uint32_t head = 0, tail = 0;
+    uint16_t irq_mask = 0;
+
+    if (!s->fmt_info || !buf)
+        return FAIL;
+
+    onedram_disable_interrupt(s);
+    onedram_disable_write(s);
+
+    head = onedram_read_inhead(s);
+    tail = onedram_read_intail(s);
+
+    if (head < tail) {
+        /* +++++++++ head ---------- tail ++++++++++ */
+        size = tail - head - 1;
+        size = (len > size) ? size : len;
+
+        onedram_write_shm(s, (uint8_t *)buf,
+                              s->fmt_info->in_buff_addr + head, size);
+        ret = size;
+    } else if (tail == 0) {
+        /* tail +++++++++++++++ head --------------- */
+        size = s->fmt_info->in_buff_size - head - 1;
+        size = (len > size) ? size : len;
+
+        onedram_write_shm(s, (uint8_t *)buf,
+                          s->fmt_info->in_buff_addr + head, size);
+        ret = size;
+    } else {
+        /* ------ tail +++++++++++ head ------------ */
+        size = s->fmt_info->in_buff_size - head;
+        size = (len > size) ? size : len;
+
+        onedram_write_shm(s, (uint8_t *)buf,
+                          s->fmt_info->in_buff_addr + head, size);
+        ret = (int)size;
+
+        if ((int)len > ret) {
+            size = (len - ret > tail - 1) ? tail - 1 : len - ret;
+            buf += ret;
+            onedram_write_shm(s, (uint8_t *)buf,
+                              s->fmt_info->in_buff_addr, size);
+            ret += (int)size;
+        }
+    }
+
+    /* calculate new head */
+    head = (uint32_t)((head + ret) % s->fmt_info->in_buff_size);
+    onedram_write_inhead(s, head);
+
+    if (head >= s->fmt_info->in_buff_size ||
+        tail >= s->fmt_info->in_buff_size) {
+        fprintf(stderr, "head(%d) or tail(%d) is out of bound\n", head, tail);
+        goto err_onedram_write_fmt;
+    }
+
+    /* send interrupt to the phone, if.. */
+    irq_mask = INT_MASK_VALID;
+
+    if (ret > 0)
+        irq_mask |= s->fmt_info->mask_send;
+
+    if ((int)len > ret)
+        irq_mask |= s->fmt_info->mask_req_ack;
+
+    onedram_put_authority(s);
+    onedram_enable_interrupt(s);
+    onedram_send_cmd_to_pda(s, irq_mask);
+    onedram_enable_write(s);
+
+    return ret;
+
+err_onedram_write_fmt:
+    onedram_put_authority(s);
+    onedram_enable_interrupt(s);
+    onedram_enable_write(s);
+
+    return ret;
+}
+
+/* Interrupt Operation */
+static uint32_t onedram_irq_cp_raise_32(S5pc1xxOneDRAMState *s)
+{
+    uint32_t irq_mask;
+    int64_t timeout;
+
+    s->irq_onedram_int_cp_pending = 1;
+    irq_mask = onedram_io_readl(s, ONEDRAM_MBX_BA);
+
+    switch (irq_mask) {
+    case IPC_CP_IMG_LOADED:
+        onedram_io_writel(s, ONEDRAM_MBX_AB, IPC_CP_READY);
+        timeout = get_ticks_per_sec();
+        qemu_mod_timer(s->bootup_timer, qemu_get_clock(vm_clock) + timeout);
+        return IRQ_HANDLED;
+    case IPC_CP_READY_FOR_LOADING:
+        return IRQ_HANDLED;
+    default:
+        fprintf(stderr, "onedram_irq_cp_raise_32: unknown command\n");
+        break;
+    }
+
+    return IRQ_HANDLED;
+}
+
+static uint32_t onedram_irq_cp_raise_16(S5pc1xxOneDRAMState *s)
+{
+    uint16_t irq_mask;
+    s->irq_onedram_int_cp_pending = 1;
+
+    irq_mask = (uint16_t)onedram_io_readl(s, ONEDRAM_MBX_BA);
+
+    if (!(irq_mask & INT_MASK_VALID)) {
+        fprintf(stderr, "Invalid interrupt mask: 0x%04x\n", irq_mask);
+        return IRQ_NONE;
+    }
+
+    if (irq_mask & INT_MASK_COMMAND) {
+        irq_mask &= ~(INT_MASK_VALID | INT_MASK_COMMAND);
+        onedram_command_handler(s, irq_mask);
+    } else {
+        irq_mask &= ~INT_MASK_VALID;
+        onedram_data_handler(s, irq_mask);
+    }
+
+    return IRQ_HANDLED;
+}
+
+void onedram_disable_interrupt(S5pc1xxOneDRAMState *s)
+{
+    s->onedram_state.interruptable = 0;
+}
+
+void onedram_enable_interrupt(S5pc1xxOneDRAMState *s)
+{
+    s->onedram_state.interruptable = 1;
+}
+
+uint16_t onedram_interruptable(S5pc1xxOneDRAMState *s)
+{
+    return s->onedram_state.interruptable;
+}
+
+static void onedram_irq_cp_lower(S5pc1xxOneDRAMState *s)
+{
+    s->irq_onedram_int_cp_pending = 0;
+}
+
+static uint32_t onedram_irq_cp_pending(S5pc1xxOneDRAMState *s)
+{
+    return s->irq_onedram_int_cp_pending;
+}
+
+static void onedram_irq_ap_raise(S5pc1xxOneDRAMState *s)
+{
+    s->irq_onedram_int_ap_pending = 1;
+    qemu_irq_raise(s->irq_onedram_int_ap);
+}
+
+static void onedram_irq_ap_lower(S5pc1xxOneDRAMState *s)
+{
+    s->irq_onedram_int_ap_pending = 0;
+    qemu_irq_lower(s->irq_onedram_int_ap);
+}
+
+static uint32_t onedram_irq_ap_pending(S5pc1xxOneDRAMState *s)
+{
+    return s->irq_onedram_int_ap_pending;
+}
+
+/* Authority Operation */
+unsigned int onedram_read_sem(S5pc1xxOneDRAMState *s)
+{
+    unsigned int sem;
+
+    sem = onedram_io_readl(s, ONEDRAM_SEM);
+
+    return sem;
+}
+
+static void onedram_put_authority(S5pc1xxOneDRAMState *s)
+{
+    uint32_t sem;
+    sem = 0x1;
+    onedram_io_writel(s, ONEDRAM_SEM, sem);
+}
+
+int onedram_try_get_authority(S5pc1xxOneDRAMState *s)
+{
+    uint16_t cmd = 0;
+
+    if(!onedram_read_sem(s))
+        return TRUE;
+
+    cmd = INT_COMMAND(INT_MASK_CMD_SMP_REQ);
+    onedram_send_cmd_to_pda(s, cmd);
+
+    return FALSE;
+}
+
+void onedram_disable_write(S5pc1xxOneDRAMState *s)
+{
+    s->onedram_state.writable = 0;
+}
+
+void onedram_enable_write(S5pc1xxOneDRAMState *s)
+{
+    s->onedram_state.writable = 1;
+}
+
+uint16_t onedram_writable(S5pc1xxOneDRAMState *s)
+{
+    return s->onedram_state.writable;
+}
+
+void onedram_send_cmd_to_pda(S5pc1xxOneDRAMState *s, uint16_t val)
+{
+    uint16_t check_ab;
+
+    check_ab = (uint16_t)onedram_io_readl(s, ONEDRAM_CHECK_AB);
+    check_ab &= 0x1;
+    if (!check_ab) {
+        onedram_io_writel(s, ONEDRAM_MBX_AB, (uint32_t)val);
+    } else {
+        fprintf(stderr, "mailbox_ab has not been read by AP yet!\n");
+    }
+}
+
+/* Boot up timer */
+static void onedram_bootup(void *opaque)
+{
+    uint16_t cmd;
+
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+    if(!s->vmodem_bootup) {
+        onedram_io_writel(s, ONEDRAM_MBX_AB, IPC_CP_READY_FOR_LOADING);
+        s->vmodem_bootup = 1;
+
+    } else {
+        /* Init the in/out head/tail */
+        onedram_write_inhead(s, 0);
+        onedram_write_intail(s, 0);
+        onedram_write_outhead(s, 0);
+        onedram_write_outtail(s, 0);
+        /* put the authority to AP to let it access to shared memory */
+        onedram_put_authority(s);
+        cmd = INT_COMMAND(INT_MASK_CMD_PHONE_START|CP_CHIP_INFINEON);
+        onedram_send_cmd_to_pda(s, cmd);
+        qemu_del_timer(s->bootup_timer);
+    }
+}
+
+/* Register Modem */
+static void onedram_register_modem(S5pc1xxOneDRAMState *s,
+                                   ModemPlatformData *mp)
+{
+    /* fmt info */
+    s->fmt_info->in_head_addr = mp->in_fmt_base;
+    s->fmt_info->in_tail_addr = mp->in_fmt_base + mp->ptr_fmt_size;
+    s->fmt_info->in_buff_addr =
+                FMT_IN_BUF_PTR/*mp->in_fmt_base + (mp->ptr_fmt_size << 1)*/;
+    s->fmt_info->in_buff_size = mp->in_fmt_size;
+    s->fmt_info->out_head_addr = mp->out_fmt_base;
+    s->fmt_info->out_tail_addr = mp->out_fmt_base + mp->ptr_fmt_size;
+    s->fmt_info->out_buff_addr =
+                FMT_OUT_BUF_PTR/*mp->out_fmt_base + (mp->ptr_fmt_size << 1)*/;
+    s->fmt_info->out_buff_size = mp->out_fmt_size;
+    s->fmt_info->mask_req_ack = INT_MASK_REQ_ACK_FMT;
+    s->fmt_info->mask_res_ack = INT_MASK_RES_ACK_FMT;
+    s->fmt_info->mask_send = INT_MASK_SEND_FMT;
+    s->fmt_info->ptr_size = mp->ptr_fmt_size;
+}
+
+/* onedram IO R/W */
+static uint32_t onedram_io_readb(void *opaque,
+                                 target_phys_addr_t offset)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return (s->sem) & 0x000000FF;
+    case 0x01:
+        return ((s->sem >> 8 ) & 0x000000FF);
+    case 0x20:
+        return (s->mbx_ab) & 0x000000FF;
+    case 0x21:
+        /* If interrupt is pending, once after AP reads the mailbox_ab,
+         * we should clear the pending */
+        if (onedram_irq_ap_pending(s))
+            onedram_irq_ap_lower(s);
+        /* set check_ab to 0 means the mailbox_ab has been read by AP */
+        s->check_ab = 0;
+        return ((s->mbx_ab >> 8) & 0x000000FF);
+    /* when modem is booting, the AP will read the high two bytes of mail_box
+     * (only for new onedram driver) */
+    case 0x22:
+        return ((s->mbx_ab >> 16) & 0x000000FF);
+    case 0x23:
+        /* If interrupt is pending, once after AP reads the mailbox_ab,
+         * we should clear the pending */
+        if (onedram_irq_ap_pending(s))
+            onedram_irq_ap_lower(s);
+        /* set check_ab to 0 means the mailbox_ab has been read by AP */
+        s->check_ab = 0;
+        return ((s->mbx_ab >> 24) & 0x000000FF);
+    case 0x40:
+        return (s->mbx_ba) & 0x000000FF;
+    case 0x41:
+        if (onedram_irq_cp_pending(s))
+            onedram_irq_cp_lower(s);
+        /* set check_ba to 0 means the mailbox_ab has been read by CP */
+        s->check_ba = 0;
+        return ((s->mbx_ba >> 8 ) & 0x000000FF);
+    case 0xA0:
+        return (s->check_ab) & 0x000000FF;
+    case 0xA1:
+        return ((s->check_ab >> 8 ) & 0x000000FF);
+    case 0xC0:
+        return ((s->check_ba) & 0x000000FF);
+    case 0xC1:
+        return ((s->check_ba >> 8 ) & 0x000000FF);
+    default:
+        hw_error("onedram: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static uint32_t onedram_io_readl(void *opaque,
+                                 target_phys_addr_t offset)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return s->sem;
+    case 0x20:
+        /* If interrupt is pending, once AP reads the mailbox_ab,
+         * we should clear the pending */
+        if (onedram_irq_ap_pending(s))
+            onedram_irq_ap_lower(s);
+        /* set check_ab to 0 means the mailbox_ab has been read by AP */
+        s->check_ab = 0;
+        return s->mbx_ab;
+    case 0x40:
+        if (onedram_irq_cp_pending(s))
+            onedram_irq_cp_lower(s);
+        /* set check_ba to 0 means the mailbox_ab has been read by CP */
+        s->check_ba = 0;
+        return s->mbx_ba;
+    case 0xA0:
+        return s->check_ab;
+    case 0xC0:
+        return s->check_ba;
+    default:
+        hw_error("onedram: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void onedram_io_writeb(void *opaque, target_phys_addr_t offset,
+                              uint32_t val)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->sem = val;
+        break;
+    case 0x20:
+        (s->mbx_ab) = (val & 0x000000FF);
+        break;
+    case 0x21:
+        (s->mbx_ab) |= ((val << 8) & 0x0000FF00) ;
+        /* set check_ab to 1 means the mailbox_ab is waiting for AP to read */
+        s->check_ab = 1;
+        /* If interrupt is not pending, raise the interrupt to AP */
+        if (!onedram_irq_ap_pending(s))
+            onedram_irq_ap_raise(s);
+        break;
+    case 0x40:
+        (s->mbx_ba) = (val & 0x000000FF);
+        break;
+    case 0x41:
+        (s->mbx_ba) |= ((val << 8) & 0x0000FF00);
+        /* set check_ab to 1 means the mailbox_ba is waiting for CP to read */
+        s->check_ba = 1;
+        /* raise an interrupt to inform CP that there is a message has come */
+        if (onedram_interruptable(s))
+            onedram_irq_cp_raise_16(s);
+        break;
+    case 0xA0:
+        (s->check_ab) = (val & 0x000000FF);
+        break;
+    case 0xA1:
+        (s->check_ab) |= ((val << 8) & 0x0000FF00);
+        break;
+    case 0xC0:
+        (s->check_ba) = (val & 0x000000FF);
+        break;
+    case 0xC1:
+        (s->check_ba) |= ((val << 8) & 0x0000FF00) ;
+        break;
+    default:
+        hw_error("onedram: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void onedram_io_writel(void *opaque, target_phys_addr_t offset,
+                              uint32_t val)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->sem = val;
+        break;
+    case 0x20:
+        s->mbx_ab = val;
+        s->check_ab = 1;
+        if (!onedram_irq_ap_pending(s))
+            onedram_irq_ap_raise(s);
+        break;
+    case 0x40:
+        s->mbx_ba = val;
+        s->check_ba = 1;
+        if (onedram_interruptable(s))
+            onedram_irq_cp_raise_32(s);
+        break;
+    case 0xA0:
+        s->check_ab = val;
+        break;
+    case 0xC0:
+        s->check_ba = val;
+        break;
+    default:
+        hw_error("onedram: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+/*
+ *  In mailbox there are 2 bytes CMD and 4 bytes message,
+ *  but 4 bytes only for booting the phone.
+ *  in other cases, only use 2 bytes read
+ */
+static CPUReadMemoryFunc * const onedram_mm_read[] = {
+    onedram_io_readb,
+    onedram_io_readl,
+    onedram_io_readl
+};
+
+static CPUWriteMemoryFunc * const onedram_mm_write[] = {
+    onedram_io_writeb,
+    onedram_io_writel,
+    onedram_io_writel
+};
+
+static void s5pc1xx_onedram_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    qemu_put_be32s(f, &s->magic_code);
+    qemu_put_be32s(f, &s->sem);
+    qemu_put_be32s(f, &s->mbx_ab);
+    qemu_put_be32s(f, &s->mbx_ba);
+    qemu_put_be32s(f, &s->check_ab);
+    qemu_put_be32s(f, &s->check_ba);
+    qemu_put_be32s(f, &s->irq_onedram_int_ap_pending);
+    qemu_put_be32s(f, &s->irq_onedram_int_cp_pending);
+
+    /* FIXME: must the structure below be saved?
+     * CharDriverState *socket; */
+    qemu_put_be32s(f, &s->vmodem_connected);
+    qemu_put_be32s(f, &s->vmodem_bootup);
+
+    qemu_put_be32s (f, &s->socket_len);
+    qemu_put_buffer(f, s->socket_buffer, s->socket_len);
+
+    qemu_put_be16s(f, &s->onedram_state.waiting_authority);
+    qemu_put_be16s(f, &s->onedram_state.waiting_sem_rep);
+    qemu_put_be16s(f, &s->onedram_state.waiting_check);
+    qemu_put_be16s(f, &s->onedram_state.non_cmd);
+    qemu_put_be16s(f, &s->onedram_state.send_cmd);
+    qemu_put_be16s(f, &s->onedram_state.interruptable);
+    qemu_put_be16s(f, &s->onedram_state.writable);
+
+    qemu_put_8s    (f, &s->onedram_state.send_size);
+    qemu_put_buffer(f, s->onedram_state.send_buf, s->onedram_state.send_size);
+
+    qemu_put_timer(f, s->bootup_timer);
+    qemu_put_timer(f, s->sem_timer);
+}
+
+static int s5pc1xx_onedram_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->magic_code);
+    qemu_get_be32s(f, &s->sem);
+    qemu_get_be32s(f, &s->mbx_ab);
+    qemu_get_be32s(f, &s->mbx_ba);
+    qemu_get_be32s(f, &s->check_ab);
+    qemu_get_be32s(f, &s->check_ba);
+    qemu_get_be32s(f, &s->irq_onedram_int_ap_pending);
+    qemu_get_be32s(f, &s->irq_onedram_int_cp_pending);
+
+    /* FIXME: CharDriverState *socket; */
+    qemu_get_be32s(f, &s->vmodem_connected);
+    qemu_get_be32s(f, &s->vmodem_bootup);
+
+    qemu_get_be32s(f, &s->socket_len);
+    qemu_get_buffer(f, s->socket_buffer, s->socket_len);
+
+    qemu_get_be16s(f, &s->onedram_state.waiting_authority);
+    qemu_get_be16s(f, &s->onedram_state.waiting_sem_rep);
+    qemu_get_be16s(f, &s->onedram_state.waiting_check);
+    qemu_get_be16s(f, &s->onedram_state.non_cmd);
+    qemu_get_be16s(f, &s->onedram_state.send_cmd);
+    qemu_get_be16s(f, &s->onedram_state.interruptable);
+    qemu_get_be16s(f, &s->onedram_state.writable);
+
+    qemu_get_8s(f, &s->onedram_state.send_size);
+    qemu_get_buffer(f, s->onedram_state.send_buf, s->onedram_state.send_size);
+
+    qemu_get_timer(f, s->bootup_timer);
+    qemu_get_timer(f, s->sem_timer);
+
+    return 0;
+}
+
+/* Socket for Vmodem operation*/
+static int onedram_tcp_can_read(void *opaque)
+{
+    return MAX_BUFFER;
+}
+
+static void onedram_tcp_read(void *opaque, const uint8_t *buf,
+                             int size)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+    uint32_t send_cmd;
+    int64_t timeout;
+    /* In booting stage, we need to set up the connection to
+     * Vmodem thru Socket */
+    if (!s->vmodem_connected) {
+        if (((uint32_t *)buf)[0] == IPC_CP_CONNECT_APP) {
+            send_cmd = IPC_AP_CONNECT_ACK;
+            onedram_tcp_write(s, (uint8_t *)&send_cmd, CONNECT_LENGTH);
+            s->vmodem_connected = 1;
+            /* put the anthority to AP,
+             * because AP will try to load the modem image for CP */
+            onedram_put_authority(s);
+            /* before here, the PSI has been loaded by CP already,
+             * in the new onedram driver,
+             * we have to send IPC_CP_READY_FOR_LOADING to AP
+             * rather than waiting for to be read from AP */
+            timeout = get_ticks_per_sec();
+            qemu_mod_timer(s->bootup_timer,
+                           qemu_get_clock(vm_clock) + timeout/10);
+        }
+    } else {
+        /* The connection to Vmodem has been set up,
+         * so now we only exchange IPC */
+        if (onedram_writable(s)) {
+            //onedram_prepare_write_fmt(s, buf, size);
+            onedram_write_fmt(s, buf, size);
+        } else {
+            return;
+        }
+    }
+}
+
+void onedram_tcp_write(void *opaque, const uint8_t *buf, uint32_t size)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    s->socket->chr_write(s->socket, buf, size);
+}
+
+static void onedram_tcp_event(void *opaque, int event)
+{
+    /* not implemented yet */
+}
+
+static void onedram_tcp_init(void *opaque)
+{
+    S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+    /* open a socket to communicate with vmodem */
+    const char *p = "tcp:localhost:7777,server,nowait";
+    s->socket= qemu_chr_open("onedram_socket", p, NULL);
+
+    if (!s->socket)
+        hw_error("onedram: could not open onedram socket\n");
+
+    qemu_chr_add_handlers(s->socket, onedram_tcp_can_read,
+                          onedram_tcp_read, onedram_tcp_event,
+                          s);
+}
+
+DeviceState *s5pc1xx_onedram_init(const char *name, target_phys_addr_t base,
+                                  qemu_irq irq_ap)
+{
+    DeviceState *dev = qdev_create(NULL, name);
+    ram_addr_t onedram_shared, onedram_ap;
+
+    qdev_init_nofail(dev);
+    onedram_ap = qemu_ram_alloc(dev, "s5pc1xx.onedram.ap", ONEDRAM_AP_SIZE);
+    cpu_register_physical_memory(base, ONEDRAM_AP_SIZE,
+                                 onedram_ap | IO_MEM_RAM);
+    onedram_shared =
+      qemu_ram_alloc(dev, "s5pc1xx.onedram.shared", ONEDRAM_SHARED_SIZE);
+    cpu_register_physical_memory(base + ONEDRAM_AP_SIZE, ONEDRAM_SHARED_SIZE,
+                                 onedram_shared | IO_MEM_RAM);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+                    base + ONEDRAM_AP_SIZE + ONEDRAM_SFR);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq_ap);
+    return dev;
+}
+
+static int s5pc1xx_onedram_init1(SysBusDevice *dev, ModemPlatformData *mp)
+{
+    S5pc1xxOneDRAMState *s = FROM_SYSBUS(S5pc1xxOneDRAMState, dev);
+    int onedram_io;
+
+    sysbus_init_irq(dev, &s->irq_onedram_int_ap);
+    onedram_io = cpu_register_io_memory(onedram_mm_read,
+                                        onedram_mm_write, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, ONEDRAM_REGISTER_SIZE, onedram_io);
+
+    s->sem = 0;
+    s->mbx_ab = 0;
+    s->mbx_ba = 0;
+    s->check_ab = 0;
+    s->check_ba = 0;
+
+    s->irq_onedram_int_ap_pending = 0;
+    s->irq_onedram_int_cp_pending = 0;
+
+    s->vmodem_connected = 0;
+    s->vmodem_bootup = 0;
+    s->fmt_info = (ModemInfo *)qemu_mallocz(sizeof(ModemInfo));
+
+    s->socket_buffer = (uint8_t *)qemu_mallocz(SOCKET_BUFFER_MAX_SIZE);
+    s->socket_len = 0;
+
+    s->onedram_state.waiting_authority = FALSE;
+    s->onedram_state.non_cmd = INT_MASK_CMD_NONE;
+    s->onedram_state.send_size = 0;
+    s->onedram_state.waiting_sem_rep = 0;
+    s->onedram_state.send_buf = NULL;
+    s->onedram_state.interruptable = 1;
+    s->onedram_state.writable = 1;
+
+    onedram_register_modem(s, mp);
+    onedram_tcp_init(s);
+
+    s->bootup_timer = qemu_new_timer(vm_clock, onedram_bootup, s);
+    s->sem_timer = qemu_new_timer(vm_clock, onedram_wait_semaphore, s);
+    register_savevm(&dev->qdev, "s5pc1xx.onedram", -1, 1,
+                    s5pc1xx_onedram_save, s5pc1xx_onedram_load, s);
+
+    return 0;
+}
+
+static int s5pc1xx_onedram_aquila_xmm_init(SysBusDevice *dev)
+{
+    return s5pc1xx_onedram_init1(dev, &aquila_xmm_modem_data);
+}
+static void s5pc1xx_onedram_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.onedram.aquila.xmm",
+                        sizeof(S5pc1xxOneDRAMState),
+                        s5pc1xx_onedram_aquila_xmm_init);
+}
+
+device_init(s5pc1xx_onedram_register_devices)
diff --git a/hw/s5pc1xx_onedram.h b/hw/s5pc1xx_onedram.h
new file mode 100644 (file)
index 0000000..d3d3d8b
--- /dev/null
@@ -0,0 +1,397 @@
+#ifndef __S5PC1XX_ONEDRAM_H__
+#define __S5PC1XX_ONEDRAM_H__
+
+#include "sysbus.h"
+#include "console.h"
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+
+#define TRUE     1
+#define FALSE    0
+
+#define SUCCESS  1
+#define FAIL    -1
+
+#define SZ_4K                           0x00001000
+#define SZ_1K                           0x00000400
+#define SZ_16M                          0x01000000
+
+/* SDRAM */
+#define S5PC11X_PA_SDRAM                (0x30000000)
+
+/*
+ * OneDRAM memory map specific definitions
+ */
+#define ONEDRAM_BASE                    0x30000000
+#define ONEDRAM_AP_BASE                 (ONEDRAM_BASE)
+#define ONEDRAM_AP_SIZE                 0x05000000      /* 80MB */
+#define ONEDRAM_SHARED_BASE             (ONEDRAM_BASE + ONEDRAM_AP_SIZE)
+#define ONEDRAM_SHARED_SIZE             0x00500000      /* 0x400000*/
+                                                        /* 0xFFF800*/
+                                                        /* 16382KB */
+#define ONEDRAM_REGISTER_SIZE           0x800
+
+/*
+ * OneDRAM Interrupt related definitions
+ */
+#define ONEDRAM_SFR                     0xFFF800
+#define ONEDRAM_IO_BASE                 (ONEDRAM_SHARED_BASE + ONEDRAM_SFR)
+
+/* A is Modem, B is Application Processor */
+#define ONEDRAM_SEM                     0x00            /* semaphore */
+#define ONEDRAM_MBX_AB                  0x20            /* mailbox AtoB */
+#define ONEDRAM_MBX_BA                  0x40            /* mailbox BtoA */
+#define ONEDRAM_CHECK_AB                0xa0            /* check AtoB */
+#define ONEDRAM_CHECK_BA                0xc0            /* check BtoA */
+
+#define IPC_MAGIC_PTR                   0x0
+#define IPC_ACCESS_PTR                  0x4
+
+#define ONEDRAM_START_ADDRESS           0
+#define ONEDRAM_MAGIC_CODE_ADDRESS      (ONEDRAM_START_ADDRESS + IPC_MAGIC_PTR)
+#define ONEDRAM_ACCESS_ENABLE_ADDRESS   (ONEDRAM_START_ADDRESS + IPC_ACCESS_PTR)
+
+#define IPC_PART_PTR_SIZE               0x4
+
+/* formatted region */
+#define FMT_OUT_HEAD_PTR                0x10
+#define FMT_OUT_TAIL_PTR                (FMT_OUT_HEAD_PTR + IPC_PART_PTR_SIZE)
+#define FMT_IN_HEAD_PTR                 0x18
+#define FMT_IN_TAIL_PTR                 (FMT_IN_HEAD_PTR + IPC_PART_PTR_SIZE)
+
+/*raw region */
+#define RAW_OUT_HEAD_PTR                0x20
+#define RAW_OUT_TAIL_PTR                (RAW_OUT_HEAD_PTR + IPC_PART_PTR_SIZE)
+#define RAW_IN_HEAD_PTR                 0x28
+#define RAW_IN_TAIL_PTR                 (RAW_IN_HEAD_PTR + IPC_PART_PTR_SIZE)
+
+/* remote file system region */
+#define RFS_OUT_HEAD_PTR                0x30
+#define RFS_OUT_TAIL_PTR                (RFS_OUT_HEAD_PTR + IPC_PART_PTR_SIZE)
+#define RFS_IN_HEAD_PTR                 0x38
+#define RFS_IN_TAIL_PTR                 (RFS_IN_HEAD_PTR + IPC_PART_PTR_SIZE)
+
+#define CP_FATAL_DISP_SIZE              0xA0
+#define CP_FATAL_DISP_PTR               0x1000
+
+#define FMT_BUF_SIZE                    0x1000
+#define FMT_OUT_BUF_PTR                 0xFE000
+#define FMT_IN_BUF_PTR                  0xFF000
+
+#define RAW_BUF_SIZE                    0x100000
+#define RAW_OUT_BUF_PTR                 0x100000
+#define RAW_IN_BUF_PTR                  0x200000
+
+#define RFS_BUF_SIZE                    0x100000
+#define RFS_OUT_BUF_PTR                 0x300000
+#define RFS_IN_BUF_PTR                  0x400000
+
+#define IPC_PART_SIZE                   0x500000
+
+#define ONEDRAM_OUT_FMT_BASE            FMT_OUT_BUF_PTR
+#define ONEDRAM_OUT_FMT_SIZE            FMT_BUF_SIZE
+
+#define ONEDRAM_OUT_RAW_BASE            RAW_OUT_BUF_PTR
+#define ONEDRAM_OUT_RAW_SIZE            RAW_BUF_SIZE
+
+#define ONEDRAM_OUT_RFS_BASE            RFS_OUT_BUF_PTR
+#define ONEDRAM_OUT_RFS_SIZE            RFS_BUF_SIZE
+
+#define ONEDRAM_IN_FMT_BASE             FMT_IN_BUF_PTR
+#define ONEDRAM_IN_FMT_SIZE             FMT_BUF_SIZE
+
+#define ONEDRAM_IN_RAW_BASE             RAW_IN_BUF_PTR
+#define ONEDRAM_IN_RAW_SIZE             RAW_BUF_SIZE
+
+#define ONEDRAM_IN_RFS_BASE             RFS_IN_BUF_PTR
+#define ONEDRAM_IN_RFS_SIZE             RFS_BUF_SIZE
+
+
+#define MAGIC_CODE                      0x00aa
+#define ACCESS_ENABLE                   0x0001
+
+#define IRQ_ONEDRAM_INT_AP              11
+#define IRQ_PHONE_ACTIVE                15
+
+#define MAX_BUFFER                      1024
+
+#define CONNECT_LENGTH                  4
+
+#define TCP_CMD_LENGTH                  2
+
+
+/* AP IPC define */
+#define IPC_AP_CONNECT_ACK              0xABCD1234
+#define IPC_AP_SEND_FMT_ACK             0xCDAB1234
+
+
+/* CP IPC define */
+#define IPC_CP_CONNECT_APP              0x1234ABCD
+#define IPC_CP_READY_FOR_LOADING        0x12341234
+#define IPC_CP_IMG_LOADED               0x45674567
+#define IPC_CP_READY                    0xABCDABCD
+
+/*
+ * Samsung IPC 4.0 specific definitions
+ */
+#define INT_MASK_VALID                  0x0080
+#define INT_MASK_COMMAND                0x0040
+    /* If not command */
+    #define INT_MASK_REQ_ACK_RFS        0x0400
+    #define INT_MASK_RES_ACK_RFS        0x0200
+    #define INT_MASK_SEND_RFS           0x0100
+    #define INT_MASK_REQ_ACK_FMT        0x0020
+    #define INT_MASK_REQ_ACK_RAW        0x0010
+    #define INT_MASK_RES_ACK_FMT        0x0008
+    #define INT_MASK_RES_ACK_RAW        0x0004
+    #define INT_MASK_SEND_FMT           0x0002
+    #define INT_MASK_SEND_RAW           0x0001
+
+#define INT_MASK_CMD_NONE               0x0000
+#define INT_MASK_CMD_INIT_START         0x0001
+#define INT_MASK_CMD_INIT_END           0x0002
+    /* CMD_INIT_END extended bit */
+    /* CP boot state */
+    #define REQ_ONLINE_BOOT             0x0000
+    #define REQ_AIRPLANE_BOOT           0x1000
+    /* AP OS type */
+    #define AP_OS_ANDROID               0x0100
+    #define AP_OS_WINMOBILE             0x0200
+    #define AP_OS_LINUX                 0x0300
+    #define AP_OS_SYMBIAN               0x0400
+#define INT_MASK_CMD_REQ_ACTIVE         0x0003
+#define INT_MASK_CMD_RES_ACTIVE         0x0004
+#define INT_MASK_CMD_REQ_TIME_SYNC      0x0005
+#define INT_MASK_CMD_PHONE_START        0x0008
+    /* CMD_PHONE_START extended bit */
+    /* CP chip type */
+    #define CP_CHIP_QUALCOMM            0x0100
+    #define CP_CHIP_INFINEON            0x0200
+    #define CP_CHIP_BROADCOM            0x0300
+#define INT_MASK_CMD_ERR_DISPLAY        0x0009
+#define INT_MASK_CMD_PHONE_DEEP_SLEEP   0x000A
+#define INT_MASK_CMD_NV_REBUILDING      0x000B
+#define INT_MASK_CMD_EMER_DOWN          0x000C
+#define INT_MASK_CMD_SMP_REQ            0x000D
+#define INT_MASK_CMD_SMP_REP            0x000E
+#define INT_MASK_CMD_MAX                0x000F
+
+#define INT_COMMAND(x)                  (INT_MASK_VALID | INT_MASK_COMMAND | x)
+#define INT_NON_COMMAND(x)              (INT_MASK_VALID | x)
+
+#define BIT_INT_MASK_CMD_REQ_ACTIVE       0x001
+#define BIT_INT_MASK_CMD_ERR_DISPLAY      0x002
+#define BIT_INT_MASK_CMD_PHONE_START      0x004
+#define BIT_INT_MASK_CMD_REQ_TIME_SYNC    0x008
+#define BIT_INT_MASK_CMD_PHONE_DEEP_SLEEP 0x010
+#define BIT_INT_MASK_CMD_NV_REBUILDING    0x020
+#define BIT_INT_MASK_CMD_EMER_DOWN        0x040
+#define BIT_INT_MASK_CMD_SMP_REQ          0x080
+#define BIT_INT_MASK_CMD_SMP_REP          0x100
+#define BIT_MAX                           0x200
+
+#define FMT_SERVICE     0
+#define RAW_SERVICE     1
+#define RFS_SERVICE     2
+
+/* IRQ definition */
+#define IRQ_NONE                        (0)
+#define IRQ_HANDLED                     (1)
+#define IRQ_RETVAL(x)                   ((x) != 0)
+
+/*
+ * Modem deivce partitions.
+ */
+#define FMT_INDEX                       0
+#define RAW_INDEX                       1
+#define RFS_INDEX                       2
+#define MAX_INDEX                       3
+
+#define MESG_PHONE_OFF                  1
+#define MESG_PHONE_RESET                2
+
+#define RETRY                           50
+#define TIME_RESOLUTION                 10
+
+#define COMMAND_SUCCESS                 0x00
+#define COMMAND_GET_AUTHORITY_FAIL      0x01
+#define COMMAND_FAIL                    0x02
+
+#define CONFIG_MODEM_CORE_FMT_SERVICE
+
+typedef struct ModemServiceOps {
+    int (*send_cmd_handler)(uint16_t);
+    void (*resp_cmd_handler)(void);
+} ModemServiceOps;
+
+typedef struct ModemInfo {
+    /* DPRAM memory addresses */
+    uint32_t in_head_addr;
+    uint32_t in_tail_addr;
+    uint32_t in_buff_addr;
+    uint32_t in_buff_size;
+
+    uint32_t out_head_addr;
+    uint32_t out_tail_addr;
+    uint32_t out_buff_addr;
+    uint32_t out_buff_size;
+
+    int ptr_size;
+
+    uint16_t mask_req_ack;
+    uint16_t mask_res_ack;
+    uint16_t mask_send;
+} ModemInfo;
+
+typedef struct ModemPlatformData {
+    const char *name;
+    uint32_t booting_type;
+    uint32_t irq_onedram_int_ap;
+    uint32_t out_fmt_base;
+    uint32_t out_fmt_size;
+
+    uint32_t in_fmt_base;
+    uint32_t in_fmt_size;
+
+    int ptr_fmt_size;
+} ModemPlatformData;
+
+/* Booting type definitions */
+#define XMM                             1
+#define MSM                             2
+#define QSC                             3
+
+#define QEMU_MODEM                      XMM
+
+#define SOCKET_BUFFER_MAX_SIZE          SZ_1K
+
+typedef struct OneDRAMState {
+    uint16_t waiting_authority;
+    uint16_t waiting_sem_rep;
+    uint16_t waiting_check;
+    uint16_t non_cmd;
+    uint16_t send_cmd;
+    uint16_t interruptable;
+    uint16_t writable;
+
+    uint8_t  *send_buf;
+    uint8_t  send_size;
+} OneDRAMState;
+
+typedef struct S5pc1xxOneDRAMState {
+    SysBusDevice busdev;
+
+    uint32_t magic_code;
+    uint32_t sem;
+    uint32_t mbx_ab;
+    uint32_t mbx_ba;
+    uint32_t check_ab;
+    uint32_t check_ba;
+
+    qemu_irq irq_onedram_int_ap;
+    uint32_t irq_onedram_int_ap_pending;
+    uint32_t irq_onedram_int_cp_pending;
+
+    CharDriverState *socket;
+    uint32_t vmodem_connected;
+    uint32_t vmodem_bootup;
+    QEMUTimer *bootup_timer;
+    QEMUTimer *sem_timer;
+
+    /* OneDRAM memory addresses */
+    ModemInfo* fmt_info;
+
+    uint8_t *socket_buffer;
+    uint32_t socket_len;
+
+    OneDRAMState onedram_state;
+} S5pc1xxOneDRAMState;
+
+/* OneDRAM */
+static struct ModemPlatformData aquila_xmm_modem_data = {
+    .name               = "aquila-XMM6160",
+    .booting_type       = XMM,
+    .irq_onedram_int_ap = 11,
+
+     /* Memory map */
+    .out_fmt_base       = FMT_OUT_HEAD_PTR,
+    .out_fmt_size       = FMT_BUF_SIZE,
+    .in_fmt_base        = FMT_IN_HEAD_PTR,
+    .in_fmt_size        = FMT_BUF_SIZE,
+    .ptr_fmt_size       = IPC_PART_PTR_SIZE,
+
+};
+
+/* Command handler */
+static int onedram_req_active_handler(S5pc1xxOneDRAMState *s);
+static int onedram_smp_req_handler(S5pc1xxOneDRAMState *s);
+static int onedram_fmt_try_send_cmd(S5pc1xxOneDRAMState *s);
+static int onedram_fmt_send_cmd(S5pc1xxOneDRAMState *s);
+static void onedram_data_handler_fmt_autonomous(S5pc1xxOneDRAMState *s);
+/*static*/ void onedram_command_handler(S5pc1xxOneDRAMState *s,
+                                        uint32_t data);
+/*static*/ void onedram_data_handler(S5pc1xxOneDRAMState *s,
+                                     uint16_t non_cmd);
+static uint32_t onedram_can_access_shm(S5pc1xxOneDRAMState *s);
+static int onedram_read_shm(S5pc1xxOneDRAMState *s, uint8_t *buf,
+                            uint32_t offset, uint32_t size);
+static int onedram_write_shm(S5pc1xxOneDRAMState *s,
+                             const uint8_t *buf, uint32_t offset,
+                             uint32_t size);
+/*static*/ uint32_t onedram_read_outhead(S5pc1xxOneDRAMState *s);
+/*static*/ uint32_t onedram_read_inhead(S5pc1xxOneDRAMState *s);
+/*static*/ uint32_t onedram_read_outtail(S5pc1xxOneDRAMState *s);
+/*static*/ uint32_t onedram_read_intail(S5pc1xxOneDRAMState *s);
+/*static*/ uint32_t onedram_write_outhead(S5pc1xxOneDRAMState *s,
+                                          uint32_t head);
+/*static*/ uint32_t onedram_write_inhead(S5pc1xxOneDRAMState *s,
+                                         uint32_t head);
+/*static*/ uint32_t onedram_write_outtail(S5pc1xxOneDRAMState *s,
+                                          uint32_t tail);
+/*static*/ uint32_t onedram_write_intail(S5pc1xxOneDRAMState *s,
+                                         uint32_t tail);
+/*static*/ int onedram_read_fmt(S5pc1xxOneDRAMState *s,
+                                uint32_t *len);
+/*static*/ void onedram_read_fmt_wrapup(S5pc1xxOneDRAMState *s,
+                                        const uint16_t non_cmd);
+static int onedram_insert_socket(S5pc1xxOneDRAMState *s,
+                                 uint32_t psrc, uint16_t size);
+void onedram_socket_push(S5pc1xxOneDRAMState *s);
+int onedram_write_fmt(S5pc1xxOneDRAMState *s, const uint8_t *buf,
+                      uint32_t len);
+static uint32_t onedram_irq_cp_raise_32(S5pc1xxOneDRAMState *s);
+static uint32_t onedram_irq_cp_raise_16(S5pc1xxOneDRAMState *s);
+void onedram_disable_interrupt(S5pc1xxOneDRAMState *s);
+void onedram_enable_interrupt(S5pc1xxOneDRAMState *s);
+uint16_t onedram_interruptable(S5pc1xxOneDRAMState *s);
+static void onedram_irq_cp_lower(S5pc1xxOneDRAMState *s);
+static uint32_t onedram_irq_cp_pending(S5pc1xxOneDRAMState *s);
+static void onedram_irq_ap_raise(S5pc1xxOneDRAMState *s);
+static void onedram_irq_ap_lower(S5pc1xxOneDRAMState *s);
+static uint32_t onedram_irq_ap_pending(S5pc1xxOneDRAMState *s);
+unsigned int onedram_read_sem(S5pc1xxOneDRAMState *s);
+static void onedram_put_authority(S5pc1xxOneDRAMState *s);
+int onedram_try_get_authority(S5pc1xxOneDRAMState *s);
+void onedram_disable_write(S5pc1xxOneDRAMState *s);
+void onedram_enable_write(S5pc1xxOneDRAMState *s);
+uint16_t onedram_writable(S5pc1xxOneDRAMState *s);
+void onedram_send_cmd_to_pda(S5pc1xxOneDRAMState *s, uint16_t val);
+static void onedram_bootup(void *opaque);
+static void onedram_register_modem(S5pc1xxOneDRAMState *s,
+                                   ModemPlatformData *mp);
+static uint32_t onedram_io_readb(void *opaque,
+                                 target_phys_addr_t offset);
+static uint32_t onedram_io_readl(void *opaque,
+                                 target_phys_addr_t offset);
+static void onedram_io_writeb(void *opaque, target_phys_addr_t offset,
+                              uint32_t val);
+static void onedram_io_writel(void *opaque, target_phys_addr_t offset,
+                              uint32_t val);
+static int onedram_tcp_can_read(void *opaque);
+static void onedram_tcp_read(void *opaque, const uint8_t *buf,
+                             int size);
+void onedram_tcp_write(void *opaque, const uint8_t *buf, uint32_t size);
+static void onedram_tcp_event(void *opaque, int event);
+static void onedram_tcp_init(void *opaque);
+
+#endif
diff --git a/hw/s5pc1xx_onenand.c b/hw/s5pc1xx_onenand.c
new file mode 100644 (file)
index 0000000..d98ba57
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * S5PC1XX OneNAND controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "flash.h"
+#include "s5pc1xx.h"
+
+
+/* OneNAND Device Description:
+   EC - Samsung, 50 - device properties, 2E - version */
+#define ONENAND_DEVICE_ID  0xEC502E
+
+/* Device is mux */
+#define IS_MUX_TYPE 1
+
+#define ONENAND_CONTR_REG_BASE  0x00600000
+#define ONENAND_CONTR_REGS_SIZE 0x0000106C
+
+#define ONENAND_BUFFRES_SIZE    0x00012000
+
+#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18)
+#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17)
+#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16)
+
+
+typedef struct S5pc1xxOneNANDState {
+    SysBusDevice busdev;
+    target_phys_addr_t base;
+
+    /* OneNAND Interface Control register */
+    uint32_t onenand_if_clrt;
+    /* OneNAND Interface Async. Timing Control register */
+    uint32_t onenand_async_timing;
+    /* DMA Source Address Register */
+    uint32_t dma_src_addr;
+    /* DMA Source Configuration Register */
+    uint32_t dma_src_cfg;
+    /* DMA Destination Address Register */
+    uint32_t dma_dst_addr;
+    /* DMA Destination Configuration Register */
+    uint32_t dma_dst_cfg;
+    /* DMA Transfer Size Register */
+    uint32_t dma_trans_size;
+    /* DMA Transfer Direction Register */
+    uint32_t dma_trans_dir;
+    /* Sequencer Start Address Offset Register */
+    uint32_t sqc_sao;
+    /* Sequencer Register Control Register */
+    uint32_t sqc_reg_ctrl;
+    /* Sequencer Breakpoint Address Offset#0 Register */
+    uint32_t sqc_brpao0;
+    /* Sequencer Breakpoint Address Offset#1 Register */
+    uint32_t sqc_brpao1;
+    /* Interrupt Controller Sequencer Mask Register */
+    uint32_t intc_sqc_mask;
+    /* Interrupt Controller DMA Mask Register */
+    uint32_t intc_dma_mask;
+    /* Interrupt Controller OneNAND Mask Register */
+    uint32_t intc_onenand_mask;
+} S5pc1xxOneNANDState;
+
+
+static uint32_t s5pc1xx_onenand_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+    /* for compatibility with documentation */
+    int test_offset = offset + ONENAND_CONTR_REG_BASE;
+
+    switch (test_offset) {
+    case 0x00600100: /*RW OneNAND Interface Control register*/
+        return s->onenand_if_clrt;
+
+    case 0x00600400: /*RW DMA Source Address Register*/
+        return s->dma_src_addr;
+    case 0x00600404: /*RW DMA Source Configuration Register*/
+        return s->dma_src_cfg;
+    case 0x00600408: /*RW DMA Destination Address Register*/
+        return s->dma_dst_addr;
+    case 0x0060040C: /*RW DMA Destination Configuration Register*/
+        return s->dma_dst_cfg;
+    case 0x00600414: /*RW DMA Transfer Size Register*/
+        return s->dma_trans_size;
+    case 0x00600418: /*WO DMA Transfer Command Register*/
+        return 0;
+    case 0x0060041C: /*RO DMA Transfer Status Register*/
+        return S5PC110_DMA_TRANS_STATUS_TD;
+    case 0x00600420: /*RW DMA Transfer Direction Register*/
+        return s->dma_trans_dir;
+#if 0 /*TODO: implement support for all of these registers*/
+    case 0x00600104: /*WO OneNAND Interface Command register*/
+        return 0;
+    case 0x00600108: /*RW OneNAND Interface Async. Timing*/
+        return s->onenand_async_timing;
+    case 0x0060010C: /*RO OneNAND Interface Status Register*/
+        return 0x00FC0000;
+    case 0x00600600: /*RW Sequencer Start Address Offset Register*/
+        return s->sqc_sao;
+    case 0x00600608: /*WO Sequencer Command Register*/
+        return 0;
+    case 0x0060060C: /*RO Sequencer Status Register*/
+    case 0x00600610: /*RO Sequencer Current Address Offset*/
+    case 0x00600614: /*RW Sequencer Register Control Register*/
+        return s->sqc_reg_ctrl;
+    case 0x00600618: /*RO Sequencer Register Value Register*/
+    case 0x00600620: /*RW Sequencer Breakpoint Address Offset#0*/
+        return s->sqc_brpao0;
+    case 0x00600624: /*RW Sequencer Breakpoint Address Offset#1*/
+        return s->sqc_brpao1;
+    case 0x00601000: /*WO Interrupt Controller Sequencer Clear*/
+        return 0;
+    case 0x00601004: /*WO Interrupt Controller DMA Clear Register*/
+        return 0;
+    case 0x00601008: /*WO Interrupt Controller OneNAND Clear*/
+        return 0;
+    case 0x00601020: /*RW Interrupt Controller Sequencer Mask*/
+        return s->intc_sqc_mask;
+    case 0x00601024: /*RW Interrupt Controller DMA Mask Register*/
+        return s->intc_dma_mask;
+    case 0x00601028: /*RW Interrupt Controller OneNAND Mask*/
+        return s->intc_onenand_mask;
+    case 0x00601040: /*RO Interrupt Controller Sequencer Pending*/
+    case 0x00601044: /*RO Interrupt Controller DMA Pending*/
+    case 0x00601048: /*RO Interrupt Controller OneNAND Pending*/
+    case 0x00601060: /*RO Interrupt Controller Sequencer Status*/
+    case 0x00601064: /*RO Interrupt Controller DMA Status Register*/
+    case 0x00601068: /*RO Interrupt Controller OneNAND Status*/
+#endif
+    default:
+        hw_error("s5pc1xx.onenand: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static int is_inside_onenand(uint32_t offset, target_phys_addr_t base, uint32_t size)
+{
+    return offset >= base &&
+           offset < base + ONENAND_BUFFRES_SIZE &&
+           offset + size < base + ONENAND_BUFFRES_SIZE;
+}
+
+static void s5pc1xx_onenand_write(void *opaque, target_phys_addr_t offset,
+                                  uint32_t val)
+{
+    S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+    uint8_t *buf = NULL;
+    /* for compatibility with documentation */
+    int test_offset = offset + ONENAND_CONTR_REG_BASE;
+
+    switch (test_offset) {
+    case 0x00600100: /*RW OneNAND Interface Control register*/
+        s->onenand_if_clrt = val;
+        break;
+    case 0x00600400: /*RW DMA Source Address Register*/
+        s->dma_src_addr = val;
+        break;
+    case 0x00600404: /*RW DMA Source Configuration Register*/
+        s->dma_src_cfg = val;
+        break;
+    case 0x00600408: /*RW DMA Destination Address Register*/
+        s->dma_dst_addr = val;
+        break;
+    case 0x0060040C: /*RW DMA Destination Configuration Register*/
+        s->dma_dst_cfg = val;
+        break;
+    case 0x00600414: /*RW DMA Transfer Size Register*/
+        s->dma_trans_size = val;
+        break;
+    case 0x00600418: /*WO DMA Transfer Command Register*/
+        if (!is_inside_onenand(s->dma_src_addr, s->base, s->dma_trans_size) &&
+            !is_inside_onenand(s->dma_dst_addr, s->base, s->dma_trans_size)) {
+            hw_error("s5pc1xx.onenand: invalide memory transfer: src = "
+                     TARGET_FMT_plx ", dst = " TARGET_FMT_plx ", size = 0x%X\n",
+                     (target_phys_addr_t)s->dma_src_addr,
+                     (target_phys_addr_t)s->dma_dst_addr,
+                     s->dma_trans_size);
+        }
+
+        buf = qemu_malloc(s->dma_trans_size);
+        cpu_physical_memory_read (s->dma_src_addr, buf, s->dma_trans_size);
+        cpu_physical_memory_write(s->dma_dst_addr, buf, s->dma_trans_size);
+        qemu_free(buf);
+        break;
+    case 0x0060041C: /*RO DMA Transfer Status Register*/
+        break;
+    case 0x00600420: /*RW DMA Transfer Direction Register*/
+        s->dma_trans_dir = val;
+        break;
+#if 0 /*TODO: implement support for all of these registers*/
+    case 0x00600104: /*WO OneNAND Interface Command register*/
+        /* TODO see WR bits */
+        break;
+    case 0x00600108: /*RW OneNAND Interface Async. Timing*/
+        s->onenand_async_timing = val;
+        break;
+    case 0x0060010C: /*RO OneNAND Interface Status Register*/
+        break;
+    case 0x00600600: /*RW Sequencer Start Address Offset Register*/
+        s->sqc_sao = val;
+        break;
+    case 0x00600608: /*WO Sequencer Command Register*/
+        break;
+    case 0x0060060C: /*RO Sequencer Status Register*/
+        break;
+    case 0x00600610: /*RO Sequencer Current Address Offset*/
+        break;
+    case 0x00600614: /*RW Sequencer Register Control Register*/
+        s->sqc_reg_ctrl = val;
+        break;
+    case 0x00600618: /*RO Sequencer Register Value Register*/
+        break;
+    case 0x00600620: /*RW Sequencer Breakpoint Address Offset#0*/
+        s->sqc_brpao0 = val;
+        break;
+    case 0x00600624: /*RW Sequencer Breakpoint Address Offset#1*/
+        s->sqc_brpao1 = val;
+        break;
+    case 0x00601000: /*WO Interrupt Controller Sequencer Clear*/
+        break;
+    case 0x00601004: /*WO Interrupt Controller DMA Clear Register*/
+        break;
+    case 0x00601008: /*WO Interrupt Controller OneNAND Clear*/
+        break;
+    case 0x00601020: /*RW Interrupt Controller Sequencer Mask*/
+        s->intc_sqc_mask = val;
+        break;
+    case 0x00601024: /*RW Interrupt Controller DMA Mask Register*/
+        s->intc_dma_mask = val;
+        break;
+    case 0x00601028: /*RW Interrupt Controller OneNAND Mask*/
+        s->intc_onenand_mask = val;
+        break;
+    case 0x00601040: /*RO Interrupt Controller Sequencer Pending*/
+        break;
+    case 0x00601044: /*RO Interrupt Controller DMA Pending*/
+        break;
+    case 0x00601048: /*RO Interrupt Controller OneNAND Pending*/
+        break;
+    case 0x00601060: /*RO Interrupt Controller Sequencer Status*/
+        break;
+    case 0x00601064: /*RO Interrupt Controller DMA Status Register*/
+        break;
+    case 0x00601068: /*RO Interrupt Controller OneNAND Status*/
+        break;
+#endif
+    default:
+        hw_error("s5pc1xx.onenand: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const onenand_config_reg_mm_read[] = {
+    s5pc1xx_onenand_read,
+    s5pc1xx_onenand_read,
+    s5pc1xx_onenand_read
+};
+
+static CPUWriteMemoryFunc * const onenand_config_reg_mm_write[] = {
+    s5pc1xx_onenand_write,
+    s5pc1xx_onenand_write,
+    s5pc1xx_onenand_write
+};
+
+static void s5pc1xx_onenand_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+    qemu_put_be32s(f, &s->onenand_if_clrt);
+    qemu_put_be32s(f, &s->onenand_async_timing);
+    qemu_put_be32s(f, &s->dma_src_addr);
+    qemu_put_be32s(f, &s->dma_src_cfg);
+    qemu_put_be32s(f, &s->dma_dst_addr);
+    qemu_put_be32s(f, &s->dma_dst_cfg);
+    qemu_put_be32s(f, &s->dma_trans_size);
+    qemu_put_be32s(f, &s->dma_trans_dir);
+    qemu_put_be32s(f, &s->sqc_sao);
+    qemu_put_be32s(f, &s->sqc_reg_ctrl);
+    qemu_put_be32s(f, &s->sqc_brpao0);
+    qemu_put_be32s(f, &s->sqc_brpao1);
+    qemu_put_be32s(f, &s->intc_sqc_mask);
+    qemu_put_be32s(f, &s->intc_dma_mask);
+    qemu_put_be32s(f, &s->intc_onenand_mask);
+}
+
+static int s5pc1xx_onenand_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->onenand_if_clrt);
+    qemu_get_be32s(f, &s->onenand_async_timing);
+    qemu_get_be32s(f, &s->dma_src_addr);
+    qemu_get_be32s(f, &s->dma_src_cfg);
+    qemu_get_be32s(f, &s->dma_dst_addr);
+    qemu_get_be32s(f, &s->dma_dst_cfg);
+    qemu_get_be32s(f, &s->dma_trans_size);
+    qemu_get_be32s(f, &s->dma_trans_dir);
+    qemu_get_be32s(f, &s->sqc_sao);
+    qemu_get_be32s(f, &s->sqc_reg_ctrl);
+    qemu_get_be32s(f, &s->sqc_brpao0);
+    qemu_get_be32s(f, &s->sqc_brpao1);
+    qemu_get_be32s(f, &s->intc_sqc_mask);
+    qemu_get_be32s(f, &s->intc_dma_mask);
+    qemu_get_be32s(f, &s->intc_onenand_mask);
+
+    return 0;
+}
+
+static void onenand_config_reg_reset(void *opaque)
+{
+    S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+    s->onenand_if_clrt      = 0x00004000 | (IS_MUX_TYPE << 31);
+    s->onenand_async_timing = 0x00003415;
+    s->dma_src_addr         = 0x00000000;
+    s->dma_src_cfg          = 0x00040002;
+    s->dma_dst_addr         = 0x00000000;
+    s->dma_dst_cfg          = 0x00040002;
+    s->dma_trans_size       = 0x00000000;
+    s->dma_trans_dir        = 0x00000000;
+    s->sqc_sao              = 0x00000000;
+    s->sqc_reg_ctrl         = 0x00000000;
+    s->sqc_brpao0           = 0x00000000;
+    s->sqc_brpao1           = 0x00000000;
+    s->intc_sqc_mask        = 0x01010000;
+    s->intc_dma_mask        = 0x01010000;
+    s->intc_onenand_mask    = 0x000000FF;
+}
+
+DeviceState *s5pc1xx_onenand_init(target_phys_addr_t base)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    void *onenand_dev;
+
+    dev = qdev_create(NULL, "s5pc1xx.onenand");
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+
+    sysbus_mmio_map(s, 0, base + ONENAND_CONTR_REG_BASE);
+
+    FROM_SYSBUS(S5pc1xxOneNANDState, s)->base = base;
+
+    onenand_dev =
+        onenand_init(ONENAND_DEVICE_ID, 1, NULL, ONENAND_4KB_PAGE, 1);
+    onenand_base_update(onenand_dev, base);
+
+    return dev;
+}
+
+static int s5pc1xx_onenand_init1(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxOneNANDState *s = FROM_SYSBUS(S5pc1xxOneNANDState, dev);
+
+    iomemtype = cpu_register_io_memory(onenand_config_reg_mm_read,
+                                       onenand_config_reg_mm_write, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, ONENAND_CONTR_REGS_SIZE, iomemtype);
+
+    onenand_config_reg_reset(s);
+
+    qemu_register_reset(onenand_config_reg_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.onenand", -1, 1,
+                    s5pc1xx_onenand_save, s5pc1xx_onenand_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_onenand_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.onenand", sizeof(S5pc1xxOneNANDState),
+                        s5pc1xx_onenand_init1);
+}
+
+device_init(s5pc1xx_onenand_register_devices)
diff --git a/hw/s5pc1xx_pcm.c b/hw/s5pc1xx_pcm.c
new file mode 100644 (file)
index 0000000..1710859
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * PCM audio interface for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+
+#define ALL_BITS(b,a)               (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the PCM Main Control 0x00000000 */
+#define PCM_CTL             0x00
+    #define TXFIFO_DIPSTICK_SHIFT   13      /* 6 bits */
+    #define RXFIFO_DIPSTICK_SHIFT   7       /* 6 bits */
+    #define PCM_TX_DMA_EN           (1 << 6)
+    #define PCM_RX_DMA_EN           (1 << 5)
+    #define TX_MSB_POS              (1 << 4)
+    #define RX_MSB_POS              (1 << 3)
+    #define PCM_TXFIFO_EN           (1 << 2)
+    #define PCM_RXFIFO_EN           (1 << 1)
+    #define PCM_PCM_ENABLE          (1 << 0)
+
+/* R/W Specifies the PCM Clock and Shift control 0x00000000 */
+#define PCM_CLKCTL          0x04
+    #define CTL_SERCLK_EN           (1 << 19)
+    #define CTL_SERCLK_SEL          (1 << 18)   /* TODO: now is set high as default */
+    #define SCLK_DIV_SHIFT          9
+    #define SYNC_DIV_SHIFT          0
+
+/* R/W Specifies the PCM TxFIFO write port 0x00010000 */
+#define PCM_TXFIFO          0x08
+    #define TXFIFO_DVALID           (1 << 16)
+
+/* R/W Specifies the PCM RxFIFO read port 0x00010000 */
+#define PCM_RXFIFO          0x0C
+    #define RXFIFO_DVALID           (1 << 16)
+
+/* R/W Specifies the PCM Interrupt Control 0x00000000 */
+#define PCM_IRQ_CTL         0x10
+    #define INT_EN(stat)            (stat)
+    #define EN_IRQ_TO_ARM           (1 << 14)
+
+/* R Specifies the PCM Interrupt Status 0x00000000 */
+#define PCM_IRQ_STAT        0x14
+    #define IRQ_PENDING             (1 << 13)
+    #define TRANSFER_DONE           (1 << 12)
+    #define TXFIFO_EMPTY            (1 << 11)
+    #define TXFIFO_ALMOST_EMPTY     (1 << 10)
+    #define TXFIFO_FULL             (1 << 9)
+    #define TXFIFO_ALMOST_FULL      (1 << 8)
+    #define TXFIFO_ERROR_STARVE     (1 << 7)
+    #define TXFIFO_ERROR_OVERFLOW   (1 << 6)
+    #define RXFIFO_EMPTY            (1 << 5)
+    #define RXFIFO_ALMOST_EMPTY     (1 << 4)
+    #define RXFIFO_FULL             (1 << 3)
+    #define RXFIFO_ALMOST_FULL      (1 << 2)
+    #define RXFIFO_ERROR_STARVE     (1 << 1)
+    #define RXFIFO_ERROR_OVERFLOW   (1 << 0)
+    #define ALL_STAT                ALL_BITS(13,0)
+
+/* R Specifies the PCM FIFO Status 0x00000000 */
+#define PCM_FIFO_STAT       0x18
+    #define TXFIFO_COUNT_SHIFT       14     /* 6 bits */
+    #define TXFIFO_EMPTY_FLAG        (1 << 13)
+    #define TXFIFO_ALMOST_EMPTY_FLAG (1 << 12)
+    #define TXFIFO_FULL_FLAG         (1 << 11)
+    #define TXFIFO_ALMOST_FULL_FLAG  (1 << 10)
+    #define RXFIFO_COUNT_SHIFT       4      /* 6 bits */
+    #define RXFIFO_EMPTY_FLAG        (1 << 3)
+    #define RXFIFO_ALMOST_EMPTY_FLAG (1 << 2)
+    #define RXFIFO_FULL_FLAG         (1 << 1)
+    #define RXFIFO_ALMOST_FULL_FLAG  (1 << 0)
+
+/* W Specifies the PCM Interrupt Clear - */
+#define PCM_CLRINT          0x20
+
+#define S5PC1XX_PCM_REG_MEM_SIZE    0x24
+
+
+typedef struct S5pc1xxPCMState {
+
+    SysBusDevice busdev;
+    qemu_irq     irq;
+    uint32_t     instance;
+
+    uint32_t ctl;
+    uint32_t clk_ctl;
+    uint32_t irq_ctl;
+    uint32_t irq_stat;
+    uint32_t fifo_stat;
+
+    struct FrameIn {
+        uint16_t fifo[32];
+
+        uint64_t write_idx;
+        uint64_t read_idx;
+
+        uint8_t fifo_dipstick;
+        uint8_t delay;
+   } rx;
+
+    struct FrameOut {
+        uint16_t fifo[32];
+
+        uint64_t write_idx;
+        uint64_t read_idx;
+
+        uint8_t fifo_dipstick;
+        uint8_t delay;
+    } tx;
+
+    QEMUTimer *pcm_timer;
+    uint16_t  sync_div;
+    uint32_t  sync_freq;
+    uint32_t  sclk_freq;
+    uint64_t  last_pcm_time;
+
+    uint8_t   sclk_en;
+    uint8_t   pcm_io_en;
+
+} S5pc1xxPCMState;
+
+
+/* Function for initialization and reset */
+static void s5pc1xx_pcm_reset(DeviceState *d)
+{
+    S5pc1xxPCMState *s =
+        FROM_SYSBUS(S5pc1xxPCMState, sysbus_from_qdev(d));
+
+    s->ctl           = 0;
+    s->clk_ctl       = 0x40000;
+    s->irq_ctl       = 0;
+    s->irq_stat      = 0;
+    s->fifo_stat     = 0;
+
+    s->rx.read_idx   = 0;
+    s->rx.write_idx  = 0;
+    s->rx.delay      = 0;
+
+    s->tx.read_idx   = 0;
+    s->tx.write_idx  = 0;
+    s->tx.delay      = 0;
+
+    s->sync_div      = 0;
+    s->sync_freq     = 0;
+    s->sclk_freq     = 0;
+    s->last_pcm_time = 0;
+
+    s->sclk_en       = 0;
+    s->pcm_io_en     = 0;
+}
+
+/* Interrupts handler */
+static void s5pc1xx_pcm_irq(S5pc1xxPCMState *s,
+                            uint32_t stat, uint8_t clear)
+{
+    if (stat) {
+        s->irq_stat |= (IRQ_PENDING | stat);
+        if ((s->irq_ctl & EN_IRQ_TO_ARM) &&
+            (s->irq_ctl & INT_EN(stat)))    /* if enabled */
+                qemu_irq_raise(s->irq);
+    }
+    if (clear) {
+        s->irq_stat &= ~(ALL_STAT); /* clear all at once */
+        qemu_irq_lower(s->irq);
+    }
+}
+
+/* Controls RXFIFO stage */
+static void s5pc1xx_pcm_rx_control(S5pc1xxPCMState *s)
+{
+    uint8_t rx_depth;
+
+    rx_depth = (s->rx.write_idx - s->rx.read_idx) % 33;
+
+    if (rx_depth == 0) {
+        s5pc1xx_pcm_irq(s, RXFIFO_EMPTY, 0);
+        s->fifo_stat |= RXFIFO_EMPTY_FLAG;
+    } else {
+        s->fifo_stat &= ~(RXFIFO_EMPTY_FLAG);
+    }
+
+    if (rx_depth == 32) {
+        s5pc1xx_pcm_irq(s, RXFIFO_FULL, 0);
+        s->fifo_stat |= RXFIFO_FULL_FLAG;
+    } else {
+        s->fifo_stat &= ~(RXFIFO_FULL_FLAG);
+    }
+
+    if (rx_depth < s->rx.fifo_dipstick) {
+        s5pc1xx_pcm_irq(s, RXFIFO_ALMOST_EMPTY, 0);
+        s->fifo_stat |= RXFIFO_ALMOST_EMPTY_FLAG;
+    } else {
+        s->fifo_stat &= ~(RXFIFO_ALMOST_EMPTY_FLAG);
+    }
+
+    if (rx_depth > (32 - s->rx.fifo_dipstick)) {
+        s5pc1xx_pcm_irq(s, RXFIFO_ALMOST_FULL, 0);
+        s->fifo_stat |= RXFIFO_ALMOST_FULL_FLAG;
+    } else {
+        s->fifo_stat &= ~(RXFIFO_ALMOST_FULL_FLAG);
+    }
+}
+
+/* Controls TXFIFO stage */
+static void s5pc1xx_pcm_tx_control(S5pc1xxPCMState *s)
+{
+    uint8_t tx_depth;
+
+    tx_depth = (s->tx.write_idx - s->tx.read_idx) % 33;
+
+    if (tx_depth ==  0) {
+        s5pc1xx_pcm_irq(s, TXFIFO_EMPTY, 0);
+        s->fifo_stat |= TXFIFO_EMPTY_FLAG;
+    } else {
+        s->fifo_stat &= ~(TXFIFO_EMPTY_FLAG);
+    }
+
+    if (tx_depth == 32) {
+        s5pc1xx_pcm_irq(s, TXFIFO_FULL, 0);
+        s->fifo_stat |= TXFIFO_FULL_FLAG;
+    } else {
+        s->fifo_stat &= ~(TXFIFO_FULL_FLAG);
+    }
+
+    if (tx_depth < s->tx.fifo_dipstick) {
+        s5pc1xx_pcm_irq(s, TXFIFO_ALMOST_EMPTY, 0);
+        s->fifo_stat |= TXFIFO_ALMOST_EMPTY_FLAG;
+    } else {
+        s->fifo_stat &= ~(TXFIFO_ALMOST_EMPTY_FLAG);
+    }
+
+    if (tx_depth > (32 - s->tx.fifo_dipstick)) {
+        s5pc1xx_pcm_irq(s, TXFIFO_ALMOST_FULL, 0);
+        s->fifo_stat |= TXFIFO_ALMOST_FULL_FLAG;
+    } else {
+        s->fifo_stat &= ~(TXFIFO_ALMOST_FULL_FLAG);
+    }
+}
+
+/* Increase fifo indexes */
+static void s5pc1xx_pcm_next_frame(S5pc1xxPCMState *s)
+{
+    if (s->ctl & PCM_RXFIFO_EN) {
+        if (s->rx.write_idx < s->rx.read_idx + 32)
+            s->rx.write_idx++;
+        s5pc1xx_pcm_rx_control(s);
+    }
+
+    if (s->ctl & PCM_TXFIFO_EN) {
+        if (s->tx.read_idx < s->tx.write_idx)
+            s->tx.read_idx++;
+        s5pc1xx_pcm_tx_control(s);
+    }
+}
+
+/* Determine sclk/sync_freq and sync_div */
+static void s5pc1xx_pcm_sclk_update(S5pc1xxPCMState *s)
+{
+    S5pc1xxClk clk;
+    uint16_t sclk_div;
+
+    clk = s5pc1xx_findclk("pclk_66");
+
+    if (!(s->pcm_io_en))
+        s->clk_ctl = 0x40000;
+
+    sclk_div = 2 * ((s->clk_ctl >> SCLK_DIV_SHIFT & ALL_BITS(8, 0)) + 1);
+    s->sclk_freq = s5pc1xx_clk_getrate(clk) / sclk_div;
+
+    if (!(s->sclk_freq))
+        hw_error("s5pc1xx.pcm: timer frequency is zero\n");
+
+    s->sync_div = (s->clk_ctl >> SYNC_DIV_SHIFT & ALL_BITS(8, 0)) + 1;
+    s->sync_freq = s->sclk_freq / s->sync_div;
+}
+
+/* Sync timer */
+static void s5pc1xx_pcm_sync(void *opaque)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+    uint64_t next_pcm_time;
+
+    if (s->sclk_en) {
+        if (!(s->sync_freq))
+            s5pc1xx_pcm_sclk_update(s);
+
+        s5pc1xx_pcm_next_frame(s);
+
+        s->last_pcm_time = qemu_get_clock(vm_clock);
+        next_pcm_time =
+            s->last_pcm_time + muldiv64(1, get_ticks_per_sec(), s->sync_freq);
+        qemu_mod_timer(s->pcm_timer, next_pcm_time);
+    } else {
+        qemu_del_timer(s->pcm_timer);
+    }
+}
+
+/* SCLK x2 cycles counter */
+static uint16_t s5pc1xx_pcm_2sclk(S5pc1xxPCMState *s)
+{
+    uint32_t spent_1G;
+    uint16_t spent_2sclk;
+
+    if (s->last_pcm_time) {
+        spent_1G = qemu_get_clock(vm_clock) - s->last_pcm_time;
+        spent_2sclk =
+            muldiv64(spent_1G, (s->sclk_freq * 2), get_ticks_per_sec());
+        return spent_2sclk;
+    } else {
+        return 0;
+    }
+}
+
+/* SCLK state */
+static uint8_t s5pc1xx_pcm_sclk_s(S5pc1xxPCMState *s)
+{
+    if (s->sclk_en)
+        return ((s5pc1xx_pcm_2sclk(s) % 2) ? 0 : 1);
+    else
+        return 0;
+}
+
+/* SYNC signal state */
+static uint8_t s5pc1xx_pcm_sync_s(S5pc1xxPCMState *s)
+{
+    if (s->sclk_en)
+        return (s5pc1xx_pcm_2sclk(s) < 2);
+    else
+        return 0;
+}
+
+/* Put PCM_SIN bit */
+static void  s5pc1xx_write_rxfifo(S5pc1xxPCMState *s, uint32_t sdata)
+{
+    uint16_t cur_pos;
+
+    if (!(s->sclk_en) || !(s->ctl & PCM_RXFIFO_EN))
+        return;
+
+    if (s->rx.write_idx == s->rx.read_idx + 32) {
+        s5pc1xx_pcm_irq(s, RXFIFO_ERROR_OVERFLOW, 0);
+        return;
+    }
+
+    cur_pos = (s5pc1xx_pcm_2sclk(s) / 2) % s->sync_div - s->rx.delay;
+
+    if (cur_pos < 16)
+        s->rx.fifo[s->rx.write_idx % 32] |= (sdata & 0x1) << (15 - cur_pos);
+}
+
+/* Return PCM_SOUT bit */
+static uint8_t s5pc1xx_read_txfifo(S5pc1xxPCMState *s)
+{
+    uint16_t word, cur_pos;
+
+    if (!(s->sclk_en) || !(s->ctl & PCM_TXFIFO_EN))
+        return 0;
+
+    if (s->tx.read_idx == s->tx.write_idx) {
+        s5pc1xx_pcm_irq(s, TXFIFO_ERROR_STARVE, 0);
+        return 0;
+    }
+
+    cur_pos = (s5pc1xx_pcm_2sclk(s) / 2) % s->sync_div - s->tx.delay;
+
+    if (cur_pos > 15) {
+        s5pc1xx_pcm_irq(s, TRANSFER_DONE, 0);
+        return 0;
+    }
+
+    word = s->tx.fifo[s->tx.read_idx % 32];
+    return (word >> (15 - cur_pos) & 0x1);
+}
+
+/* Read PCM by GPIO */
+static uint32_t s5pc1xx_pcm_gpio_read(void *opaque, int io_index)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+    if (!(s->pcm_io_en))
+        return 0;
+
+    if (io_index == PCM_SCLK(s->instance))
+        return s5pc1xx_pcm_sclk_s(s);
+
+    if (io_index == PCM_FSYNC(s->instance))
+        return s5pc1xx_pcm_sync_s(s);
+
+    if (io_index == PCM_SOUT(s->instance))
+        return s5pc1xx_read_txfifo(s);
+
+    return 0;
+}
+
+/* Write PCM by GPIO */
+static void s5pc1xx_pcm_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+    /* TODO: not implemented for now */
+    if (io_index == PCM_EXTCLK(s->instance))
+        return;
+
+    if (io_index == PCM_SIN(s->instance)) {
+        s5pc1xx_write_rxfifo(s, value);
+    }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_pcm_gpio_readfn   = s5pc1xx_pcm_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_pcm_gpio_writefn = s5pc1xx_pcm_gpio_write;
+
+/* Read PCM by OS */
+static uint32_t s5pc1xx_pcm_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+    uint8_t rx_depth, tx_depth;
+    uint32_t ret_val;
+
+    switch(offset) {
+    case PCM_CTL:
+        return s->ctl;
+    case PCM_CLKCTL:
+        return s->clk_ctl;
+
+    case PCM_TXFIFO:
+        if (!(s->ctl & PCM_TX_DMA_EN) || !(s->ctl & PCM_TXFIFO_EN))
+            return 0;
+
+        return (s->tx.fifo[s->tx.read_idx % 32] | TXFIFO_DVALID);
+
+    case PCM_RXFIFO:
+        if (!(s->ctl & PCM_RX_DMA_EN) || !(s->ctl & PCM_RXFIFO_EN))
+            return 0;
+
+        if (s->rx.read_idx ==  s->rx.write_idx) {
+            s5pc1xx_pcm_irq(s, RXFIFO_ERROR_STARVE, 0);
+            return 0;
+        }
+
+        ret_val = s->rx.fifo[s->rx.read_idx % 32] | RXFIFO_DVALID;
+
+        if (s->rx.read_idx < s->rx.write_idx)
+            s->rx.read_idx++;
+
+        s5pc1xx_pcm_rx_control(s);
+
+        return ret_val;
+
+    case PCM_IRQ_CTL:
+        return s->irq_ctl;
+    case PCM_IRQ_STAT:
+        return s->irq_stat;
+
+    case PCM_FIFO_STAT:
+        rx_depth = (s->rx.write_idx - s->rx.read_idx) % 33;
+        tx_depth = (s->tx.write_idx - s->tx.read_idx) % 33;
+        ret_val  = (s->fifo_stat & ALL_BITS(3, 0)) | (rx_depth << 4) |
+                   (s->fifo_stat & ALL_BITS(13, 10)) | (tx_depth << 14);
+        return ret_val;
+
+    default:
+        hw_error("s5pc1xx.pcm: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+/* Write PCM by OS */
+static void s5pc1xx_pcm_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+    switch(offset) {
+    case PCM_CTL:
+        if ((value & PCM_PCM_ENABLE) < (s->ctl & PCM_PCM_ENABLE)) {
+            s->pcm_io_en = 0;
+            s5pc1xx_pcm_sclk_update(s);
+        }
+        /* the value is set low above */
+        s->pcm_io_en = (!(value & PCM_PCM_ENABLE)) ? : 1;
+
+        if ((value & PCM_RXFIFO_EN) < (s->ctl & PCM_RXFIFO_EN)) {
+            s->rx.read_idx  = 0;
+            s->rx.write_idx = 0;
+        }
+        if ((value & PCM_TXFIFO_EN) < (s->ctl & PCM_TXFIFO_EN)) {
+            s->tx.read_idx  = 0;
+            s->tx.write_idx = 0;
+        }
+
+        s->rx.delay         = (value & RX_MSB_POS) ? 1 : 0;
+        s->rx.fifo_dipstick = value >> RXFIFO_DIPSTICK_SHIFT & ALL_BITS(6, 0);
+
+        s->tx.delay         = (value & TX_MSB_POS) ? 1 : 0;
+        s->tx.fifo_dipstick = value >> TXFIFO_DIPSTICK_SHIFT & ALL_BITS(6, 0);
+
+        s->ctl = value;
+        break;
+
+    case PCM_CLKCTL:
+        if ((value & CTL_SERCLK_EN) > (s->clk_ctl & CTL_SERCLK_EN)) {
+            s->sclk_en = 1;
+            s5pc1xx_pcm_sync(s);
+        }
+        /* the value is set high above */
+        s->sclk_en = (value & CTL_SERCLK_EN) ? : 0;
+
+        if (value != s->clk_ctl)
+            s5pc1xx_pcm_sclk_update(s);
+
+        s->clk_ctl = value;
+        break;
+
+    case PCM_TXFIFO:
+        if (!(s->ctl & PCM_TX_DMA_EN) || !(s->ctl & PCM_TXFIFO_EN))
+            break;
+
+        if (s->tx.write_idx == s->tx.read_idx + 32) {
+            s5pc1xx_pcm_irq(s, TXFIFO_ERROR_OVERFLOW, 0);
+            break;
+        }
+
+        s->tx.fifo[s->tx.write_idx % 32] = value;
+
+        if (s->tx.write_idx <  s->tx.read_idx + 32)
+            s->tx.write_idx++;
+
+        s5pc1xx_pcm_tx_control(s);
+
+        break;
+
+    case PCM_RXFIFO:
+        if (!(s->ctl & PCM_RX_DMA_EN) || !(s->ctl & PCM_RXFIFO_EN))
+            break;
+
+        s->rx.fifo[s->rx.write_idx % 32] = value;
+        break;
+
+    case PCM_IRQ_CTL:
+        s->irq_ctl = value;
+        break;
+
+    case PCM_CLRINT:
+        /* clear all irq stats and lower the interrupt */
+        s5pc1xx_pcm_irq(s, 0, 1);
+        break;
+
+    default:
+        hw_error("s5pc1xx.pcm: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pcm_readfn[] = {
+   s5pc1xx_pcm_read,
+   s5pc1xx_pcm_read,
+   s5pc1xx_pcm_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pcm_writefn[] = {
+   s5pc1xx_pcm_write,
+   s5pc1xx_pcm_write,
+   s5pc1xx_pcm_write
+};
+
+static void s5pc1xx_pcm_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+    uint64_t last;
+    int i;
+
+    qemu_put_be32s(f, &s->ctl);
+    qemu_put_be32s(f, &s->clk_ctl);
+    qemu_put_be32s(f, &s->irq_ctl);
+    qemu_put_be32s(f, &s->irq_stat);
+    qemu_put_be32s(f, &s->fifo_stat);
+
+    for (i = 0; i < 32; i++) {
+        qemu_put_be16s(f, &s->rx.fifo[i]);
+    }
+
+    qemu_put_be64s(f, &s->rx.write_idx);
+    qemu_put_be64s(f, &s->rx.read_idx);
+    qemu_put_8s   (f, &s->rx.fifo_dipstick);
+    qemu_put_8s   (f, &s->rx.delay);
+
+    for (i = 0; i < 32; i++) {
+        qemu_put_be16s(f, &s->tx.fifo[i]);
+    }
+
+    qemu_put_be64s(f, &s->tx.write_idx);
+    qemu_put_be64s(f, &s->tx.read_idx);
+    qemu_put_8s   (f, &s->tx.fifo_dipstick);
+    qemu_put_8s   (f, &s->tx.delay);
+
+    qemu_put_be16s(f, &s->sync_div);
+    qemu_put_be32s(f, &s->sync_freq);
+    qemu_put_be32s(f, &s->sclk_freq);
+
+    last = qemu_get_clock(vm_clock) - s->last_pcm_time;
+    qemu_put_be64s(f, &last);
+
+    qemu_put_8s(f, &s->sclk_en);
+    qemu_put_8s(f, &s->pcm_io_en);
+
+    qemu_put_timer(f, s->pcm_timer);
+}
+
+static int s5pc1xx_pcm_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+    uint64_t last;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->ctl);
+    qemu_get_be32s(f, &s->clk_ctl);
+    qemu_get_be32s(f, &s->irq_ctl);
+    qemu_get_be32s(f, &s->irq_stat);
+    qemu_get_be32s(f, &s->fifo_stat);
+
+    for (i = 0; i < 32; i++) {
+        qemu_get_be16s(f, &s->rx.fifo[i]);
+    }
+
+    qemu_get_be64s(f, &s->rx.write_idx);
+    qemu_get_be64s(f, &s->rx.read_idx);
+    qemu_get_8s   (f, &s->rx.fifo_dipstick);
+    qemu_get_8s   (f, &s->rx.delay);
+
+    for (i = 0; i < 32; i++) {
+        qemu_get_be16s(f, &s->tx.fifo[i]);
+    }
+
+    qemu_get_be64s(f, &s->tx.write_idx);
+    qemu_get_be64s(f, &s->tx.read_idx);
+    qemu_get_8s   (f, &s->tx.fifo_dipstick);
+    qemu_get_8s   (f, &s->tx.delay);
+
+    qemu_get_be16s(f, &s->sync_div);
+    qemu_get_be32s(f, &s->sync_freq);
+    qemu_get_be32s(f, &s->sclk_freq);
+
+    qemu_get_be64s(f, &last);
+    s->last_pcm_time = qemu_get_clock(vm_clock) - last;
+
+    qemu_get_8s(f, &s->sclk_en);
+    qemu_get_8s(f, &s->pcm_io_en);
+
+    qemu_get_timer(f, s->pcm_timer);
+
+    return 0;
+}
+
+DeviceState *s5pc1xx_pcm_init(target_phys_addr_t base, int instance,
+                              qemu_irq irq)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.pcm");
+    qdev_prop_set_uint32(dev, "instance", instance);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
+
+/* PCM initialization */
+static int s5pc1xx_pcm_init1(SysBusDevice *dev)
+{
+    S5pc1xxPCMState *s = FROM_SYSBUS(S5pc1xxPCMState, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_pcm_readfn, s5pc1xx_pcm_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_PCM_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_PCM, s->instance,
+                                    s5pc1xx_pcm_gpio_readfn,
+                                    s5pc1xx_pcm_gpio_writefn, NULL, s);
+    s->pcm_timer = qemu_new_timer(vm_clock, s5pc1xx_pcm_sync, s);
+
+    s5pc1xx_pcm_reset(&s->busdev.qdev);
+
+    register_savevm(&dev->qdev, "s5pc1xx.pcm", s->instance, 1,
+                    s5pc1xx_pcm_save, s5pc1xx_pcm_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_pcm_info = {
+    .init       = s5pc1xx_pcm_init1,
+    .qdev.name  = "s5pc1xx.pcm",
+    .qdev.size  = sizeof(S5pc1xxPCMState),
+    .qdev.reset = s5pc1xx_pcm_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("instance", S5pc1xxPCMState, instance, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_pcm_register(void)
+{
+    sysbus_register_withprop(&s5pc1xx_pcm_info);
+}
+
+device_init(s5pc1xx_pcm_register)
diff --git a/hw/s5pc1xx_pmu.c b/hw/s5pc1xx_pmu.c
new file mode 100644 (file)
index 0000000..d6428fe
--- /dev/null
@@ -0,0 +1,1295 @@
+/*
+ * S5PC1XX Power Management Unit controller,
+ * Maxim MAX17040 Fuel Gauge,
+ * Maxim MAX8998 Battery Charger and Real Time Clock.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "smbus.h"
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+
+
+#define S5PC110_PMU_SFR_SIZE 0x8000
+
+
+typedef struct S5pc1xxPMUState {
+    SysBusDevice busdev;
+
+    uint32_t osc_con;
+    uint32_t rst_stat;
+    uint32_t pwr_cfg;
+    uint32_t eint_wakeup_mask;
+    uint32_t wakeup_mask;
+    uint32_t pwr_mode;
+    uint32_t normal_cfg;
+    uint32_t idle_cfg;
+    uint32_t stop_cfg;
+    uint32_t stop_mem_cfg;
+    uint32_t sleep_cfg;
+    uint32_t osc_freq;
+    uint32_t osc_stable;
+    uint32_t pwr_stable;
+    uint32_t mtc_stable;
+    uint32_t clamp_stable;
+    uint32_t wakeup_stat;
+    uint32_t blk_pwr_stat;
+    uint32_t body_bias_con;
+    uint32_t ion_skew_con;
+    uint32_t ion_skew_mon;
+    uint32_t ioff_skew_con;
+    uint32_t ioff_skew_mon;
+    uint32_t others;
+    uint32_t om_stat;
+    uint32_t mie_control;
+    uint32_t hdmi_control;
+    uint32_t usb_phy_control;
+    uint32_t dac_control;
+    uint32_t mipi_dphy_control;
+    uint32_t adc_control;
+    uint32_t ps_hold_control;
+    uint32_t inform0;
+    uint32_t inform1;
+    uint32_t inform2;
+    uint32_t inform3;
+    uint32_t inform4;
+    uint32_t inform5;
+    uint32_t inform6;
+    uint32_t inform7;
+} S5pc1xxPMUState;
+
+
+static uint32_t s5pc1xx_pmu_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+    target_phys_addr_t test_offset = offset + 0x8000;
+
+    if (offset & 3) {
+        hw_error("s5pc1xx.pmu: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    switch (test_offset) {
+    case 0x8000:
+        return s->osc_con;
+    case 0xA000:
+        return s->rst_stat;
+    case 0xC000:
+        return s->pwr_cfg;
+    case 0xC004:
+        return s->eint_wakeup_mask;
+    case 0xC008:
+        return s->wakeup_mask;
+    case 0xC00C:
+        return s->pwr_mode;
+    case 0xC010:
+        return s->normal_cfg;
+    case 0xC020:
+        return s->idle_cfg;
+    case 0xC030:
+        return s->stop_cfg;
+    case 0xC034:
+        return s->stop_mem_cfg;
+    case 0xC040:
+        return s->sleep_cfg;
+    case 0xC100:
+        return s->osc_freq;
+    case 0xC104:
+        return s->osc_stable;
+    case 0xC108:
+        return s->pwr_stable;
+    case 0xC110:
+        return s->mtc_stable;
+    case 0xC114:
+        return s->clamp_stable;
+    case 0xC200:
+        return s->wakeup_stat;
+    case 0xC204:
+        return s->blk_pwr_stat;
+    case 0xC300:
+        return s->body_bias_con;
+    case 0xC310:
+        return s->ion_skew_con;
+    case 0xC314:
+        return s->ion_skew_mon;
+    case 0xC320:
+        return s->ioff_skew_con;
+    case 0xC324:
+        return s->ioff_skew_mon;
+    case 0xE000:
+        return s->others;
+    case 0xE100:
+        return s->om_stat;
+    case 0xE800:
+        return s->mie_control;
+    case 0xE804:
+        return s->hdmi_control;
+    case 0xE80C:
+        return s->usb_phy_control;
+    case 0xE810:
+        return s->dac_control;
+    case 0xE814:
+        return s->mipi_dphy_control;
+    case 0xE818:
+        return s->adc_control;
+    case 0xE81C:
+        return s->ps_hold_control;
+    case 0xF000:
+        return s->inform0;
+    case 0xF004:
+        return s->inform1;
+    case 0xF008:
+        return s->inform2;
+    case 0xF00C:
+        return s->inform3;
+    case 0xF010:
+        return s->inform4;
+    case 0xF014:
+        return s->inform5;
+    case 0xF018:
+        return s->inform6;
+    case 0xF01C:
+        return s->inform7;
+    default:
+        hw_error("s5pc1xx.pmu: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static void s5pc1xx_pmu_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t val)
+{
+    S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+    target_phys_addr_t test_offset = offset + 0x8000;
+
+    /* TODO: Check if writing any values should change emulation flow */
+
+    if (offset & 3) {
+        hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    switch (test_offset) {
+    case 0x8000:
+        s->osc_con = val;
+        break;
+    case 0xC000:
+        s->pwr_cfg = val;
+        break;
+    case 0xC004:
+        s->eint_wakeup_mask = val;
+        break;
+    case 0xC008:
+        s->wakeup_mask = val;
+        break;
+    case 0xC00C:
+        s->pwr_mode = val;
+        break;
+    case 0xC010:
+        s->normal_cfg = val;
+        break;
+    case 0xC020:
+        s->idle_cfg = val;
+        break;
+    case 0xC030:
+        s->stop_cfg = val;
+        break;
+    case 0xC034:
+        s->stop_mem_cfg = val;
+        break;
+    case 0xC040:
+        s->sleep_cfg = val;
+        break;
+    case 0xC100:
+        s->osc_freq = val;
+        break;
+    case 0xC104:
+        s->osc_stable = val;
+        break;
+    case 0xC108:
+        s->pwr_stable = val;
+        break;
+    case 0xC110:
+        s->mtc_stable = val;
+        break;
+    case 0xC114:
+        s->clamp_stable = val;
+        break;
+    case 0xC200:
+        s->wakeup_stat = val;
+        break;
+    case 0xC300:
+        s->body_bias_con = val;
+        break;
+    case 0xC310:
+        s->ion_skew_con = val;
+        break;
+    case 0xC320:
+        s->ioff_skew_con = val;
+        break;
+    case 0xE000:
+        s->others = val;
+        break;
+    case 0xE800:
+        s->mie_control = val;
+        break;
+    case 0xE804:
+        s->hdmi_control = val;
+        break;
+    case 0xE80C:
+        s->usb_phy_control = val;
+        break;
+    case 0xE810:
+        s->dac_control = val;
+        break;
+    case 0xE814:
+        s->mipi_dphy_control = val;
+        break;
+    case 0xE818:
+        s->adc_control = val;
+        break;
+    case 0xE81C:
+        s->ps_hold_control = val;
+        if ((val & (1 << 8)) == 0) {
+            qemu_system_shutdown_request();
+        }
+        break;
+    case 0xF000:
+        s->inform0 = val;
+        break;
+    case 0xF004:
+        s->inform1 = val;
+        break;
+    case 0xF008:
+        s->inform2 = val;
+        break;
+    case 0xF00C:
+        s->inform3 = val;
+        break;
+    case 0xF010:
+        s->inform4 = val;
+        break;
+    case 0xF014:
+        s->inform5 = val;
+        break;
+    case 0xF018:
+        s->inform6 = val;
+        break;
+    case 0xF01C:
+        s->inform7 = val;
+        break;
+    case 0xA000: /* rst_stat */
+    case 0xC204: /* blk_pwr_stat */
+    case 0xC314: /* ion_skew_mon */
+    case 0xC324: /* ioff_skew_mon */
+    case 0xE100: /* om_stat */
+        hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+        break;
+    default:
+        hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pmu_readfn[] = {
+    s5pc1xx_pmu_read,
+    s5pc1xx_pmu_read,
+    s5pc1xx_pmu_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pmu_writefn[] = {
+    s5pc1xx_pmu_write,
+    s5pc1xx_pmu_write,
+    s5pc1xx_pmu_write
+};
+
+static void s5pc1xx_pmu_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+    qemu_put_be32s(f, &s->osc_con);
+    qemu_put_be32s(f, &s->rst_stat);
+    qemu_put_be32s(f, &s->pwr_cfg);
+    qemu_put_be32s(f, &s->eint_wakeup_mask);
+    qemu_put_be32s(f, &s->wakeup_mask);
+    qemu_put_be32s(f, &s->pwr_mode);
+    qemu_put_be32s(f, &s->normal_cfg);
+    qemu_put_be32s(f, &s->idle_cfg);
+    qemu_put_be32s(f, &s->stop_cfg);
+    qemu_put_be32s(f, &s->stop_mem_cfg);
+    qemu_put_be32s(f, &s->sleep_cfg);
+    qemu_put_be32s(f, &s->osc_freq);
+    qemu_put_be32s(f, &s->osc_stable);
+    qemu_put_be32s(f, &s->pwr_stable);
+    qemu_put_be32s(f, &s->mtc_stable);
+    qemu_put_be32s(f, &s->clamp_stable);
+    qemu_put_be32s(f, &s->wakeup_stat);
+    qemu_put_be32s(f, &s->blk_pwr_stat);
+    qemu_put_be32s(f, &s->body_bias_con);
+    qemu_put_be32s(f, &s->ion_skew_con);
+    qemu_put_be32s(f, &s->ion_skew_mon);
+    qemu_put_be32s(f, &s->ioff_skew_con);
+    qemu_put_be32s(f, &s->ioff_skew_mon);
+    qemu_put_be32s(f, &s->others);
+    qemu_put_be32s(f, &s->om_stat);
+    qemu_put_be32s(f, &s->mie_control);
+    qemu_put_be32s(f, &s->hdmi_control);
+    qemu_put_be32s(f, &s->usb_phy_control);
+    qemu_put_be32s(f, &s->dac_control);
+    qemu_put_be32s(f, &s->mipi_dphy_control);
+    qemu_put_be32s(f, &s->adc_control);
+    qemu_put_be32s(f, &s->ps_hold_control);
+    qemu_put_be32s(f, &s->inform0);
+    qemu_put_be32s(f, &s->inform1);
+    qemu_put_be32s(f, &s->inform2);
+    qemu_put_be32s(f, &s->inform3);
+    qemu_put_be32s(f, &s->inform4);
+    qemu_put_be32s(f, &s->inform5);
+    qemu_put_be32s(f, &s->inform6);
+    qemu_put_be32s(f, &s->inform7);
+}
+
+static int s5pc1xx_pmu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->osc_con);
+    qemu_get_be32s(f, &s->rst_stat);
+    qemu_get_be32s(f, &s->pwr_cfg);
+    qemu_get_be32s(f, &s->eint_wakeup_mask);
+    qemu_get_be32s(f, &s->wakeup_mask);
+    qemu_get_be32s(f, &s->pwr_mode);
+    qemu_get_be32s(f, &s->normal_cfg);
+    qemu_get_be32s(f, &s->idle_cfg);
+    qemu_get_be32s(f, &s->stop_cfg);
+    qemu_get_be32s(f, &s->stop_mem_cfg);
+    qemu_get_be32s(f, &s->sleep_cfg);
+    qemu_get_be32s(f, &s->osc_freq);
+    qemu_get_be32s(f, &s->osc_stable);
+    qemu_get_be32s(f, &s->pwr_stable);
+    qemu_get_be32s(f, &s->mtc_stable);
+    qemu_get_be32s(f, &s->clamp_stable);
+    qemu_get_be32s(f, &s->wakeup_stat);
+    qemu_get_be32s(f, &s->blk_pwr_stat);
+    qemu_get_be32s(f, &s->body_bias_con);
+    qemu_get_be32s(f, &s->ion_skew_con);
+    qemu_get_be32s(f, &s->ion_skew_mon);
+    qemu_get_be32s(f, &s->ioff_skew_con);
+    qemu_get_be32s(f, &s->ioff_skew_mon);
+    qemu_get_be32s(f, &s->others);
+    qemu_get_be32s(f, &s->om_stat);
+    qemu_get_be32s(f, &s->mie_control);
+    qemu_get_be32s(f, &s->hdmi_control);
+    qemu_get_be32s(f, &s->usb_phy_control);
+    qemu_get_be32s(f, &s->dac_control);
+    qemu_get_be32s(f, &s->mipi_dphy_control);
+    qemu_get_be32s(f, &s->adc_control);
+    qemu_get_be32s(f, &s->ps_hold_control);
+    qemu_get_be32s(f, &s->inform0);
+    qemu_get_be32s(f, &s->inform1);
+    qemu_get_be32s(f, &s->inform2);
+    qemu_get_be32s(f, &s->inform3);
+    qemu_get_be32s(f, &s->inform4);
+    qemu_get_be32s(f, &s->inform5);
+    qemu_get_be32s(f, &s->inform6);
+    qemu_get_be32s(f, &s->inform7);
+
+    return 0;
+}
+
+static void s5pc1xx_pmu_reset(void *opaque)
+{
+    S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+    s->osc_con         = 0x00000003;
+    s->rst_stat        = 0x00000001;
+    s->normal_cfg      = 0xFFFFFFBF;
+    s->idle_cfg        = 0x60000000;
+    s->stop_cfg        = 0x96000000;
+    s->stop_mem_cfg    = 0x000000FF;
+    s->osc_freq        = 0x0000000F;
+    s->osc_stable      = 0x000FFFFF;
+    s->mtc_stable      = 0xFFFFFFFF;
+    s->clamp_stable    = 0x03FF03FF;
+    s->blk_pwr_stat    = 0x000000BF;
+    s->body_bias_con   = 0x00000606;
+    s->mie_control     = 0x00000001;
+    s->hdmi_control    = 0x00960000;
+    s->dac_control     = 0x00000001;
+    s->ps_hold_control = 0x00005301;
+}
+
+static int s5pc1xx_pmu_init(SysBusDevice *dev)
+{
+    S5pc1xxPMUState *s = FROM_SYSBUS(S5pc1xxPMUState, dev);
+    int iomemtype;
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_pmu_readfn, s5pc1xx_pmu_writefn, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC110_PMU_SFR_SIZE, iomemtype);
+
+    s5pc1xx_pmu_reset(s);
+
+    qemu_register_reset(s5pc1xx_pmu_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.pmu", -1, 1,
+                    s5pc1xx_pmu_save, s5pc1xx_pmu_load, s);
+
+    return 0;
+}
+
+
+/* Maxim MAX17040 Fuel Gauge */
+
+#define MAX17040_VCELL_MSB      0x02
+#define MAX17040_VCELL_LSB      0x03
+#define MAX17040_SOC_MSB        0x04
+#define MAX17040_SOC_LSB        0x05
+#define MAX17040_MODE_MSB       0x06
+#define MAX17040_MODE_LSB       0x07
+#define MAX17040_VER_MSB        0x08
+#define MAX17040_VER_LSB        0x09
+#define MAX17040_RCOMP_MSB      0x0C
+#define MAX17040_RCOMP_LSB      0x0D
+#define MAX17040_CMD_MSB        0xFE
+#define MAX17040_CMD_LSB        0xFF
+
+#define MAX17040_DELAY          1000
+#define MAX17040_BATTERY_FULL   95
+#define MAX17040_BATTERY_CHARGE 40
+
+
+typedef struct MAX17040State {
+    SMBusDevice smbusdev;
+
+    uint16_t charge;
+    uint16_t vcell;
+    uint16_t mode;
+    uint16_t ver;
+    uint16_t rcomp;
+    uint16_t cmd;
+} MAX17040State;
+
+static void max17040_reset(void *opaque)
+{
+    MAX17040State *s = (MAX17040State *)opaque;
+
+    s->charge = MAX17040_BATTERY_CHARGE << 8;
+    s->vcell = 4200;
+    s->mode = 0;
+    s->ver = 0xAA28;
+    s->rcomp = 0;
+    s->cmd = 0;
+}
+
+static void max17040_write_data(SMBusDevice *dev, uint8_t cmd,
+                                uint8_t *buf, int len)
+{
+    MAX17040State *s = (MAX17040State *)dev;
+    int shift = 0, i;
+    uint16_t *reg = NULL;
+
+    switch (cmd) {
+    case MAX17040_VCELL_LSB:
+        shift = 1;
+    case MAX17040_VCELL_MSB:
+        reg = &s->vcell;
+        break;
+    case MAX17040_SOC_LSB:
+        shift = 1;
+    case MAX17040_SOC_MSB:
+        reg = &s->charge;
+        break;
+    case MAX17040_MODE_LSB:
+        shift = 1;
+    case MAX17040_MODE_MSB:
+        reg = &s->mode;
+        break;
+    case MAX17040_VER_LSB:
+        shift = 1;
+    case MAX17040_VER_MSB:
+        reg = &s->ver;
+        break;
+    case MAX17040_RCOMP_LSB:
+        shift = 1;
+    case MAX17040_RCOMP_MSB:
+        reg = &s->rcomp;
+        break;
+    case MAX17040_CMD_LSB:
+        shift = 1;
+    case MAX17040_CMD_MSB:
+        reg = &s->cmd;
+        break;
+    default:
+        hw_error("max17040: bad write offset 0x%x\n", cmd);
+    }
+
+    if (len > 2 - shift) {
+        hw_error("max17040: bad write length %d\n", len);
+    }
+
+    for (i = 0; i < len; i++) {
+        *((uint8_t *)reg + i + shift) = buf[i];
+    }
+}
+
+static uint8_t max17040_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+    MAX17040State *s = (MAX17040State *)dev;
+    int shift = 0;
+    uint16_t val;
+
+    if (n > 0) {
+               //bypass => todo check!!
+        //hw_error("max17040: bad read length %d\n", n);
+    }
+
+    switch (cmd) {
+    case MAX17040_VCELL_MSB:
+        shift = 1;
+    case MAX17040_VCELL_LSB:
+        val = s->vcell;
+        break;
+    case MAX17040_SOC_MSB:
+        shift = 1;
+    case MAX17040_SOC_LSB:
+        val = s->charge;
+        break;
+    case MAX17040_MODE_MSB:
+        shift = 1;
+    case MAX17040_MODE_LSB:
+        val = s->mode;
+        break;
+    case MAX17040_VER_MSB:
+        shift = 1;
+    case MAX17040_VER_LSB:
+        val = s->ver;
+        break;
+    case MAX17040_RCOMP_MSB:
+        shift = 1;
+    case MAX17040_RCOMP_LSB:
+        val = s->rcomp;
+        break;
+    case MAX17040_CMD_MSB:
+        shift = 1;
+    case MAX17040_CMD_LSB:
+        val = s->cmd;
+        break;
+    default:
+        hw_error("max17040: bad read offset 0x%x\n", cmd);
+    }
+
+    return ((val >> (shift * 8)) & 0xFF);
+}
+
+static void max17040_save(QEMUFile *f, void *opaque)
+{
+    MAX17040State *s = (MAX17040State *)opaque;
+
+    qemu_put_be16s(f, &s->charge);
+    qemu_put_be16s(f, &s->vcell);
+    qemu_put_be16s(f, &s->mode);
+    qemu_put_be16s(f, &s->ver);
+    qemu_put_be16s(f, &s->rcomp);
+    qemu_put_be16s(f, &s->cmd);
+}
+
+static int max17040_load(QEMUFile *f, void *opaque, int version_id)
+{
+    MAX17040State *s = (MAX17040State *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be16s(f, &s->charge);
+    qemu_get_be16s(f, &s->vcell);
+    qemu_get_be16s(f, &s->mode);
+    qemu_get_be16s(f, &s->ver);
+    qemu_get_be16s(f, &s->rcomp);
+    qemu_get_be16s(f, &s->cmd);
+
+    return 0;
+}
+
+DeviceState *max17040_init(i2c_bus *bus, int addr)
+{
+    DeviceState *dev = qdev_create((BusState *)bus, "max17040");
+    qdev_init_nofail(dev);
+    i2c_set_slave_address((i2c_slave *)dev, addr);
+    return dev;
+}
+
+static int max17040_init1(SMBusDevice *dev)
+{
+    MAX17040State *s = (MAX17040State *) dev;
+    max17040_reset(s);
+    qemu_register_reset(max17040_reset, s);
+    register_savevm(&dev->i2c.qdev, "max17040", -1, 1,
+                    max17040_save, max17040_load, s);
+    return 0;
+}
+
+static SMBusDeviceInfo max17040_info = {
+    .i2c.qdev.name = "max17040",
+    .i2c.qdev.size = sizeof(MAX17040State),
+    .init = max17040_init1,
+    .write_data = max17040_write_data,
+    .read_data = max17040_read_data
+};
+
+
+/* Maxim MAX8998 Battery Charger */
+
+#define MAX8998_REG_IRQ1                   0
+#define MAX8998_REG_IRQ2                   1
+#define MAX8998_REG_IRQ3                   2
+#define MAX8998_REG_IRQ4                   3
+#define MAX8998_REG_IRQM1                  4
+#define MAX8998_REG_IRQM2                  5
+#define MAX8998_REG_IRQM3                  6
+#define MAX8998_REG_IRQM4                  7
+#define MAX8998_REG_STATUS1                8
+#define MAX8998_REG_STATUS2                9
+#define MAX8998_REG_STATUSM1               10
+#define MAX8998_REG_STATUSM2               11
+#define MAX8998_REG_CHGR1                  12
+#define MAX8998_REG_CHGR2                  13
+#define MAX8998_REG_LDO_ACTIVE_DISCHARGE1  14
+#define MAX8998_REG_LDO_ACTIVE_DISCHARGE2  15
+#define MAX8998_REG_BUCK_ACTIVE_DISCHARGE3 16
+#define MAX8998_REG_ONOFF1                 17
+#define MAX8998_REG_ONOFF2                 18
+#define MAX8998_REG_ONOFF3                 19
+#define MAX8998_REG_ONOFF4                 20
+#define MAX8998_REG_BUCK1_DVSARM1          21
+#define MAX8998_REG_BUCK1_DVSARM2          22
+#define MAX8998_REG_BUCK1_DVSARM3          23
+#define MAX8998_REG_BUCK1_DVSARM4          24
+#define MAX8998_REG_BUCK2_DVSINT1          25
+#define MAX8998_REG_BUCK2_DVSINT2          26
+#define MAX8998_REG_BUCK3                  27
+#define MAX8998_REG_BUCK4                  28
+#define MAX8998_REG_LDO2_LDO3              29
+#define MAX8998_REG_LDO4                   30
+#define MAX8998_REG_LDO5                   31
+#define MAX8998_REG_LDO6                   32
+#define MAX8998_REG_LDO7                   33
+#define MAX8998_REG_LDO8_LDO9              34
+#define MAX8998_REG_LDO10_LDO11            35
+#define MAX8998_REG_LDO12                  36
+#define MAX8998_REG_LDO13                  37
+#define MAX8998_REG_LDO14                  38
+#define MAX8998_REG_LDO15                  39
+#define MAX8998_REG_LDO16                  40
+#define MAX8998_REG_LDO17                  41
+#define MAX8998_REG_BKCHR                  42
+#define MAX8998_REG_LBCNFG1                43
+#define MAX8998_REG_LBCNFG2                44
+
+
+typedef struct MAX8998State {
+    SMBusDevice smbusdev;
+
+    uint32_t irq;
+    uint32_t irqm;
+    uint16_t status;
+    uint16_t statusm;
+    uint16_t chgr;
+    uint16_t ldo_active_discharge;
+    uint8_t  buck_active_discharge;
+    uint32_t onoff;
+    uint32_t buck1_dvsarm;
+    uint16_t buck2_dvsint;
+    uint8_t  buck3;
+    uint8_t  buck4;
+    uint32_t ldo23_4_5_6;
+    uint32_t ldo7_89_1011_12;
+    uint32_t ldo13_14_15_16;
+    uint8_t  ldo17;
+    uint8_t  bkchr;
+    uint16_t lbcnfg;
+} MAX8998State;
+
+static void max8998_reset(void *opaque)
+{
+    MAX8998State *s = (MAX8998State *)opaque;
+
+    s->irq             = 0;
+    s->irqm            = 0;
+    s->status          = 0;
+    s->statusm         = 0xEFFF;
+    s->chgr            = 0xA80;
+    s->ldo_active_discharge = 0;
+    s->buck_active_discharge = 0;
+    s->onoff           = 0;
+    s->buck1_dvsarm    = 0;
+    s->buck2_dvsint    = 0;
+    s->buck3           = 0;
+    s->buck4           = 0;
+    s->ldo23_4_5_6     = 0;
+    s->ldo7_89_1011_12 = 0;
+    s->ldo13_14_15_16  = 0;
+    s->ldo17           = 0;
+    s->bkchr           = 0;
+    s->lbcnfg          = 0;
+}
+
+static uint8_t *max8998_get_reg_addr(MAX8998State *s, uint8_t cmd)
+{
+    int shift = 0;
+    uint8_t *reg = NULL;
+
+    switch (cmd) {
+    case MAX8998_REG_IRQ4:
+    case MAX8998_REG_IRQM4:
+    case MAX8998_REG_STATUS2:
+    case MAX8998_REG_STATUSM2:
+    case MAX8998_REG_CHGR2:
+    case MAX8998_REG_LDO_ACTIVE_DISCHARGE2:
+    case MAX8998_REG_BUCK_ACTIVE_DISCHARGE3:
+    case MAX8998_REG_ONOFF4:
+    case MAX8998_REG_BUCK1_DVSARM4:
+    case MAX8998_REG_BUCK2_DVSINT2:
+    case MAX8998_REG_BUCK3:
+    case MAX8998_REG_BUCK4:
+    case MAX8998_REG_LDO6:
+    case MAX8998_REG_LDO12:
+    case MAX8998_REG_LDO16:
+    case MAX8998_REG_LDO17:
+    case MAX8998_REG_BKCHR:
+    case MAX8998_REG_LBCNFG2:
+        break;
+    case MAX8998_REG_IRQ3:
+    case MAX8998_REG_IRQM3:
+    case MAX8998_REG_STATUS1:
+    case MAX8998_REG_STATUSM1:
+    case MAX8998_REG_CHGR1:
+    case MAX8998_REG_LDO_ACTIVE_DISCHARGE1:
+    case MAX8998_REG_ONOFF3:
+    case MAX8998_REG_BUCK1_DVSARM3:
+    case MAX8998_REG_BUCK2_DVSINT1:
+    case MAX8998_REG_LDO5:
+    case MAX8998_REG_LDO10_LDO11:
+    case MAX8998_REG_LDO15:
+    case MAX8998_REG_LBCNFG1:
+        shift = 1;
+        break;
+    case MAX8998_REG_IRQ2:
+    case MAX8998_REG_IRQM2:
+    case MAX8998_REG_ONOFF2:
+    case MAX8998_REG_BUCK1_DVSARM2:
+    case MAX8998_REG_LDO4:
+    case MAX8998_REG_LDO8_LDO9:
+    case MAX8998_REG_LDO14:
+        shift = 2;
+        break;
+    case MAX8998_REG_IRQ1:
+    case MAX8998_REG_IRQM1:
+    case MAX8998_REG_ONOFF1:
+    case MAX8998_REG_BUCK1_DVSARM1:
+    case MAX8998_REG_LDO2_LDO3:
+    case MAX8998_REG_LDO7:
+    case MAX8998_REG_LDO13:
+        shift = 3;
+        break;
+    default:
+        hw_error("max8998: bad write offset 0x%x\n", cmd);
+    }
+
+    switch (cmd) {
+    case MAX8998_REG_IRQ1:
+    case MAX8998_REG_IRQ2:
+    case MAX8998_REG_IRQ3:
+    case MAX8998_REG_IRQ4:
+        reg = (uint8_t *)&s->irq;
+        break;
+    case MAX8998_REG_IRQM1:
+    case MAX8998_REG_IRQM2:
+    case MAX8998_REG_IRQM3:
+    case MAX8998_REG_IRQM4:
+        reg = (uint8_t *)&s->irqm;
+        break;
+    case MAX8998_REG_STATUS1:
+    case MAX8998_REG_STATUS2:
+        reg = (uint8_t *)&s->status;
+        break;
+    case MAX8998_REG_STATUSM1:
+    case MAX8998_REG_STATUSM2:
+        reg = (uint8_t *)&s->statusm;
+        break;
+    case MAX8998_REG_CHGR1:
+    case MAX8998_REG_CHGR2:
+        reg = (uint8_t *)&s->chgr;
+        break;
+    case MAX8998_REG_LDO_ACTIVE_DISCHARGE1:
+    case MAX8998_REG_LDO_ACTIVE_DISCHARGE2:
+        reg = (uint8_t *)&s->ldo_active_discharge;
+        break;
+    case MAX8998_REG_BUCK_ACTIVE_DISCHARGE3:
+        reg = (uint8_t *)&s->buck_active_discharge;
+        break;
+    case MAX8998_REG_ONOFF1:
+    case MAX8998_REG_ONOFF2:
+    case MAX8998_REG_ONOFF3:
+    case MAX8998_REG_ONOFF4:
+        reg = (uint8_t *)&s->onoff;
+        break;
+    case MAX8998_REG_BUCK1_DVSARM1:
+    case MAX8998_REG_BUCK1_DVSARM2:
+    case MAX8998_REG_BUCK1_DVSARM3:
+    case MAX8998_REG_BUCK1_DVSARM4:
+        reg = (uint8_t *)&s->buck1_dvsarm;
+        break;
+    case MAX8998_REG_BUCK2_DVSINT1:
+    case MAX8998_REG_BUCK2_DVSINT2:
+        reg = (uint8_t *)&s->buck2_dvsint;
+        break;
+    case MAX8998_REG_BUCK3:
+        reg = (uint8_t *)&s->buck3;
+        break;
+    case MAX8998_REG_BUCK4:
+        reg = (uint8_t *)&s->buck4;
+        break;
+    case MAX8998_REG_LDO2_LDO3:
+    case MAX8998_REG_LDO4:
+    case MAX8998_REG_LDO5:
+    case MAX8998_REG_LDO6:
+        reg = (uint8_t *)&s->ldo23_4_5_6;
+        break;
+    case MAX8998_REG_LDO7:
+    case MAX8998_REG_LDO8_LDO9:
+    case MAX8998_REG_LDO10_LDO11:
+    case MAX8998_REG_LDO12:
+        reg = (uint8_t *)&s->ldo7_89_1011_12;
+        break;
+    case MAX8998_REG_LDO13:
+    case MAX8998_REG_LDO14:
+    case MAX8998_REG_LDO15:
+    case MAX8998_REG_LDO16:
+        reg = (uint8_t *)&s->ldo13_14_15_16;
+        break;
+    case MAX8998_REG_LDO17:
+        reg = (uint8_t *)&s->ldo17;
+        break;
+    case MAX8998_REG_BKCHR:
+        reg = (uint8_t *)&s->bkchr;
+        break;
+    case MAX8998_REG_LBCNFG1:
+    case MAX8998_REG_LBCNFG2:
+        reg = (uint8_t *)&s->lbcnfg;
+        break;
+    default:
+        hw_error("max8998: bad write offset 0x%x\n", cmd);
+    }
+
+    return (reg + shift);
+}
+
+static void max8998_write_data(SMBusDevice *dev, uint8_t cmd,
+                               uint8_t *buf, int len)
+{
+    MAX8998State *s = (MAX8998State *)dev;
+
+    if (len > 1) {
+        hw_error("max8998: bad write length %d\n", len);
+    }
+
+    *(max8998_get_reg_addr(s, cmd)) = buf[0];
+}
+
+static uint8_t max8998_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+    MAX8998State *s = (MAX8998State *)dev;
+
+    if (n > 0 && cmd != MAX8998_REG_IRQ1) {
+        hw_error("max8998: bad read length %d\n", n);
+    }
+
+    return *(max8998_get_reg_addr(s, cmd) + n);
+}
+
+static void max8998_save(QEMUFile *f, void *opaque)
+{
+    MAX8998State *s = (MAX8998State *)opaque;
+
+    qemu_put_be32s(f, &s->irq);
+    qemu_put_be32s(f, &s->irqm);
+    qemu_put_be16s(f, &s->status);
+    qemu_put_be16s(f, &s->statusm);
+    qemu_put_be16s(f, &s->chgr);
+    qemu_put_be16s(f, &s->ldo_active_discharge);
+    qemu_put_8s   (f, &s->buck_active_discharge);
+    qemu_put_be32s(f, &s->onoff);
+    qemu_put_be32s(f, &s->buck1_dvsarm);
+    qemu_put_be16s(f, &s->buck2_dvsint);
+    qemu_put_8s   (f, &s->buck3);
+    qemu_put_8s   (f, &s->buck4);
+    qemu_put_be32s(f, &s->ldo23_4_5_6);
+    qemu_put_be32s(f, &s->ldo7_89_1011_12);
+    qemu_put_be32s(f, &s->ldo13_14_15_16);
+    qemu_put_8s   (f, &s->ldo17);
+    qemu_put_8s   (f, &s->bkchr);
+    qemu_put_be16s(f, &s->lbcnfg);
+}
+
+static int max8998_load(QEMUFile *f, void *opaque, int version_id)
+{
+    MAX8998State *s = (MAX8998State *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->irq);
+    qemu_get_be32s(f, &s->irqm);
+    qemu_get_be16s(f, &s->status);
+    qemu_get_be16s(f, &s->statusm);
+    qemu_get_be16s(f, &s->chgr);
+    qemu_get_be16s(f, &s->ldo_active_discharge);
+    qemu_get_8s   (f, &s->buck_active_discharge);
+    qemu_get_be32s(f, &s->onoff);
+    qemu_get_be32s(f, &s->buck1_dvsarm);
+    qemu_get_be16s(f, &s->buck2_dvsint);
+    qemu_get_8s   (f, &s->buck3);
+    qemu_get_8s   (f, &s->buck4);
+    qemu_get_be32s(f, &s->ldo23_4_5_6);
+    qemu_get_be32s(f, &s->ldo7_89_1011_12);
+    qemu_get_be32s(f, &s->ldo13_14_15_16);
+    qemu_get_8s   (f, &s->ldo17);
+    qemu_get_8s   (f, &s->bkchr);
+    qemu_get_be16s(f, &s->lbcnfg);
+
+    return 0;
+}
+
+DeviceState *max8998_init(i2c_bus *bus, int addr)
+{
+    DeviceState *dev = qdev_create((BusState *)bus, "max8998");
+    qdev_init_nofail(dev);
+    i2c_set_slave_address((i2c_slave *)dev, addr);
+    return dev;
+}
+
+static int max8998_init1(SMBusDevice *dev)
+{
+    MAX8998State *s = (MAX8998State *) dev;
+    max8998_reset(s);
+    qemu_register_reset(max8998_reset, s);
+    register_savevm(&dev->i2c.qdev, "max8998", -1, 1,
+                    max8998_save, max8998_load, s);
+    return 0;
+}
+
+static SMBusDeviceInfo max8998_info = {
+    .i2c.qdev.name = "max8998",
+    .i2c.qdev.size = sizeof(MAX8998State),
+    .init = max8998_init1,
+    .write_data = max8998_write_data,
+    .read_data = max8998_read_data
+};
+
+
+/* Maxim MAX8998 Real Time Clock */
+
+/* MX8998 RTC I2C MAP */
+#define RTC_SEC         0x0         /* second 00-59 */
+#define RTC_MIN         0x1         /* minute 00-59 */
+#define RTC_HR          0x2         /* hour AM/PM 1-12 or 00-23 */
+#define RTC_DAY         0x3         /* weekday 1-7 */
+#define RTC_DATE        0x4         /* date 01-31 */
+#define RTC_MT          0x5         /* month 01-12 */
+#define RTC_YEAR        0x6         /* year 00-99 */
+#define RTC_CEN         0x7         /* century 00-99 */
+
+#define RTC_CON         0x1A
+    #define RTC_EN          0x01    /* RTC control enable */
+
+#define MAX8998_REG(x, y)   (0x8 * y + x)
+
+/* Time Keeper */
+#define MAX8998_RTC         0
+/* Alarm0 */
+#define MAX8998_ALRM0       1
+/* Alarm1 */
+#define MAX8998_ALRM1       2
+/* Conf */
+#define MAX8998_ALRM0_CONF  0x18
+#define MAX8998_ALRM1_CONF  0x19
+
+#define MAX8998_ALRM_ON     0x77
+#define MAX8998_ALRM_OFF    0x0
+
+
+typedef struct MAX8998RTCState {
+    SMBusDevice smbusdev;
+
+    uint8_t  regs[RTC_CON + 1];
+
+    /* seconds update */
+    QEMUTimer *seconds_timer;
+    struct tm current_tm;
+} MAX8998RTCState;
+
+static void max8998_rtc_seconds_update(void *opaque);
+
+/* Set default values for all fields */
+static void max8998_rtc_reset(void *opaque)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+    short i;
+
+    /* stop seconds timer */
+    s->regs[RTC_CON] = 0x0;
+    max8998_rtc_seconds_update(s);
+
+    for (i = RTC_SEC; i <= RTC_CEN; i++) {
+        s->regs[MAX8998_REG(i, MAX8998_RTC)] = 0;
+    }
+    /* get time from host */
+    qemu_get_timedate(&s->current_tm, 0);
+
+    /* start seconds timer */
+    s->regs[RTC_CON] |= RTC_EN;
+    max8998_rtc_seconds_update(s);
+}
+
+/* Get days in month. Month is between 0 and 11 */
+static int get_days_in_month(int month, int year)
+{
+    short d;
+    static const int days_tab[12] = {
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+
+    if ((unsigned)month >= 12) {
+        return 31;
+    }
+    d = days_tab[month];
+    if (month == 1) {
+        if ((year % 4) == 0) {
+            d++;
+        }
+    }
+    return d;
+}
+
+/* Update 'struct tm' to the next second */
+static void max8998_rtc_next_second(struct tm *tm)
+{
+    int days_in_month;
+
+    tm->tm_sec++;
+    if ((unsigned)tm->tm_sec >= 60) {
+        tm->tm_sec = 0;
+        tm->tm_min++;
+        if ((unsigned)tm->tm_min >= 60) {
+            tm->tm_min = 0;
+            tm->tm_hour++;
+            if ((unsigned)tm->tm_hour >= 24) {
+                tm->tm_hour = 0;
+                /* next day */
+                tm->tm_wday++;
+                if ((unsigned)tm->tm_wday >= 8) {
+                    tm->tm_wday = 1;
+                }
+                days_in_month = get_days_in_month(tm->tm_mon, tm->tm_year);
+                tm->tm_mday++;
+                if (tm->tm_mday < 1) {
+                    tm->tm_mday = 1;
+                } else if (tm->tm_mday > days_in_month) {
+                    tm->tm_mday = 1;
+                    tm->tm_mon++;
+                    if (tm->tm_mon >= 12) {
+                        tm->tm_mon = 0;
+                        tm->tm_year++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+/* Using of qemu_timer to increase seconds */
+static void max8998_rtc_seconds_update(void *opaque)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+    uint64_t next_seconds_time;
+
+    if (s->regs[RTC_CON] & RTC_EN) {
+        max8998_rtc_next_second(&(s->current_tm));
+        next_seconds_time = qemu_get_clock(vm_clock) + get_ticks_per_sec();
+        qemu_mod_timer(s->seconds_timer, next_seconds_time);
+    } else {
+        qemu_del_timer(s->seconds_timer);
+    }
+}
+
+/* Convert time from bcd */
+static void max8998_rtc_set_time(MAX8998RTCState *s)
+{
+    struct tm *tm = &(s->current_tm);
+
+    tm->tm_sec  = from_bcd(s->regs[RTC_SEC]);
+    tm->tm_min  = from_bcd(s->regs[RTC_MIN]);
+    tm->tm_hour = from_bcd(s->regs[RTC_HR] & 0x3f);
+    tm->tm_mday = from_bcd(s->regs[RTC_DATE]);
+    tm->tm_mon  = from_bcd(s->regs[RTC_MT]);
+    tm->tm_year = from_bcd(s->regs[RTC_YEAR]) +
+                  from_bcd(s->regs[RTC_CEN]) * 100 - 1900;
+    tm->tm_wday = s->regs[RTC_DAY]; /* one decimal digit */
+}
+
+/* Convert time to bcd */
+static void max8998_rtc_read_time(MAX8998RTCState *s)
+{
+    const struct tm *tm = &(s->current_tm);
+
+    s->regs[RTC_SEC]  = to_bcd(tm->tm_sec);
+    s->regs[RTC_MIN]  = to_bcd(tm->tm_min);
+    s->regs[RTC_HR]   = to_bcd(tm->tm_hour);
+    s->regs[RTC_DATE] = to_bcd(tm->tm_mday);
+    s->regs[RTC_MT]   = to_bcd(tm->tm_mon);
+    s->regs[RTC_YEAR] = to_bcd(tm->tm_year % 100);
+    s->regs[RTC_CEN]  = to_bcd((tm->tm_year + 1900) / 100);
+    s->regs[RTC_DAY]  = tm->tm_wday; /* one decimal digit */
+}
+
+/* Write RTC MAX8998 by I2C through SMBus */
+static void max8998_rtc_write(SMBusDevice *dev, uint8_t cmd,
+                              uint8_t *buf, int len)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)dev;
+    uint8_t *reg = NULL;
+    int i;
+
+    switch (cmd) {
+    case MAX8998_REG(RTC_SEC, MAX8998_RTC) ...
+         MAX8998_REG(RTC_CEN, MAX8998_RTC):
+    case MAX8998_ALRM0_CONF:
+    case MAX8998_ALRM1_CONF:
+        reg = &s->regs[cmd];
+        break;
+
+    /* TODO: Implement alarm support */
+    case MAX8998_REG(RTC_SEC, MAX8998_ALRM0) ...
+         MAX8998_REG(RTC_CEN, MAX8998_ALRM1):
+        return;
+
+    default:
+        hw_error("max8998-rtc: bad write offset 0x%x\n", cmd);
+    }
+
+    for (i = 0; i < len; i++) {
+        *((uint8_t *)reg + i) = buf[i];
+    }
+
+    if (cmd <= MAX8998_REG(RTC_CEN, MAX8998_RTC)) {
+        max8998_rtc_set_time(s);
+    }
+}
+
+/* Read RTC MAX8998 by I2C through SMBus */
+static uint8_t max8998_rtc_read(SMBusDevice *dev, uint8_t cmd, int n)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)dev;
+    uint16_t val;
+
+    if (n > 0) {
+        hw_error("max8998-rtc: bad read length %d\n", n);
+    }
+
+    switch (cmd) {
+    case MAX8998_REG(RTC_SEC, MAX8998_RTC) ...
+         MAX8998_REG(RTC_CEN, MAX8998_RTC):
+        max8998_rtc_read_time(s);
+    case MAX8998_ALRM0_CONF:
+    case MAX8998_ALRM1_CONF:
+        val = s->regs[cmd];
+        break;
+
+    /* TODO: Implement alarm support */
+    case MAX8998_REG(RTC_SEC, MAX8998_ALRM0) ...
+         MAX8998_REG(RTC_CEN, MAX8998_ALRM1):
+        return 0;
+
+    default:
+        hw_error("max8998-rtc: bad read offset 0x%x\n", cmd);
+    }
+
+    return (val & 0xFF);
+}
+
+static void max8998_rtc_save(QEMUFile *f, void *opaque)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+
+    max8998_rtc_read_time(s);
+
+    qemu_put_buffer(f, s->regs, sizeof(s->regs));
+    qemu_put_timer(f, s->seconds_timer);
+}
+
+static int max8998_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_buffer(f, s->regs, sizeof(s->regs));
+
+    qemu_get_timedate(&s->current_tm, 0);
+    max8998_rtc_read_time(s);
+
+    qemu_get_timer(f, s->seconds_timer);
+
+    return 0;
+}
+
+DeviceState *max8998_rtc_init(i2c_bus *bus, int addr)
+{
+    DeviceState *dev = qdev_create((BusState *)bus, "max8998-rtc");
+    qdev_init_nofail(dev);
+    i2c_set_slave_address((i2c_slave *)dev, addr);
+    return dev;
+}
+
+static int max8998_rtc_init1(SMBusDevice *dev)
+{
+    MAX8998RTCState *s = (MAX8998RTCState *) dev;
+
+    s->seconds_timer = qemu_new_timer(vm_clock, max8998_rtc_seconds_update, s);
+
+    /* initialize values */
+    max8998_rtc_reset(s);
+
+    qemu_register_reset(max8998_rtc_reset, s);
+    register_savevm(&dev->i2c.qdev, "max8998-rtc", -1, 1,
+                    max8998_rtc_save, max8998_rtc_load, s);
+
+    return 0;
+}
+
+static SMBusDeviceInfo max8998_rtc_info = {
+    .i2c.qdev.name = "max8998-rtc",
+    .i2c.qdev.size = sizeof(MAX8998RTCState),
+    .init          = max8998_rtc_init1,
+    .write_data    = max8998_rtc_write,
+    .read_data     = max8998_rtc_read
+};
+
+static void s5pc1xx_pmu_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.pmu", sizeof(S5pc1xxPMUState), s5pc1xx_pmu_init);
+    smbus_register_device(&max17040_info);
+    smbus_register_device(&max8998_info);
+    smbus_register_device(&max8998_rtc_info);
+}
+
+device_init(s5pc1xx_pmu_register_devices)
diff --git a/hw/s5pc1xx_pwm.c b/hw/s5pc1xx_pwm.c
new file mode 100644 (file)
index 0000000..3be4d2d
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * PWM Timer for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define S5C_TCFG0                   (0x00)  /* R/W Timer Configuration
+                                             * Register 0 that configures two
+                                             * 8-bit Prescaler and DeadZone
+                                             * Length */
+#define S5C_TCFG1                   (0x04)  /* R/W Timer Configuration
+                                             * Register 1 that controls
+                                             * 5 MUX Select Bit */
+#define S5C_TCON                    (0x08)  /* R/W Timer Control Register */
+#define S5C_TINT_CSTAT              (0x44)  /* R/W Timer Interrupt Control and
+                                             * Status Register */
+/* TCFG0 */
+#define S5C_TCFG0_PRESCALER0_SHIFT  (0)
+#define S5C_TCFG0_PRESCALER1_SHIFT  (8)
+
+/* TCFG1 */
+#define S5C_TCFG1_DIV_SHIFT(tmr)    (tmr * 0x04)
+#define TCLK                        0x5
+
+/* TINT_CSTAT */
+#define INT_EN(tmr)                 (1 << tmr)
+#define INT_STAT(tmr)               (1 << (tmr+5))
+
+#define S5C_TIMERREG(tmr,reg)       (0x0c + reg + (0x0c * tmr))
+
+/* R/W Timer 0..4 Count Buffer Register */
+#define S5C_TCNTB(tmr)              S5C_TIMERREG(tmr, 0x00)
+
+/* R/W Timer 0..4 Compare Buffer Register */
+#define S5C_TCMPB(tmr)              S5C_TIMERREG(tmr, 0x04)
+
+/* R Timer 0..4 Count Observation Register */
+#define S5C_TCNTO(tmr)              S5C_TIMERREG(tmr, ((tmr == 4) ? 0x04 : 0x08))
+#define ALL_STAT                    (INT_STAT(0) | INT_STAT(1) | INT_STAT(2) | \
+                                     INT_STAT(3) | INT_STAT(4))
+
+#define S5PC1XX_PWM_REG_MEM_SIZE    0x50
+
+static const uint32_t S5C_TCON_RELOAD[5]    = {(1 << 3), (1 << 11), (1 << 15), (1 << 19), (1 << 22)};
+static const uint32_t S5C_TCON_INVERT[5]    = {(1 << 2), (1 << 10), (1 << 14), (1 << 18),    (0)   };
+static const uint32_t S5C_TCON_MANUALUPD[5] = {(1 << 1), (1 << 9),  (1 << 13), (1 << 17), (1 << 21)};
+static const uint32_t S5C_TCON_START[5]     = {(1 << 0), (1 << 8),  (1 << 12), (1 << 16), (1 << 20)};
+
+
+struct S5pc1xxPWMState;
+
+typedef struct S5pc1xxPWMTimerState {
+    qemu_irq  irq_inst;         /* irq instance */
+    uint8_t   tag;              /* control tag and timer number */
+
+    QEMUTimer *pwm_timer;
+    uint32_t  freq_out;
+    uint64_t  last_pwm_time;
+
+    uint32_t  tcntb;            /* count buffer value */
+    uint64_t  tcntb_in_qemu;    /* tcntb measured in 1/(9GHz) units */
+    uint32_t  tcnt;             /* count initial value */
+    uint64_t  tcnt_in_qemu;     /* tcnt measured in 1/(9GHz) units */
+    uint32_t  tcmpb;            /* comparison buffer value */
+    uint32_t  tcmp;             /* comparison current value */
+
+    struct S5pc1xxPWMState *regs;
+} S5pc1xxPWMTimerState;
+
+typedef struct S5pc1xxPWMState {
+    SysBusDevice busdev;
+    uint8_t      tag;           /* control tag */
+    /* control regs */
+    uint32_t     tcfg0;
+    uint32_t     tcfg1;
+    uint32_t     tcon;
+    uint32_t     tint_cstat;
+    S5pc1xxPWMTimerState tmr[5]; /* timers settings */
+} S5pc1xxPWMState;
+
+
+/* Convert term to be measured in (1/pwm_timer_freq) units */
+static uint32_t s5pc1xx_pwm_convert_term(uint64_t term, uint32_t timer_freq)
+{
+    return muldiv64(term, timer_freq, get_ticks_per_sec());
+}
+
+/* Get buffer values */
+static void s5pc1xx_pwm_timer_renew(S5pc1xxPWMTimerState *t)
+{
+    t->tcmp = t->tcmpb;
+    t->tcnt = t->tcntb;
+    t->tcnt_in_qemu = t->tcntb_in_qemu;
+}
+
+/* Set timer */
+static void s5pc1xx_pwm_timer_start(S5pc1xxPWMTimerState *t)
+{
+    t->last_pwm_time = qemu_get_clock(vm_clock);
+    qemu_mod_timer(t->pwm_timer, t->last_pwm_time + t->tcnt_in_qemu);
+}
+
+/* Stop timer */
+static void s5pc1xx_pwm_timer_stop(S5pc1xxPWMTimerState *t)
+{
+    qemu_del_timer(t->pwm_timer);
+}
+
+/* Events when timer riches zero */
+static void s5pc1xx_pwm_timer_expiry(void *opaque)
+{
+    S5pc1xxPWMTimerState *t = (S5pc1xxPWMTimerState *)opaque;
+    uint8_t num = t->tag;
+
+    if (t->tag > 4)
+        hw_error("s5pc1xx.pwm: timer_expiry - wrong param (tag = %u)\n",
+                 t->tag);
+
+    /* Interrupt raising */
+    t->regs->tint_cstat |= INT_STAT(num);
+    if (t->regs->tint_cstat & INT_EN(num))
+        qemu_irq_raise(t->irq_inst);
+
+    if (t->regs->tcon & S5C_TCON_RELOAD[num]) {
+        s5pc1xx_pwm_timer_renew(t);
+        s5pc1xx_pwm_timer_start(t);
+    }
+}
+
+/* Update timer frequency */
+static void s5pc1xx_pwm_timer_freq(S5pc1xxPWMTimerState *t)
+{
+    S5pc1xxClk clk;
+    uint8_t divisor;
+    uint8_t prescaler = 0;
+
+    if (t->tag > 4)
+        hw_error("s5pc1xx.pwm: timer_freq - wrong param (tag = %u)\n",
+                 t->tag);
+
+    switch (t->regs->tcfg1 >> S5C_TCFG1_DIV_SHIFT(t->tag) & 0xf) {
+        case TCLK:
+            clk = s5pc1xx_findclk("sclk_pwm");
+            prescaler = 0;
+            divisor = 1;
+            break;
+        default:
+            clk = s5pc1xx_findclk("pclk_66");
+            switch (t->tag) {
+                case 0 ... 1:
+                    prescaler =
+                        t->regs->tcfg0 >> S5C_TCFG0_PRESCALER0_SHIFT & 0xFF;
+                    break;
+                case 2 ... 4:
+                    prescaler =
+                        t->regs->tcfg0 >> S5C_TCFG0_PRESCALER1_SHIFT & 0xFF;
+                    break;
+            }
+        divisor = 1 << (t->regs->tcfg1 >> S5C_TCFG1_DIV_SHIFT(t->tag) & 0xF);
+    }
+    t->freq_out = s5pc1xx_clk_getrate(clk) / (prescaler + 1) / divisor;
+}
+
+/* Read PWM by GPIO */
+static uint32_t s5pc1xx_pwm_gpio_read(void *opaque,
+                                      int io_index)
+{
+    S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+    uint8_t i, tout = 0;
+
+    if (s->tag < 255)
+        hw_error("s5pc1xx.pwm: gpio_read - wrong param (tag = %u)\n",
+                 s->tag);
+
+    for (i = 0; i < 4; i++)
+        if (io_index == GPIO_PWM_TOUT(i)) {
+            tout = (s->tcon & S5C_TCON_INVERT[i]) ? 0 : 1;
+
+            if (s5pc1xx_pwm_convert_term(qemu_get_clock(vm_clock) - s->tmr[i].last_pwm_time,
+                s->tmr[i].freq_out) < s->tmr[i].tcmp)
+                tout = (s->tcon & S5C_TCON_INVERT[i]) ? 1 : 0;
+        }
+    return tout;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_pwm_gpio_readfn   = s5pc1xx_pwm_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_pwm_gpio_writefn = s5pc1xx_empty_gpio_write;    /* a plug */
+
+/* Read PWM by OS */
+static uint32_t s5pc1xx_pwm_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxPWMState *s        = (S5pc1xxPWMState *)opaque;
+    S5pc1xxPWMTimerState *tmr = s->tmr;
+    uint32_t tcnto;
+    uint8_t  num;
+
+    if (s->tag < 255)
+        hw_error("s5pc1xx.pwm: read - wrong param (tag = %u)\n",
+                 s->tag);
+
+    switch (offset) {
+        case S5C_TCFG0:
+            return s->tcfg0;
+
+        case S5C_TCFG1:
+            return s->tcfg1;
+
+        case S5C_TCON:
+            return s->tcon;
+
+        case S5C_TINT_CSTAT:
+            return s->tint_cstat;
+
+        case S5C_TCNTB(0):
+        case S5C_TCNTB(1):
+        case S5C_TCNTB(2):
+        case S5C_TCNTB(3):
+        case S5C_TCNTB(4):
+            return tmr[(offset - S5C_TCNTB(0)) / 0x0C].tcntb;
+
+        case S5C_TCMPB(0):
+        case S5C_TCMPB(1):
+        case S5C_TCMPB(2):
+        case S5C_TCMPB(3):
+            return tmr[(offset - S5C_TCMPB(0)) / 0x0C].tcmpb;
+
+        case S5C_TCNTO(0):
+        case S5C_TCNTO(1):
+        case S5C_TCNTO(2):
+        case S5C_TCNTO(3):
+        case S5C_TCNTO(4):
+            num =
+                (offset == S5C_TCNTO(4)) ? 4 : ((offset - S5C_TCNTO(0)) / 0x0C);
+
+            if (!(s->tcon & S5C_TCON_START[num]))
+                return tmr[num].tcnt;
+
+            if (qemu_get_clock(vm_clock) - tmr[num].last_pwm_time > tmr[num].tcnt_in_qemu)
+                return 0;
+
+            tcnto =
+                tmr[num].tcnt -
+                s5pc1xx_pwm_convert_term(qemu_get_clock(vm_clock) -
+                                         tmr[num].last_pwm_time,
+                                         tmr[num].freq_out);
+            return tcnto;
+
+        default:
+            hw_error("s5pc1xx.pwm: bad read offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+/* Write PWM by OS */
+static void s5pc1xx_pwm_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxPWMState *s        = (S5pc1xxPWMState *)opaque;
+    S5pc1xxPWMTimerState *tmr = s->tmr;
+    uint8_t num;
+
+    if (s->tag < 255)
+        hw_error("s5pc1xx.pwm: write - wrong param (tag = %u)\n",
+                 s->tag);
+
+    switch (offset) {
+        case S5C_TCFG0:
+            s->tcfg0 = value;
+            for (num = 0; num < 5; num++)
+                s5pc1xx_pwm_timer_freq(&tmr[num]);
+            break;
+
+        case S5C_TCFG1:
+            s->tcfg1 = value;
+            for (num = 0; num < 5; num++)
+                s5pc1xx_pwm_timer_freq(&tmr[num]);
+            break;
+
+        case S5C_TCON:
+            /* start timer if it was stopped */
+            for (num = 0; num < 5; num++) {
+                if ((value & S5C_TCON_START[num]) >
+                    (s->tcon & S5C_TCON_START[num]))
+                    s5pc1xx_pwm_timer_start(&tmr[num]);
+
+                if ((value & S5C_TCON_START[num]) <
+                    (s->tcon & S5C_TCON_START[num]))
+                    s5pc1xx_pwm_timer_stop(&tmr[num]);
+
+                if (value & S5C_TCON_MANUALUPD[num]) {
+                    s5pc1xx_pwm_timer_stop(&tmr[num]);
+                    s5pc1xx_pwm_timer_renew(&tmr[num]);
+                }
+            }
+            s->tcon = value;
+            break;
+
+        case S5C_TINT_CSTAT:
+            for (num = 0; num < 5; num++)
+                if (value & INT_STAT(num))
+                    qemu_irq_lower(tmr[num].irq_inst);
+            /* set TINT_CSTAT as value except *_STAT bits */
+            s->tint_cstat =
+                (s->tint_cstat & ALL_STAT) | (value & ~ALL_STAT);
+            /* clear *_STAT bits if they are set in value */
+            s->tint_cstat &= ~(value & ALL_STAT);
+            break;
+
+        case S5C_TCNTB(0):
+        case S5C_TCNTB(1):
+        case S5C_TCNTB(2):
+        case S5C_TCNTB(3):
+        case S5C_TCNTB(4):
+            num = (offset - S5C_TCNTB(0)) / 0x0C;
+            /* count buffer */
+            tmr[num].tcntb = value;
+            tmr[num].tcntb_in_qemu =
+                muldiv64(value, get_ticks_per_sec(), tmr[num].freq_out);
+            break;
+
+        case S5C_TCMPB(0):
+        case S5C_TCMPB(1):
+        case S5C_TCMPB(2):
+        case S5C_TCMPB(3):
+            /* comparison buffer */
+            tmr[(offset - S5C_TCMPB(0)) / 0x0C].tcmpb = value;
+            break;
+
+        default:
+            hw_error("s5pc1xx.pwm: bad write offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pwm_readfn[] = {
+   s5pc1xx_pwm_read,
+   s5pc1xx_pwm_read,
+   s5pc1xx_pwm_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pwm_writefn[] = {
+   s5pc1xx_pwm_write,
+   s5pc1xx_pwm_write,
+   s5pc1xx_pwm_write
+};
+
+static void s5pc1xx_pwm_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+    S5pc1xxPWMTimerState *tmr = s->tmr;
+    uint64_t last;
+    int num;
+
+    qemu_put_8s   (f, &s->tag);
+    qemu_put_be32s(f, &s->tcfg0);
+    qemu_put_be32s(f, &s->tcfg1);
+    qemu_put_be32s(f, &s->tcon);
+    qemu_put_be32s(f, &s->tint_cstat);
+
+    for (num = 0; num < 5; num++) {
+        last = qemu_get_clock(vm_clock) - tmr[num].last_pwm_time;
+
+        qemu_put_8s   (f, &tmr[num].tag);
+        qemu_put_be32s(f, &tmr[num].freq_out);
+        qemu_put_be32s(f, &tmr[num].tcntb);
+        qemu_put_be64s(f, &tmr[num].tcntb_in_qemu);
+        qemu_put_be32s(f, &tmr[num].tcnt);
+        qemu_put_be64s(f, &tmr[num].tcnt_in_qemu);
+        qemu_put_be32s(f, &tmr[num].tcmpb);
+        qemu_put_be32s(f, &tmr[num].tcmp);
+        qemu_put_be64s(f, &last);
+
+        qemu_put_timer(f, tmr[num].pwm_timer);
+    }
+}
+
+static int s5pc1xx_pwm_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+    S5pc1xxPWMTimerState *tmr = s->tmr;
+    uint64_t last;
+    int num;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_8s   (f, &s->tag);
+    qemu_get_be32s(f, &s->tcfg0);
+    qemu_get_be32s(f, &s->tcfg1);
+    qemu_get_be32s(f, &s->tcon);
+    qemu_get_be32s(f, &s->tint_cstat);
+
+    for (num = 0; num < 5; num++) {
+        qemu_get_8s   (f, &tmr[num].tag);
+        qemu_get_be32s(f, &tmr[num].freq_out);
+        qemu_get_be32s(f, &tmr[num].tcntb);
+        qemu_get_be64s(f, &tmr[num].tcntb_in_qemu);
+        qemu_get_be32s(f, &tmr[num].tcnt);
+        qemu_get_be64s(f, &tmr[num].tcnt_in_qemu);
+        qemu_get_be32s(f, &tmr[num].tcmpb);
+        qemu_get_be32s(f, &tmr[num].tcmp);
+        qemu_get_be64s(f, &last);
+
+        tmr[num].last_pwm_time = qemu_get_clock(vm_clock) - last;
+
+        qemu_get_timer(f, tmr[num].pwm_timer);
+    }
+
+    return 0;
+}
+
+/* PWM Init */
+static int s5pc1xx_pwm_init(SysBusDevice *dev)
+{
+    int i, iomemtype;
+    S5pc1xxPWMState *s        = FROM_SYSBUS(S5pc1xxPWMState, dev);
+    S5pc1xxPWMTimerState *tmr = s->tmr;
+
+    s->tag = 255;
+
+    for (i = 0; i < 5; i++) {
+        tmr[i].tag      = i;
+        tmr[i].regs     = s;
+        sysbus_init_irq(dev, &tmr[i].irq_inst);
+        tmr[i].pwm_timer =
+            qemu_new_timer(vm_clock, s5pc1xx_pwm_timer_expiry, &tmr[i]);
+    }
+
+    s5pc1xx_pwm_write(s, S5C_TCFG0, 0x00000101);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_pwm_readfn, s5pc1xx_pwm_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_PWM_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_PWM, 0, s5pc1xx_pwm_gpio_readfn,
+                                    s5pc1xx_pwm_gpio_writefn, NULL, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.pwm", -1, 1,
+                    s5pc1xx_pwm_save, s5pc1xx_pwm_load, s);
+    return 0;
+}
+
+static void s5pc1xx_pwm_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.pwm", sizeof(S5pc1xxPWMState),
+                        s5pc1xx_pwm_init);
+}
+
+device_init(s5pc1xx_pwm_register_devices)
diff --git a/hw/s5pc1xx_rtc.c b/hw/s5pc1xx_rtc.c
new file mode 100644 (file)
index 0000000..83c621a
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Real Time Clock for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+
+/* Registers addresses */
+
+#define INT_PEND    0x30    /* R/W Interrupt Pending Register */
+#define RTC_CON     0x40    /* R/W RTC Control Register */
+#define TIC_CNT     0x44    /* R/W Tick Time Count Register */
+#define RTC_ALM     0x50    /* R/W RTC Alarm Control Register */
+#define ALM_SEC     0x54    /* R/W Alarm Second Data Register */
+#define ALM_MIN     0x58    /* R/W Alarm Minute Data Register */
+#define ALM_HOUR    0x5C    /* R/W Alarm Hour Data Register */
+#define ALM_DAY     0x60    /* R/W Alarm Day of the month Data Register */
+#define ALM_MON     0x64    /* R/W Alarm Month Data Register */
+#define ALM_YEAR    0x68    /* R/W Alarm Year Data Register */
+#define BCD_SEC     0x70    /* R/W BCD Second Register */
+#define BCD_MIN     0x74    /* R/W BCD Minute Register */
+#define BCD_HOUR    0x78    /* R/W BCD Hour Register */
+#define BCD_DAY     0x7C    /* R/W BCD value for a day of the month (1-31) */
+#define BCD_WEEKDAY 0x80    /* R/W BCD value for a day of the week (1-7) */
+#define BCD_MON     0x84    /* R/W BCD Month Register */
+#define BCD_YEAR    0x88    /* R/W BCD Year Register */
+#define CUR_TIC_CNT 0x90    /* R Current Tick Time Counter Register */
+
+/* Bit mapping for INT_PEND register */
+
+#define INT_ALM     0x02    /* Alarm interrupt pending bit */
+#define INT_TIC     0x01    /* Time TIC interrupt pending bit */
+
+/* Bit mapping for RTC_CON register */
+
+#define TIC_EN      0x100   /* Tick timer enable */
+#define TIC_CK_SEL_SHIFT (4)/* Tick timer sub clock selection */
+#define CLK_RST     0x08    /* RTC clock count reset */
+#define CNT_SEL     0x04    /* BCD count select */
+#define CLK_SEL     0x02    /* BCD clock select */
+#define RTC_EN      0x01    /* RTC control enable */
+
+/* Bit mapping for RTC_ALM register */
+
+#define ALM_EN      0x40    /* Alarm global enable */
+#define YEAR_EN     0x20    /* Year alarm enable */
+#define MON_EN      0x10    /* Month alarm enable */
+#define DAY_EN      0x08    /* Day of the month alarm enable */
+#define HOUR_EN     0x04    /* Hour alarm enable */
+#define MIN_EN      0x02    /* Minute alarm enable */
+#define SEC_EN      0x01    /* Second alarm enable */
+
+#define S5PC1XX_RTC_REG_MEM_SIZE 0x94
+
+
+typedef struct S5pc1xxRTCState {
+    SysBusDevice busdev;
+
+    uint32_t  regs[BCD_YEAR + 1];
+
+    /* periodic timer */
+    QEMUTimer *periodic_timer;
+    uint32_t  freq_out;
+    uint64_t  last_tick;
+    uint64_t  cur_tic_cnt;
+    qemu_irq  tick_irq;
+
+    /* second update */
+    QEMUTimer *second_timer;
+    struct tm current_tm;
+    qemu_irq  alm_irq;
+
+} S5pc1xxRTCState;
+
+
+static void s5pc1xx_rtc_periodic_tick(void *opaque);
+static void s5pc1xx_rtc_second_update(void *opaque);
+
+static void s5pc1xx_rtc_set_time(S5pc1xxRTCState *s)
+{
+    struct tm *tm = &(s->current_tm);
+
+    tm->tm_sec = from_bcd(s->regs[BCD_SEC] & 0x7F);
+    tm->tm_min = from_bcd(s->regs[BCD_MIN] & 0x7F);
+    tm->tm_hour = from_bcd(s->regs[BCD_HOUR] & 0x3F);
+    tm->tm_wday = from_bcd(s->regs[BCD_WEEKDAY] & 0x07);
+    tm->tm_mday = from_bcd(s->regs[BCD_DAY] & 0x3F);
+    /* month value in qemu is between 0 and 11 */
+    tm->tm_mon = from_bcd(s->regs[BCD_MON] & 0x1F) - 1;
+    /* year value is with base_year = 2000 (not 1900 as in qemu) */
+    tm->tm_year = 100 + from_bcd(s->regs[BCD_YEAR] & 0xFF) +
+                  from_bcd((s->regs[BCD_YEAR] >> 8) & 0xF) * 100;
+}
+
+static void s5pc1xx_rtc_read_time(S5pc1xxRTCState *s)
+{
+    const struct tm *tm = &(s->current_tm);
+
+    s->regs[BCD_SEC] = to_bcd(tm->tm_sec);
+    s->regs[BCD_MIN] = to_bcd(tm->tm_min);
+    s->regs[BCD_HOUR] = to_bcd(tm->tm_hour);
+    s->regs[BCD_WEEKDAY] = to_bcd(tm->tm_wday);
+    s->regs[BCD_DAY] = to_bcd(tm->tm_mday);
+    /* month value in qemu is between 0 and 11 */
+    s->regs[BCD_MON] = to_bcd(tm->tm_mon + 1);
+    /* year value is with base_year = 2000 (not 1900 as in qemu) */
+    s->regs[BCD_YEAR] = to_bcd((tm->tm_year - 100) % 100) |
+                        (to_bcd((tm->tm_year - 100) % 1000 / 100) << 8);
+}
+
+/* set default values for all fields */
+static void s5pc1xx_rtc_reset(S5pc1xxRTCState *s)
+{
+    short i;
+
+    /* stop tick timer */
+    s->regs[RTC_CON] &= ~TIC_EN;
+    s5pc1xx_rtc_periodic_tick(s);
+
+    /* stop second timer */
+    s->regs[RTC_CON] &= ~RTC_EN;
+    s5pc1xx_rtc_second_update(s);
+
+    for (i = 0x30; i < 0x90; i += 0x4) {
+        s->regs[i] = 0;
+    }
+
+    s->last_tick = 0;
+    s->freq_out  = 0;
+}
+
+/* Setup next timer tick */
+static void s5pc1xx_rtc_periodic_update(S5pc1xxRTCState *s)
+{
+    uint64_t next_periodic_time, last = qemu_get_clock(vm_clock);
+    s->last_tick = last;
+    next_periodic_time =
+        last + muldiv64(s->regs[TIC_CNT], get_ticks_per_sec(), s->freq_out);
+    qemu_mod_timer(s->periodic_timer, next_periodic_time);
+}
+
+/* Send periodic interrupt */
+static void s5pc1xx_rtc_periodic_tick(void *opaque)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+    /* tick actually happens not every CUR_TIC_CNT but rather at irq time;
+     * if current ticnto value is needed it is calculated in s5pc1xx_rtc_read */
+    if (s->regs[RTC_CON] & TIC_EN) {
+        qemu_irq_raise(s->tick_irq);
+        s->regs[INT_PEND] |= INT_TIC;
+        s5pc1xx_rtc_periodic_update(s);
+    } else {
+        s->last_tick = 0;
+        qemu_del_timer(s->periodic_timer);
+    }
+}
+
+/* Update tick timer frequency */
+static void s5pc1xx_rtc_periodic_rate(S5pc1xxRTCState *s)
+{
+    short freq_code;
+
+    /* get four binary digits to determine the frequency */
+    freq_code = (s->regs[RTC_CON] >> TIC_CK_SEL_SHIFT) & 0xF;
+
+    /* tick timer frequency */
+    s->freq_out = 32768 / (1 << freq_code);
+}
+
+/* Month is between 0 and 11 */
+static int get_days_in_month(int month, int year)
+{
+    short d;
+    static const int days_tab[12] = {
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+
+    if ((unsigned) month >= 12) {
+        return 31;
+    }
+    d = days_tab[month];
+    if (month == 1) {
+        if ((year % 4) == 0) {
+            d++;
+        }
+    }
+    return d;
+}
+
+/* Update 'tm' to the next second */
+static void s5pc1xx_rtc_next_second(struct tm *tm)
+{
+    int days_in_month;
+
+    tm->tm_sec++;
+    if ((unsigned) tm->tm_sec >= 60) {
+        tm->tm_sec = 0;
+        tm->tm_min++;
+        if ((unsigned) tm->tm_min >= 60) {
+            tm->tm_min = 0;
+            tm->tm_hour++;
+            if ((unsigned) tm->tm_hour >= 24) {
+                tm->tm_hour = 0;
+                /* next day */
+                tm->tm_wday++;
+                /* TODO: check if wday value in qemu is between 1 and 7 */
+                if ((unsigned) tm->tm_wday >= 8) {
+                    tm->tm_wday = 1;
+                }
+                days_in_month = get_days_in_month(tm->tm_mon, tm->tm_year);
+                tm->tm_mday++;
+                if (tm->tm_mday < 1) {
+                    tm->tm_mday = 1;
+                } else if (tm->tm_mday > days_in_month) {
+                    tm->tm_mday = 1;
+                    tm->tm_mon++;
+                    if (tm->tm_mon >= 12) {
+                        tm->tm_mon = 0;
+                        tm->tm_year++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+/* Working with up-to-date time and check alarm */
+static void s5pc1xx_rtc_second_update(void *opaque)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+    uint64_t next_second_time;
+
+    if (s->regs[RTC_CON] & RTC_EN) {
+        /* check if the alarm is generally enabled */
+        /* then check if an alarm of at least one kind is enabled */
+        if (s->regs[RTC_ALM] & ALM_EN &&
+            (s->regs[RTC_ALM] & SEC_EN ||
+             s->regs[RTC_ALM] & MIN_EN ||
+             s->regs[RTC_ALM] & HOUR_EN ||
+             s->regs[RTC_ALM] & DAY_EN ||
+             s->regs[RTC_ALM] & MON_EN ||
+             s->regs[RTC_ALM] & YEAR_EN)) {
+
+            /* check alarm values together with corresponding permissive bits */
+            if (((s->regs[ALM_SEC] & 0x7F) ==
+                    to_bcd(s->current_tm.tm_sec) ||
+                 !(s->regs[RTC_ALM] & SEC_EN)) &&
+
+                ((s->regs[ALM_MIN] & 0x7F) ==
+                    to_bcd(s->current_tm.tm_min) ||
+                 !(s->regs[RTC_ALM] & MIN_EN)) &&
+
+                ((s->regs[ALM_HOUR] & 0x3F) ==
+                    to_bcd(s->current_tm.tm_hour) ||
+                 !(s->regs[RTC_ALM] & HOUR_EN)) &&
+
+                ((s->regs[ALM_DAY] & 0x3F) ==
+                    to_bcd(s->current_tm.tm_mday) ||
+                 !(s->regs[RTC_ALM] & DAY_EN)) &&
+
+                ((s->regs[ALM_MON]  & 0x1F) ==
+                    to_bcd(s->current_tm.tm_mon) ||
+                 !(s->regs[RTC_ALM] & MON_EN)) &&
+
+                (((s->regs[ALM_YEAR] & 0xFF) ==
+                    to_bcd((s->current_tm.tm_year - 100) % 100) &&
+                  ((s->regs[ALM_YEAR] >> 8) & 0xF) ==
+                    to_bcd((s->current_tm.tm_year - 100) % 1000 / 100)) ||
+                 !(s->regs[RTC_ALM] & YEAR_EN))) {
+
+                qemu_irq_raise(s->alm_irq);
+                s->regs[INT_PEND] |= INT_ALM;
+            }
+        }
+
+        s5pc1xx_rtc_next_second(&s->current_tm);
+        next_second_time = qemu_get_clock(vm_clock) + get_ticks_per_sec();
+        qemu_mod_timer(s->second_timer, next_second_time);
+    } else {
+        qemu_del_timer(s->second_timer);
+    }
+}
+
+static uint32_t s5pc1xx_rtc_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+    switch (offset) {
+        case BCD_SEC:
+        case BCD_MIN:
+        case BCD_HOUR:
+        case BCD_WEEKDAY:
+        case BCD_DAY:
+        case BCD_MON:
+        case BCD_YEAR:
+            s5pc1xx_rtc_read_time(s);
+            /* fallthrough */
+        case INT_PEND:
+        case RTC_CON:
+        case TIC_CNT:
+        case RTC_ALM:
+        case ALM_SEC:
+        case ALM_MIN:
+        case ALM_HOUR:
+        case ALM_DAY:
+        case ALM_MON:
+        case ALM_YEAR:
+            return s->regs[offset];
+        case CUR_TIC_CNT:
+            if (s->freq_out && s->last_tick && s->regs[TIC_CNT]) {
+                s->cur_tic_cnt = s->regs[TIC_CNT] -
+                    muldiv64(qemu_get_clock(vm_clock) - s->last_tick,
+                             s->freq_out, get_ticks_per_sec()) %
+                             s->regs[TIC_CNT];
+            } else {
+                s->cur_tic_cnt = 0;
+            }
+
+            return s->cur_tic_cnt;
+        default:
+            hw_error("s5pc1xx.rtc: bad read offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+static void s5pc1xx_rtc_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+    switch (offset) {
+        case INT_PEND:
+            /* lower interrupts if any */
+            if (value & INT_TIC) {
+                qemu_irq_lower(s->tick_irq);
+            }
+            if (value & INT_ALM) {
+                qemu_irq_lower(s->alm_irq);
+            }
+
+            /* clear INT_* bits if they are set in value */
+            s->regs[INT_PEND] &= ~(value & (INT_TIC | INT_ALM));
+            break;
+        case RTC_CON:
+            /* reset tick counter */
+            if (value & CLK_RST) {
+                s5pc1xx_rtc_reset(s);
+                value &= ~CLK_RST;
+            }
+            /* start second timer */
+            if ((value & RTC_EN) > (s->regs[RTC_CON] & RTC_EN)) {
+                s->regs[RTC_CON] |= RTC_EN;
+                qemu_get_timedate(&s->current_tm, 0);
+                s5pc1xx_rtc_second_update(s);
+            }
+            /* start tick timer */
+            if ((value & TIC_EN) > (s->regs[RTC_CON] & TIC_EN)) {
+                s->regs[RTC_CON] = value;
+                s5pc1xx_rtc_periodic_rate(s);
+                s5pc1xx_rtc_periodic_update(s);
+                break;
+            } else if ((value & TIC_EN) < (s->regs[RTC_CON] & TIC_EN)) {
+                qemu_del_timer(s->periodic_timer);
+            }
+            s->regs[RTC_CON] = value;
+            s5pc1xx_rtc_periodic_rate(s);
+            break;
+        case TIC_CNT:
+        case RTC_ALM:
+        case ALM_SEC:
+        case ALM_MIN:
+        case ALM_HOUR:
+        case ALM_DAY:
+        case ALM_MON:
+        case ALM_YEAR:
+            s->regs[offset] = value;
+            break;
+        case BCD_SEC:
+        case BCD_MIN:
+        case BCD_HOUR:
+        case BCD_WEEKDAY:
+        case BCD_DAY:
+        case BCD_MON:
+        case BCD_YEAR:
+            s->regs[offset] = value;
+            /* if in disabled mode, do not update the time */
+            if (s->regs[RTC_CON] & RTC_EN) {
+                s5pc1xx_rtc_set_time(s);
+            }
+            break;
+        default:
+            hw_error("s5pc1xx.rtc: bad write offset " TARGET_FMT_plx "\n",
+                     offset);
+    }
+}
+
+/* Memory mapped interface */
+static CPUReadMemoryFunc * const s5pc1xx_rtc_mm_read[] = {
+    s5pc1xx_rtc_read,
+    s5pc1xx_rtc_read,
+    s5pc1xx_rtc_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_rtc_mm_write[] = {
+    s5pc1xx_rtc_write,
+    s5pc1xx_rtc_write,
+    s5pc1xx_rtc_write
+};
+
+static void s5pc1xx_rtc_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+    int i;
+
+    s5pc1xx_rtc_read_time(s);
+    for (i = INT_PEND; i <= BCD_YEAR; i++) {
+        qemu_put_be32s(f, &s->regs[i]);
+    }
+    qemu_put_timer(f, s->second_timer);
+
+    qemu_put_be64s(f, &s->last_tick);
+    qemu_put_be32s(f, &s->freq_out);
+    qemu_put_timer(f, s->periodic_timer);
+}
+
+static int s5pc1xx_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (i = INT_PEND; i <= BCD_YEAR; i++) {
+        qemu_get_be32s(f, &s->regs[i]);
+    }
+
+    //s5pc1xx_rtc_set_time(s);
+    qemu_get_timedate(&s->current_tm, 0);
+    s5pc1xx_rtc_read_time(s);
+    qemu_get_timer(f, s->second_timer);
+
+    qemu_get_be64s(f, &s->last_tick);
+    qemu_get_be32s(f, &s->freq_out);
+    qemu_get_timer(f, s->periodic_timer);
+
+    return 0;
+}
+
+/* initialize and start timers */
+static int s5pc1xx_rtc_init(SysBusDevice *dev)
+{
+    int iomemory;
+    S5pc1xxRTCState *s = FROM_SYSBUS(S5pc1xxRTCState, dev);
+
+    sysbus_init_irq(dev, &s->alm_irq);
+    sysbus_init_irq(dev, &s->tick_irq);
+
+    s->periodic_timer =
+        qemu_new_timer(vm_clock, s5pc1xx_rtc_periodic_tick, s);
+    s->second_timer =
+        qemu_new_timer(vm_clock, s5pc1xx_rtc_second_update, s);
+
+    iomemory =
+        cpu_register_io_memory(s5pc1xx_rtc_mm_read, s5pc1xx_rtc_mm_write, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_RTC_REG_MEM_SIZE, iomemory);
+
+    s5pc1xx_rtc_reset(s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.rtc", -1, 1,
+                    s5pc1xx_rtc_save, s5pc1xx_rtc_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_rtc_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.rtc", sizeof(S5pc1xxRTCState),
+                        s5pc1xx_rtc_init);
+}
+
+device_init(s5pc1xx_rtc_register_devices)
diff --git a/hw/s5pc1xx_spdif.c b/hw/s5pc1xx_spdif.c
new file mode 100644 (file)
index 0000000..d34dbf2
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ * SPDIF transmitter for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+#define ALL_BITS(b,a)               (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the Clock control register 0x0000_0002 */
+#define SPDCLKCON       0x00
+
+    #define MAIN_CLK_SEL                (1 << 2)
+    #define CLK_DWN_READY               (1 << 1)
+    #define POWER_ON                    (1 << 0)
+
+/* R/W Specifies the Control register 0x0000_0000 */
+#define SPDCON          0x04
+
+    #define FIFO_LEVEL_SHIFT            22      /* 5 bits */
+    #define FIFO_LEVEL_THR_SHIFT        19      /* 3 bits */
+    #define FIFO_TRANSFER_MODE_SHIFT    17      /* 2 bits */
+    #define FIFO_LEVEL_INT_ST           (1 << 16)
+    #define FIFO_LEVEL_INT_EN           (1 << 15)
+    #define ENDIAN_FORMAT_SHIFT         13      /* 2 bits */
+    #define USER_DATA_ATTACH            (1 << 12)
+    #define USER_DATA_INT_ST            (1 << 11)
+    #define USER_DATA_INT_EN            (1 << 10)
+    #define BUF_EMPTY_INT_ST            (1 << 9)
+    #define BUF_EMPTY_INT_EN            (1 << 8)
+    #define STREAM_END_INT_ST           (1 << 7)
+    #define STREAM_END_INT_EN           (1 << 6)
+    #define SOFTWARE_RESET              (1 << 5)
+    #define MAIN_CLK_FREQ_SHIFT         3       /* 2 bits */
+    #define PCM_DATA_SIZE_SHIFT         1       /* 2 bits */
+    /* TODO: Stream data coding is not described in the documentation.
+     * So, this is not implemented so far. */
+    #define PCM_OR_STREAM               (1 << 0)
+
+/* R/W Specifies the Burst status register 0x0000_0000 */
+#define SPDBSTAS        0x08
+
+    #define BURST_DATA_L_BIT_SHIFT      16      /* 16 bits */
+    #define BITSTREAM_NUMBER_SHIFT      13      /* 3 bits */
+    #define DATA_TYPE_DEP_INFO_SHIFT    8       /* 5 bits */
+    #define ERROR_FLAG                  (1 << 7)
+    #define COMPR_DATA_TYPE_SHIFT       0       /* 5 bits */
+
+/* R/W Specifies the Channel status register 0x0000_0000 */
+#define SPDCSTAS        0x0C
+
+    #define CLK_ACCURACY_SHIFT          28      /* 2 bits */
+    #define SAMP_FREQ_SHIFT             24      /* 4 bits */
+    #define CH_NUM_SHIFT                20      /* 4 bits */
+    #define SRC_NUM_SHIFT               16      /* 4 bits */
+    #define CAT_CODE_SHIFT              8       /* 8 bits */
+    #define CH_STAT_MODE_SHIFT          6       /* 2 bits */
+    #define EMPHASIS_SHIFT              3       /* 3 bits */
+    #define COPYRIGHT_ASSERTION         (1 << 2)
+    #define AUDIO_WORD                  (1 << 1)
+    #define CH_STAT_BLOCK               (1 << 0)
+
+/* W Specifies the SPDIFOUT data buffer 0x0000_0000 */
+#define SPDDAT          0x10
+
+/* R/W Specifies the Repetition count register 0x0000_0000 */
+#define SPDCNT          0x14
+
+/* R Specifies the Shadowed Burst Status Register 0x0000_0000 */
+#define SPDBSTAS_SHD    0x18
+
+/* R Specifies the Shadowed Repetition Count Register 0x0000_0000 */
+#define SPDCNT_SHD      0x1C
+
+/* R/W Specifies the Subcode Q1 ~ Q32 0x0000_0000 */
+#define USERBIT1        0x20
+
+/* R/W Specifies the Subcode Q33 ~ Q64 0x0000_0000 */
+#define USERBIT2        0x24
+
+/* R/W Specifies the Subcode Q65 ~ Q96 0x0000_0000 */
+#define USERBIT3        0x28
+
+/* R Specifies the Shadowed Register Userbit1 0x0000_0000 */
+#define USERBIT1_SHD    0x2C
+
+/* R Specifies the Shadowed Register Userbit2 0x0000_0000 */
+#define USERBIT2_SHD    0x30
+
+/* R Specifies the Shadowed Register Userbit3 0x0000_0000 */
+#define USERBIT3_SHD    0x34
+
+/* R Specifies the RTL Version Information 0x0000_000C */
+#define VERSION_INFO    0x38
+
+#define S5PC1XX_SPDIF_REG_MEM_SIZE  0x3C
+
+#define M_PREAMBLE      1
+#define B_PREAMBLE      2
+#define W_PREAMBLE      3
+
+#define FIFO_DEPTH      (s->write_idx - s->read_idx)
+#define INT_EN(stat)    (stat >> 1)
+#define ALL_STAT        (STREAM_END_INT_ST | BUF_EMPTY_INT_ST | \
+                         USER_DATA_INT_ST  | FIFO_LEVEL_INT_ST)
+
+
+typedef struct S5pc1xxSpdifState {
+    SysBusDevice busdev;
+    qemu_irq     irq;
+
+    uint32_t  spdclkcon;
+    uint32_t  spdcon;
+    uint32_t  spdbstas;
+    uint32_t  spdcstas;
+    uint32_t  spdcnt;
+    uint32_t  userbit[3];
+    uint32_t  version_info;
+
+    struct shadowed_regs {
+        uint32_t  spdbstas;
+        uint32_t  spdcnt;
+        uint32_t  userbit[3];
+    } shd;
+
+    QEMUTimer *spdif_timer;
+    uint32_t  sclk_freq;
+    int64_t   count;                /* has signed type */
+    uint8_t   updated;
+
+    uint32_t  fifo[2][16];
+    uint64_t  read_idx, write_idx;
+    uint8_t   read_ch, write_ch;
+
+    uint32_t  sub_frame;
+    int8_t    fifo_thr, endian_f;   /* have signed type */
+    int16_t   Pa, Pb, Pc, Pd;       /* have signed type */
+    uint8_t   lsb_pos, non_linear_pcm, stat_bit[16];
+    uint8_t   first_state, second_state;
+    uint16_t  rep_period, data_sframe_num;
+} S5pc1xxSpdifState;
+
+
+/* Reset SPDIF */
+static void s5pc1xx_spdif_reset(void *opaque)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+    uint8_t i;
+
+    s->spdclkcon    = 0x2;
+    s->spdcon       = 0x1;
+    s->spdbstas     = 0;
+    s->spdcstas     = 0;
+    s->spdcnt       = 0;
+    s->shd.spdbstas = 0;
+    s->shd.spdcnt   = 0;
+    s->version_info = 0xC;
+
+    for (i = 0; i < 3; i++) {
+        s->userbit[i]     = 0;
+        s->shd.userbit[i] = 0;
+    }
+
+    s->sclk_freq    = 0;
+    s->count        = -1;
+
+    s->read_idx     = 0;
+    s->write_idx    = 0;
+    s->read_ch      = 0;
+    s->write_ch     = 0;
+
+    s->fifo_thr     = -1;
+    s->endian_f     = -1;
+    s->lsb_pos      = 0;
+    s->first_state  = 0;
+    s->second_state = 0;
+
+    s->Pa           = 0xF872;
+    s->Pb           = 0x4E1F;
+    s->Pc           = -1;
+    s->Pd           = -1;
+    s->data_sframe_num = 0;         /* used for non-linear PCM */
+}
+
+/* Interrupts handler */
+static void s5pc1xx_spdif_irq(S5pc1xxSpdifState *s,
+                              uint32_t stat, uint8_t to_clear)
+{
+    if (to_clear) {
+        s->spdcon &= ~(stat);
+        if (!(s->spdcon & ALL_STAT))    /* if all clear */
+            qemu_irq_lower(s->irq);
+    } else {
+        s->spdcon |= stat;
+        if (s->spdcon & INT_EN(stat))   /* if enabled */
+            qemu_irq_raise(s->irq);
+    }
+}
+
+/* -=FIFO HANDLING=- */
+
+/* Determine threshold FIFO depth */
+static void s5pc1xx_spdif_fifo_thr(S5pc1xxSpdifState *s)
+{
+    uint8_t thr[8]  = {0, 1, 4, 6, 10, 12, 14, 15};
+    uint8_t thr_idx = (s->spdcon >> FIFO_LEVEL_THR_SHIFT) & 0x7;
+
+    s->fifo_thr = thr[thr_idx];
+}
+
+/* Control FIFO level */
+static void s5pc1xx_spdif_fifo_control(S5pc1xxSpdifState *s)
+{
+    if (s->fifo_thr < 0)
+        s5pc1xx_spdif_fifo_thr(s);
+
+    if (FIFO_DEPTH > s->fifo_thr)
+        s5pc1xx_spdif_irq(s, FIFO_LEVEL_INT_ST, 0);
+
+    if (FIFO_DEPTH == 0)
+        s5pc1xx_spdif_irq(s, BUF_EMPTY_INT_ST, 0);
+}
+
+/* Determine endian format index */
+static void s5pc1xx_spdif_endian_idx(S5pc1xxSpdifState *s)
+{
+    s->endian_f = (s->spdcon >> ENDIAN_FORMAT_SHIFT) & 0x3;
+}
+
+/* Convert value according to current endian format */
+static uint32_t s5pc1xx_spdif_endian_format(S5pc1xxSpdifState *s,
+                                            uint32_t value)
+{
+    uint32_t ret_val = 0;
+
+    if (s->endian_f < 0)
+        s5pc1xx_spdif_endian_idx(s);
+
+    switch(s->endian_f) {
+    case 0:
+        ret_val = value & ALL_BITS(23, 0);
+        break;
+    case 1:
+        ret_val = (((value & ALL_BITS(15,  8)) <<  8) |
+                   ((value & ALL_BITS(23, 16)) >>  8) |
+                   ((value & ALL_BITS(31, 24)) >> 24));
+        break;
+    case 2:
+        ret_val = (((value & ALL_BITS( 7,  0)) << 16) |
+                   ((value & ALL_BITS(15,  8)) >>  0) |
+                   ((value & ALL_BITS(23, 16)) >> 16));
+        break;
+    case 3:
+        ret_val = (((value & ALL_BITS( 7,  0)) <<  8) |
+                   ((value & ALL_BITS(15,  8)) >>  8));
+        break;
+    }
+    return ret_val;
+}
+
+/* Get value from FIFO */
+static uint32_t s5pc1xx_spdif_fifo_read(S5pc1xxSpdifState *s)
+{
+    uint32_t ret_val = 0;
+
+    if (FIFO_DEPTH > 0) {
+        ret_val = s->fifo[s->read_ch][s->read_idx % 16];
+
+        if (s->read_ch == 1)
+            s->read_idx++;
+    }
+    s->read_ch = (s->read_ch + 1) % 2;
+    s5pc1xx_spdif_fifo_control(s);
+
+    return ret_val;
+}
+
+/* Put value into FIFO */
+static void s5pc1xx_spdif_fifo_write(S5pc1xxSpdifState *s, uint32_t value)
+{
+    value = s5pc1xx_spdif_endian_format(s, value);
+
+    if (FIFO_DEPTH < 16) {
+        s->fifo[s->write_ch][s->write_idx % 16] = value;
+
+        if (s->write_ch == 1)
+            s->write_idx++;
+    }
+    s->write_ch = (s->write_ch + 1) % 2;
+    s5pc1xx_spdif_fifo_control(s);
+}
+
+/* -=SPDIF MAIN LOGIC=- */
+
+/* Update burst params (for non-linear PCM) */
+static void s5pc1xx_spdif_stream_end(S5pc1xxSpdifState *s)
+{
+    s->shd.spdbstas = s->spdbstas;
+    s->shd.spdcnt   = s->spdcnt;
+
+    s->Pc = (s->shd.spdbstas >>  0) & ALL_BITS(15, 0);
+    s->Pd = (s->shd.spdbstas >> 16) & ALL_BITS(15, 0);
+    s->rep_period = (s->shd.spdcnt) & ALL_BITS(12, 0);
+
+    s5pc1xx_spdif_irq(s, STREAM_END_INT_ST, 0);
+}
+
+/* Spdif_tx block */
+static uint32_t s5pc1xx_spdif_tx_block(S5pc1xxSpdifState *s)
+{
+    uint32_t value;
+    uint16_t next_payload_size;
+
+    /* check for beginning of new payload frame */
+    if ((s->data_sframe_num > 3) && !(s->data_sframe_num % 2)) {
+        /* check if bit stream size will be past s->rep_period
+         * value after next write routine and avoid this */
+        if ((s->data_sframe_num + 2) * 16 > s->rep_period) {
+            s5pc1xx_spdif_stream_end(s);
+            s->data_sframe_num = 0;
+        }
+    }
+
+    next_payload_size =
+        (s->data_sframe_num > 3) ? (s->data_sframe_num - 4 + 2) * 16 : 0;
+
+    switch(s->data_sframe_num) {
+    case 0:
+        value = s->Pa;
+        break;
+    case 1:
+        value = s->Pb;
+        break;
+    case 2:
+        value = s->Pc;
+        break;
+    case 3:
+        value = s->Pd;
+        break;
+    default:
+        if (next_payload_size > s->Pd) {
+            value = 0;
+        } else {
+            value = s5pc1xx_spdif_fifo_read(s);
+        }
+    }
+    s->data_sframe_num++;
+
+    return value;
+}
+
+/* Determine LSB position within sub-frame */
+static void s5pc1xx_spdif_lsb(S5pc1xxSpdifState *s)
+{
+    uint8_t data_size;
+
+    if (s->non_linear_pcm) {
+        s->lsb_pos = 12;
+        return;
+    }
+    data_size = (((s->spdcon >> PCM_DATA_SIZE_SHIFT) & 0x3) + 4) << 2;
+    s->lsb_pos = 28 - data_size;
+}
+
+/* Convert audio data to SPDIF format
+ * (compose 0~31 time slots into s->sub_frame) */
+static void s5pc1xx_spdif_sub_frame(S5pc1xxSpdifState *s)
+{
+    uint32_t value;
+    uint8_t  ballast, preamble, v_flag, user_bit, parity_bit;
+    uint8_t  i, ones;
+    uint16_t carrier_sframe_num = (s->count / 64) % 384;
+
+    ballast = (s->second_state) ? 0 : 0x3;  /* 2b'00 or 2b'11 */
+
+    /* Determine preamble */
+    if (carrier_sframe_num % 2) {
+        /* channel 2 has odd sub-frames numbers and always has W-preamble */
+        preamble = W_PREAMBLE;
+    } else {
+        /* channel 1 has even sub-frames numbers and has M-preamble except
+         * the first sub-frame */
+        if (carrier_sframe_num) {
+            preamble = M_PREAMBLE;
+        } else {
+            preamble = B_PREAMBLE;
+        }
+    }
+
+    /* Determine audio sample word */
+    if (s->non_linear_pcm) {
+        value = s5pc1xx_spdif_tx_block(s);
+    } else {
+        value = s5pc1xx_spdif_fifo_read(s);
+    }
+
+    /* Validity flag, user bit and channel status */
+    v_flag = (value) ? 0 : 1;
+    /* TODO: User bit is always 0 for linear-PCM, but PCM user data is set
+     * for non-linear PCM in the registers USERBIT1~3(_SHD). The allocation
+     * of user data between two channels is unclear in the documentation.
+     * So, user data transmission is not inplemented so far. */
+    user_bit = 0;
+
+    /* Compose sub_frame without parity_bit */
+    if (!(s->lsb_pos))
+        s5pc1xx_spdif_lsb(s);
+
+    s->sub_frame = ballast | (preamble << 2) |
+                   (value << s->lsb_pos & ALL_BITS(27, s->lsb_pos)) |
+                   (v_flag << 28) | (user_bit << 29) |
+                   (s->stat_bit[carrier_sframe_num % 2] << 30);
+
+    /* Determine parity_bit */
+    ones = parity_bit = 0;
+    for (i = 4; i < 31; i++) {
+        if (s->sub_frame >> i & 0x1)
+            ones++;
+    }
+    if (ones % 2)
+        parity_bit = 1;
+
+    s->sub_frame |= (parity_bit << 31);
+}
+
+/* Channel coding of source signal */
+static void s5pc1xx_spdif_channel_coding(S5pc1xxSpdifState *s)
+{
+    uint8_t cur_pos = (s->count % 64) / 2;
+    uint8_t source_coding = s->sub_frame >> cur_pos & 0x1;
+
+    /* Ballast handling (ballast ensures one deference
+     * from bi-phase scheme in channel coded preamble) */
+    if (cur_pos < 2) {
+        s->first_state = s->second_state = source_coding;
+        return;
+    }
+
+    /* Preamble and data handling */
+    if (cur_pos < 3) {
+        /* the line below ensures one more deference
+         * from bi-phase scheme in channel coded preamble */
+        s->first_state = s->second_state;
+    } else {
+        s->first_state = !(s->second_state);
+    }
+
+    if (source_coding) {
+        s->second_state = !(s->first_state);
+    } else {
+        s->second_state = s->first_state;
+    }
+}
+
+/* -=TIMER HANDLING=- */
+
+/* Update timer frequency */
+static void s5pc1xx_spdif_sclk_update(S5pc1xxSpdifState *s)
+{
+    uint8_t freq_id = s->spdcstas >> SAMP_FREQ_SHIFT & 0xF;
+
+    switch (freq_id) {
+    case 0:
+        /* samplling_freq x 32 time slots x 2 channels x bi-phase mark */
+        s->sclk_freq = 44100 * 32 * 2 * 2;
+        break;
+    case 2:
+        s->sclk_freq = 48000 * 32 * 2 * 2;
+        break;
+    case 3:
+        s->sclk_freq = 32000 * 32 * 2 * 2;
+        break;
+    case 10:
+        s->sclk_freq = 96000 * 32 * 2 * 2;
+        break;
+    default:
+        hw_error("s5pc1xx.spdif: frequency id %u is not supported\n", freq_id);
+    }
+}
+
+/* Sync timer engine */
+static void s5pc1xx_spdif_sync(void *opaque)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+    uint64_t next_spdif_time;
+
+    if (s->spdclkcon & POWER_ON) {
+        if (!(s->sclk_freq))
+            s5pc1xx_spdif_sclk_update(s);
+
+        /* s->updated is used to avoid conflict between two functions
+         * in case of synchronous runs */
+        s->updated = 0;
+
+        s->count++;
+
+        if (!(s->count % 64))
+            s5pc1xx_spdif_sub_frame(s);
+
+        if (!(s->count % 2))
+            s5pc1xx_spdif_channel_coding(s);
+
+        s->updated = 1;
+
+        next_spdif_time = qemu_get_clock(vm_clock) +
+                          muldiv64(1, get_ticks_per_sec(), s->sclk_freq);
+        qemu_mod_timer(s->spdif_timer, next_spdif_time);
+    } else {
+        s->spdclkcon |= CLK_DWN_READY;
+        qemu_del_timer(s->spdif_timer);
+    }
+}
+
+/* -=RELATION WITH GPIO AND OS=- */
+
+/* Read SPDIF by GPIO */
+static uint32_t s5pc1xx_spdif_gpio_read(void *opaque, int io_index)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+    if (io_index == SPDIF_0_OUT) {
+        if (s->count % 2) {
+            return s->second_state;
+        } else {
+            if (s->updated) {
+                return s->first_state;
+            } else {
+                return s->second_state;
+            }
+        }
+    }
+    return 0;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_spdif_gpio_readfn   = s5pc1xx_spdif_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_spdif_gpio_writefn = s5pc1xx_empty_gpio_write;
+
+/* Read SPDIF by OS */
+static uint32_t s5pc1xx_spdif_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+    switch(offset) {
+    case SPDCLKCON:
+        return s->spdclkcon;
+    case SPDCON:
+        s->spdcon = ((FIFO_DEPTH << FIFO_LEVEL_SHIFT) & ALL_BITS(26, 22)) |
+                    (s->spdcon & ALL_BITS(21 ,0));
+        return s->spdcon;
+    case SPDBSTAS:
+        return s->spdbstas;
+    case SPDCSTAS:
+        return s->spdcstas;
+    case SPDCNT:
+        return s->spdcnt;
+    case SPDBSTAS_SHD:
+        return s->shd.spdbstas;
+    case SPDCNT_SHD:
+        return s->shd.spdcnt;
+    case USERBIT1 ... USERBIT3:
+        return s->userbit[(offset - USERBIT1) / (USERBIT2 - USERBIT1)];
+    case USERBIT1_SHD ... USERBIT3_SHD:
+        return s->shd.userbit[(offset - USERBIT1_SHD) /
+                              (USERBIT2_SHD - USERBIT1_SHD)];
+    case VERSION_INFO:
+        return s->version_info;
+    default:
+        hw_error("s5pc1xx.spdif: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+/* Write SPDIF by OS */
+static void s5pc1xx_spdif_write(void *opaque, target_phys_addr_t offset,
+                                uint32_t value)
+{
+    uint32_t old_val;
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+    switch(offset) {
+    case SPDCLKCON:
+        old_val = s->spdclkcon;
+
+        /* Note: the CLK_DWN_READY field is read-only */
+        s->spdclkcon =
+            (value & (POWER_ON | MAIN_CLK_SEL)) | (old_val & CLK_DWN_READY);
+
+        if ((value & POWER_ON) > (old_val & POWER_ON)) {
+            s5pc1xx_spdif_stream_end(s);
+            s5pc1xx_spdif_sync(s);
+            s->spdclkcon &= ~CLK_DWN_READY;
+        }
+        break;
+    case SPDCON:
+        old_val = s->spdcon;
+
+        /* Note: the 'FIFO level' field is read-only */
+        s->spdcon = (old_val & ALL_BITS(26, 22)) | (value & ALL_BITS(21, 0));
+
+        /* Check 'FIFO level threshold' field for update */
+        if ((value & ALL_BITS(21, 19)) != (old_val & ALL_BITS(21, 19)))
+            s5pc1xx_spdif_fifo_thr(s);
+
+        /* Check 'Endian format' field for update */
+        if ((value & ALL_BITS(14, 13)) != (old_val & ALL_BITS(14, 13)))
+            s5pc1xx_spdif_endian_idx(s);
+
+        /* Check 'PCM data size' field for update */
+        if ((value & ALL_BITS(2, 1)) != (old_val & ALL_BITS(2, 1)))
+            s5pc1xx_spdif_lsb(s);
+
+        /* Clear irq states if any */
+        if (value & ALL_STAT)
+            s5pc1xx_spdif_irq(s, (value & ALL_STAT), 1);
+
+        break;
+    case SPDBSTAS:
+        s->spdbstas = value;
+        break;
+    case SPDCSTAS:
+        old_val = s->spdcstas;
+        s->spdcstas = value;
+
+        /* Check 'Sampling frequency' field for update */
+        if ((value & ALL_BITS(27, 24)) != (old_val & ALL_BITS(27, 24)))
+            s5pc1xx_spdif_sclk_update(s);
+
+        s->stat_bit[(value >> CH_NUM_SHIFT) & ALL_BITS(3, 0)] = value & 0x1;
+
+        s->non_linear_pcm = (value & AUDIO_WORD) ? 1 : 0;
+
+        break;
+    case SPDCNT:
+        s->spdcnt = value;
+        break;
+    case SPDDAT:
+        s5pc1xx_spdif_fifo_write(s, value);
+        break;
+    case USERBIT1 ... USERBIT3:
+        s->userbit[(offset - USERBIT1) / (USERBIT2 - USERBIT1)] = value;
+        break;
+    default:
+        hw_error("s5pc1xx.spdif: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_spdif_readfn[] = {
+   s5pc1xx_spdif_read,
+   s5pc1xx_spdif_read,
+   s5pc1xx_spdif_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_spdif_writefn[] = {
+   s5pc1xx_spdif_write,
+   s5pc1xx_spdif_write,
+   s5pc1xx_spdif_write
+};
+
+static void s5pc1xx_spdif_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->spdclkcon);
+    qemu_put_be32s(f, &s->spdcon);
+    qemu_put_be32s(f, &s->spdbstas);
+    qemu_put_be32s(f, &s->spdcstas);
+    qemu_put_be32s(f, &s->spdcnt);
+    qemu_put_be32s(f, &s->version_info);
+
+    for (i = 0; i < 3; i++) {
+        qemu_put_be32s(f, &s->userbit[i]);
+        qemu_put_be32s(f, &s->shd.userbit[i]);
+    }
+
+    qemu_put_be32s(f, &s->shd.spdbstas);
+    qemu_put_be32s(f, &s->shd.spdcnt);
+
+    qemu_put_be32s (f, &s->sclk_freq);
+    qemu_put_sbe64s(f, &s->count);
+    qemu_put_8s    (f, &s->updated);
+    qemu_put_be64s (f, &s->read_idx);
+    qemu_put_be64s (f, &s->write_idx);
+    qemu_put_8s    (f, &s->read_ch);
+    qemu_put_8s    (f, &s->write_ch);
+    qemu_put_be32s (f, &s->sub_frame);
+    qemu_put_s8s   (f, &s->fifo_thr);
+    qemu_put_s8s   (f, &s->endian_f);
+    qemu_put_sbe16s(f, &s->Pa);
+    qemu_put_sbe16s(f, &s->Pb);
+    qemu_put_sbe16s(f, &s->Pc);
+    qemu_put_sbe16s(f, &s->Pd);
+
+    for (i = 0; i < 16; i++) {
+        qemu_put_be32s(f, &s->fifo[0][i]);
+        qemu_put_be32s(f, &s->fifo[1][i]);
+        qemu_put_8s   (f, &s->stat_bit[i]);
+    }
+
+    qemu_put_8s (f, &s->lsb_pos);
+    qemu_put_8s (f, &s->non_linear_pcm);
+    qemu_put_8s (f, &s->first_state);
+    qemu_put_8s (f, &s->second_state);
+    qemu_put_be16s(f, &s->rep_period);
+    qemu_put_be16s(f, &s->data_sframe_num);
+
+    qemu_put_timer(f, s->spdif_timer);
+}
+
+static int s5pc1xx_spdif_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->spdclkcon);
+    qemu_get_be32s(f, &s->spdcon);
+    qemu_get_be32s(f, &s->spdbstas);
+    qemu_get_be32s(f, &s->spdcstas);
+    qemu_get_be32s(f, &s->spdcnt);
+    qemu_get_be32s(f, &s->version_info);
+
+    for (i = 0; i < 3; i++) {
+        qemu_get_be32s(f, &s->userbit[i]);
+        qemu_get_be32s(f, &s->shd.userbit[i]);
+    }
+
+    qemu_get_be32s(f, &s->shd.spdbstas);
+    qemu_get_be32s(f, &s->shd.spdcnt);
+
+    qemu_get_be32s (f, &s->sclk_freq);
+    qemu_get_sbe64s(f, &s->count);
+    qemu_get_8s    (f, &s->updated);
+    qemu_get_be64s (f, &s->read_idx);
+    qemu_get_be64s (f, &s->write_idx);
+    qemu_get_8s    (f, &s->read_ch);
+    qemu_get_8s    (f, &s->write_ch);
+    qemu_get_be32s (f, &s->sub_frame);
+    qemu_get_s8s   (f, &s->fifo_thr);
+    qemu_get_s8s   (f, &s->endian_f);
+    qemu_get_sbe16s(f, &s->Pa);
+    qemu_get_sbe16s(f, &s->Pb);
+    qemu_get_sbe16s(f, &s->Pc);
+    qemu_get_sbe16s(f, &s->Pd);
+
+    for (i = 0; i < 16; i++) {
+        qemu_get_be32s(f, &s->fifo[0][i]);
+        qemu_get_be32s(f, &s->fifo[1][i]);
+        qemu_get_8s   (f, &s->stat_bit[i]);
+    }
+
+    qemu_get_8s (f, &s->lsb_pos);
+    qemu_get_8s (f, &s->non_linear_pcm);
+    qemu_get_8s (f, &s->first_state);
+    qemu_get_8s (f, &s->second_state);
+    qemu_get_be16s(f, &s->rep_period);
+    qemu_get_be16s(f, &s->data_sframe_num);
+
+    qemu_get_timer(f, s->spdif_timer);
+
+    return 0;
+}
+
+/* SPDIF initialization */
+static int s5pc1xx_spdif_init(SysBusDevice *dev)
+{
+    S5pc1xxSpdifState *s = FROM_SYSBUS(S5pc1xxSpdifState, dev);
+    int iomemtype;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_spdif_readfn, s5pc1xx_spdif_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_SPDIF_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_SPDIF, 0,
+                                    s5pc1xx_spdif_gpio_readfn,
+                                    s5pc1xx_spdif_gpio_writefn, NULL, s);
+
+    s->spdif_timer = qemu_new_timer(vm_clock, s5pc1xx_spdif_sync, s);
+
+    s5pc1xx_spdif_reset(s);
+
+    qemu_register_reset(s5pc1xx_spdif_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.spdif", -1, 1,
+                    s5pc1xx_spdif_save, s5pc1xx_spdif_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_spdif_register(void)
+{
+    sysbus_register_dev("s5pc1xx.spdif", sizeof(S5pc1xxSpdifState),
+                        s5pc1xx_spdif_init);
+}
+
+device_init(s5pc1xx_spdif_register)
diff --git a/hw/s5pc1xx_spi.c b/hw/s5pc1xx_spi.c
new file mode 100644 (file)
index 0000000..13dfa9e
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * S5PC1XX SPI Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "sysbus.h"
+
+
+#define S5PC1XX_WDT_REG_MEM_SIZE 0x30
+
+
+typedef struct S5pc1xxSPIState {
+    SysBusDevice busdev;
+
+    uint32_t ch_cfg;
+    uint32_t clk_cfg;
+    uint32_t mode_cfg;
+    uint32_t cs_reg;
+    uint32_t spi_int_en;
+    uint32_t spi_status;
+    uint32_t spi_tx_dat;
+    uint32_t spi_rx_dat;
+    uint32_t packet_cnt_reg;
+    uint32_t pending_clr_reg;
+    uint32_t swap_cfg;
+    uint32_t fb_clk_sel;
+
+    qemu_irq irq;
+} S5pc1xxSPIState;
+
+
+static uint32_t s5pc1xx_spi_mm_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return s->ch_cfg;
+    case 0x04:
+        return s->clk_cfg;
+    case 0x08:
+        return s->mode_cfg;
+    case 0x0C:
+        return s->cs_reg;
+    case 0x10:
+        return s->spi_int_en;
+    case 0x14:
+        return s->spi_status;
+    case 0x18:
+        return s->spi_tx_dat;
+    case 0x1C:
+        return s->spi_rx_dat;
+    case 0x20:
+        return s->packet_cnt_reg;
+    case 0x24:
+        return s->pending_clr_reg;
+    case 0x28:
+        return s->swap_cfg;
+    case 0x2C:
+        return s->fb_clk_sel;
+    default:
+        hw_error("s5pc1x_spi: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_spi_mm_write(void *opaque, target_phys_addr_t offset,
+                                 uint32_t val)
+{
+    S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->ch_cfg = val;
+        break;
+    case 0x04:
+        s->clk_cfg = val;
+        break;
+    case 0x08:
+        s->mode_cfg = val;
+        break;
+    case 0x0C:
+        s->cs_reg = val;
+        break;
+    case 0x10:
+        s->spi_int_en = val;
+        break;
+    case 0x14:
+        s->spi_status = val;
+        break;
+    case 0x18:
+        s->spi_tx_dat = val;
+        break;
+    case 0x1C:
+        s->spi_rx_dat = val;
+        break;
+    case 0x20:
+        s->packet_cnt_reg = val;
+        break;
+    case 0x24:
+        s->pending_clr_reg = val;
+        break;
+    case 0x28:
+        s->swap_cfg = val;
+        break;
+    case 0x2C:
+        s->fb_clk_sel = val;
+        break;
+    default:
+        hw_error("s5pc1x_spi: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+CPUReadMemoryFunc * const s5pc1xx_spi_readfn[] = {
+    s5pc1xx_spi_mm_read,
+    s5pc1xx_spi_mm_read,
+    s5pc1xx_spi_mm_read
+};
+
+CPUWriteMemoryFunc * const s5pc1xx_spi_writefn[] = {
+    s5pc1xx_spi_mm_write,
+    s5pc1xx_spi_mm_write,
+    s5pc1xx_spi_mm_write
+};
+
+static void s5pc1xx_spi_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+    qemu_put_be32s(f, &s->ch_cfg);
+    qemu_put_be32s(f, &s->clk_cfg);
+    qemu_put_be32s(f, &s->mode_cfg);
+    qemu_put_be32s(f, &s->cs_reg);
+    qemu_put_be32s(f, &s->spi_int_en);
+    qemu_put_be32s(f, &s->spi_status);
+    qemu_put_be32s(f, &s->spi_tx_dat);
+    qemu_put_be32s(f, &s->spi_rx_dat);
+    qemu_put_be32s(f, &s->packet_cnt_reg);
+    qemu_put_be32s(f, &s->pending_clr_reg);
+    qemu_put_be32s(f, &s->swap_cfg);
+    qemu_put_be32s(f, &s->fb_clk_sel);
+}
+
+static int s5pc1xx_spi_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->ch_cfg);
+    qemu_get_be32s(f, &s->clk_cfg);
+    qemu_get_be32s(f, &s->mode_cfg);
+    qemu_get_be32s(f, &s->cs_reg);
+    qemu_get_be32s(f, &s->spi_int_en);
+    qemu_get_be32s(f, &s->spi_status);
+    qemu_get_be32s(f, &s->spi_tx_dat);
+    qemu_get_be32s(f, &s->spi_rx_dat);
+    qemu_get_be32s(f, &s->packet_cnt_reg);
+    qemu_get_be32s(f, &s->pending_clr_reg);
+    qemu_get_be32s(f, &s->swap_cfg);
+    qemu_get_be32s(f, &s->fb_clk_sel);
+
+    return 0;
+}
+
+static void s5pc1xx_spi_reset(void *opaque)
+{
+    S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+    s->ch_cfg = 0;
+    s->clk_cfg = 0;
+    s->mode_cfg = 0;
+    s->cs_reg = 1;
+    s->spi_int_en = 0;
+    s->spi_status = 0;
+    s->spi_tx_dat = 0;
+    s->spi_rx_dat = 0;
+    s->packet_cnt_reg = 0;
+    s->pending_clr_reg = 0;
+    s->swap_cfg = 0;
+    s->fb_clk_sel = 0;
+}
+
+static int s5pc1xx_spi_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxSPIState *s = FROM_SYSBUS(S5pc1xxSPIState, dev);
+
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_spi_readfn, s5pc1xx_spi_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_WDT_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_spi_reset(s);
+
+    qemu_register_reset(s5pc1xx_spi_reset, s);
+    register_savevm(&dev->qdev, "s5pc1xx.spi", -1, 1,
+                    s5pc1xx_spi_save, s5pc1xx_spi_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_spi_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.spi", sizeof(S5pc1xxSPIState),
+                        s5pc1xx_spi_init);
+}
+
+device_init(s5pc1xx_spi_register_devices)
diff --git a/hw/s5pc1xx_srom.c b/hw/s5pc1xx_srom.c
new file mode 100644 (file)
index 0000000..058b996
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * S5PC1XX SROM controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "s5pc1xx.h"
+
+
+typedef struct S5pc1xxSROMState {
+    SysBusDevice busdev;
+
+    /* SROM_BW - SROM Bus width & wait control */
+    uint32_t control;
+    /* SROM_BCn - SROM Bank n control register */
+    uint32_t *bank_control;
+    uint32_t num_banks;
+} S5pc1xxSROMState;
+
+
+static uint32_t s5pc1xx_srom_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+
+    if ((offset > (s->num_banks + 1) * 4) || (offset & 3)) {
+        hw_error("s5pc1xx.srom: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+
+    switch (offset) {
+    case 0x00:
+        return s->control;
+    default:
+        return s->bank_control[offset / 4 - 1];
+    }
+}
+
+static void s5pc1xx_srom_write(void *opaque, target_phys_addr_t offset,
+                               uint32_t val)
+{
+    S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+
+    if ((offset > (s->num_banks + 1) * 4) || (offset & 3)) {
+        hw_error("s5pc1xx.srom: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+
+    switch (offset) {
+    case 0x00:
+        s->control = val;
+        break;
+    default:
+        s->bank_control[offset / 4 - 1] = val;
+        break;
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_srom_mm_read[] = {
+    s5pc1xx_srom_read,
+    s5pc1xx_srom_read,
+    s5pc1xx_srom_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_srom_mm_write[] = {
+    s5pc1xx_srom_write,
+    s5pc1xx_srom_write,
+    s5pc1xx_srom_write
+};
+
+static void s5pc1xx_srom_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->control);
+    qemu_put_be32s(f, &s->num_banks);
+
+    for (i = 0; i < s->num_banks; i++) {
+        qemu_put_be32s(f, s->bank_control + i);
+    }
+
+}
+
+static int s5pc1xx_srom_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->control);
+    qemu_get_be32s(f, &s->num_banks);
+
+    for (i = 0; i < s->num_banks; i++) {
+        qemu_get_be32s(f, s->bank_control + i);
+    }
+
+    return 0;
+}
+
+static void s5pc1xx_srom_reset(DeviceState *d)
+{
+    S5pc1xxSROMState *s =
+        FROM_SYSBUS(S5pc1xxSROMState, sysbus_from_qdev(d));
+    int i = 0;
+
+    s->control = 0x00000008;
+    for (i = 0; i < s->num_banks; i++) {
+        s->bank_control[i] = 0x000F0000;
+    }
+}
+
+DeviceState *s5pc1xx_srom_init(target_phys_addr_t base, int num_banks)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.srom");
+
+    qdev_prop_set_uint32(dev, "num-banks", num_banks);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    return dev;
+}
+
+static int s5pc1xx_srom_init1(SysBusDevice *dev)
+{
+    S5pc1xxSROMState *s = FROM_SYSBUS(S5pc1xxSROMState, dev);
+    int iomemtype;
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_srom_mm_read, s5pc1xx_srom_mm_write, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, (s->num_banks + 1) * 4, iomemtype);
+    s->bank_control = qemu_mallocz(s->num_banks * sizeof(uint32_t));
+
+    s5pc1xx_srom_reset(&s->busdev.qdev);
+
+    register_savevm(&dev->qdev, "s5pc1xx.srom", -1, 1,
+                    s5pc1xx_srom_save, s5pc1xx_srom_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_srom_info = {
+    .init = s5pc1xx_srom_init1,
+    .qdev.name  = "s5pc1xx.srom",
+    .qdev.size  = sizeof(S5pc1xxSROMState),
+    .qdev.reset = s5pc1xx_srom_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("num-banks", S5pc1xxSROMState, num_banks, 6),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_srom_register(void)
+{
+    sysbus_register_withprop(&s5pc1xx_srom_info);
+}
+
+device_init(s5pc1xx_srom_register)
diff --git a/hw/s5pc1xx_st.c b/hw/s5pc1xx_st.c
new file mode 100644 (file)
index 0000000..ea0ed43
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * System Timer for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "s5pc1xx.h"
+
+
+#define TCFG            0x00        /* R/W Configures 8-bit-Prescaler and Clock MUX 0x0000_0000 */
+#define TCON            0x04        /* R/W Timer Control Register 0x0000_0000 */
+#define TICNTB          0x08        /* R/W Tick Integer Count Buffer Register 0x0000_0000 */
+#define TICNTO          0x0C        /* R Tick Integer Count Observation Register 0x0000_0000 */
+#define TFCNTB          0x10        /* R/W Tick Fractional Count Buffer Register 0x0000_0000 */
+#define ICNTB           0x18        /* R/W Interrupt Count Buffer Register 0x0000_0000 */
+#define ICNTO           0x1C        /* R Interrupt Count Observation Register 0x0000_0000 */
+#define INT_CSTAT       0x20        /* R/W Interrupt Control and Status Register 0x0000_0000 */
+
+/* TCFG */
+#define TICK_SWRST      (1 << 16)   /* SW reset of TICK generation logic */
+#define FDIV_SEL        (1 << 15)   /* Fractional divider select */
+#define TICKGEN_SEL     (1 << 14)   /* 0 = Integer divider 1 = Fractional divider */
+#define TCLKB_MUX       (3 << 12)   /* Selects clock input for TCLKB */
+#define DIV_MUX         (7 << 8)    /* Selects Mux input for Timer */
+#define PRESCALER       (0xFF << 0) /* Prescaler value for timer 0x00 */
+
+/* TCON */
+#define INT_AUTO_RELOAD (1 << 5)    /* 0 = No operation 1 = Interval mode(auto-reload) */
+#define INT_MAN_UPD     (1 << 4)    /* 0 = No operation 1 = Update ICNTB and One-shot mode */
+#define INT_RUN         (1 << 3)    /* 0 = Stop 1 = Start timer */
+#define TIMER_RUN       (1 << 0)    /* 0 = Stop 1 = Start timer */
+
+/* INT_CSTAT */
+#define TWIE            (1 << 10)   /* TCON Write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define IWIE            (1 << 9)    /* ICNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define TFWIE           (1 << 8)    /* TFCNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define TIWIE           (1 << 7)    /* TICNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define ICNTEIE         (1 << 6)    /* Interrupt counter expired (INTCNT=0) Interrupt Enable */
+#define TCON_W_STAT     (1 << 5)    /* TCON Write Interrupt Status Bit */
+#define ICNTB_W_STAT    (1 << 4)    /* ICNTB Write Interrupt Status Bit */
+#define TFCNTB_W_STAT   (1 << 3)    /* TFCTNB Write Interrupt Status Bit */
+#define TICNTB_W_STAT   (1 << 2)    /* TICTNB Write Interrupt Status Bit */
+#define INTCNT_EXP_STAT (1 << 1)    /* Interrupt counter expired (INTCNT=0) Interrupt Status Bit */
+#define INT_ENABLE      (1 << 0)    /* Enables Interrupt */
+#define ALL_STAT        (TCON_W_STAT | ICNTB_W_STAT | TFCNTB_W_STAT | \
+                         TICNTB_W_STAT | INTCNT_EXP_STAT)
+
+#define S5PC1XX_ST_REG_MEM_SIZE 0x24
+
+
+typedef struct {
+    SysBusDevice busdev;
+
+    uint32_t tcfg;
+    uint32_t tcon;
+    uint32_t ticntb;
+    uint32_t ticnto;
+    uint32_t tfcntb;
+    uint32_t icntb;
+    int32_t  icnto;     /* has signed type */
+    uint32_t int_cstat;
+
+    qemu_irq irq;
+
+    uint8_t  divider;
+    uint8_t  prescaler;
+
+    QEMUTimer *st_timer;
+    uint32_t freq_out;
+    uint64_t tick_interval;
+    uint64_t last_tick;
+    uint64_t next_planned_tick;
+    uint64_t base_time;
+} S5pc1xxSTState;
+
+
+const char *st_clks[] = { "XXTI", "XrtcXTI", "XusbXTI", "pclk_66" };
+
+static void s5pc1xx_st_tick(void *opaque);
+
+/* work with interrupts depending on permissive bits */
+static void s5pc1xx_st_irq(S5pc1xxSTState *s, uint32_t enab_mask,
+                           uint32_t stat_mask)
+{
+    /* stop tick timer */
+    if ((stat_mask == TICNTB_W_STAT) || (stat_mask == TFCNTB_W_STAT)) {
+        s->tcon &= ~TIMER_RUN;
+        s5pc1xx_st_tick(s);
+    }
+
+    /* reload ICNT after manual update */
+    if ((stat_mask == ICNTB_W_STAT) && (s->tcon & INT_MAN_UPD))
+        s->icnto = s->icntb;
+
+    /* raise irq */
+    if ((s->int_cstat & INT_ENABLE) && (s->int_cstat & enab_mask)) {
+        qemu_irq_raise(s->irq);
+    }
+
+    s->int_cstat |= stat_mask;
+}
+
+static void s5pc1xx_st_set_timer(S5pc1xxSTState *s)
+{
+    uint64_t last = qemu_get_clock(vm_clock) - s->base_time;
+    /* make a tick each tick_interval'th QEMU timer cycle - this way
+     * system timer is working consistently (1 second for emulated machine
+     * corresponds to 1 second for host); otherwise due to QEMU timer interrupt
+     * handling overhead it will slowly drift towards the past */
+    s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
+    qemu_mod_timer(s->st_timer, s->next_planned_tick + s->base_time);
+    s->last_tick = last;
+}
+
+/* counter step */
+static void s5pc1xx_st_tick(void *opaque)
+{
+    S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+    /* tick actually happens not every ticnto but rather at icnto update;
+     * if current ticnto value is needed it is calculated in s5pc1xx_st_read */
+    if (s->tcon & TIMER_RUN) {
+        if (s->tcon & INT_RUN) {
+            /* reload count */
+            if (s->icnto == 0 && s->tcon & INT_AUTO_RELOAD)
+                s->icnto = s->icntb;
+
+            s->icnto--;
+
+            /* time for interrupt */
+            if (s->icnto <= 0) {
+                s->icnto = 0;
+                s5pc1xx_st_irq(s, ICNTEIE, INTCNT_EXP_STAT);
+            }
+        }
+
+        /* schedule next interrupt */
+        s5pc1xx_st_set_timer(s);
+    } else {
+        s->next_planned_tick = 0;
+        s->last_tick = 0;
+        qemu_del_timer(s->st_timer);
+    }
+}
+
+/* set default values for all fields */
+static void s5pc1xx_st_reset(S5pc1xxSTState *s)
+{
+    /* TODO: Check if reseting all counters is needed */
+    s->tcfg      = 0;
+    s->tcon      = 0;
+    s->ticntb    = 0;
+    s->ticnto    = 0;
+    s->tfcntb    = 0;
+    s->icntb     = 1;
+    s->icnto     = 0;
+    s->int_cstat = 0;
+
+    s->last_tick = 0;
+    s->next_planned_tick = 0;
+    s->freq_out  = 0;
+    s->tick_interval = 0;
+    s->divider   = 1;
+    s->prescaler = 0;
+    s->base_time = qemu_get_clock(vm_clock);
+
+    qemu_del_timer(s->st_timer);
+}
+
+/* update timer frequency */
+static void s5pc1xx_st_update(S5pc1xxSTState *s)
+{
+    S5pc1xxClk clk;
+
+    s->divider = 1 << ((s->tcfg & DIV_MUX) >> 8);
+    s->prescaler = s->tcfg & PRESCALER;
+
+    clk = s5pc1xx_findclk(st_clks[(s->tcfg & TCLKB_MUX) >> 12]);
+    s->freq_out = s5pc1xx_clk_getrate(clk) / (s->prescaler + 1) / s->divider;
+    s->tick_interval =
+        muldiv64(s->ticntb, get_ticks_per_sec(), s->freq_out) +
+        (muldiv64(s->tfcntb, get_ticks_per_sec(), s->freq_out) >> 16);
+    s->next_planned_tick = 0;
+
+    if (!s->freq_out)
+        hw_error("s5pc1xx.st: timer update input frequency is zero\n");
+}
+
+/* System Timer read */
+static uint32_t s5pc1xx_st_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+    uint64_t cur_clock, tps;
+
+    switch (offset) {
+    case TCFG:
+        return s->tcfg;
+    case TCON:
+        return s->tcon;
+    case TICNTB:
+        return s->ticntb;
+    case TICNTO:
+        if (s->freq_out && s->last_tick && s->ticntb && s->next_planned_tick) {
+            cur_clock = qemu_get_clock(vm_clock) - s->base_time;
+            if (cur_clock < s->next_planned_tick) {
+                if (s->tick_interval > 0xFFFFFFFF) {
+                    /* very large tick interval; muldiv64 can't be used
+                     * in this case directly; avoid 64-bit difference by
+                     * knowing the fact that next_planned_tick and last_tick
+                     * may be represented as "next_planned_tick =
+                     * K * tick_interval" and "last_tick = N * tick_interval"
+                     * assuming that last_tick happened in time */
+                    tps = get_ticks_per_sec();
+                    s->ticnto = muldiv64(s->next_planned_tick - cur_clock,
+                                         s->ticntb,
+                                         (s->next_planned_tick - s->last_tick +
+                                          tps / 2) / tps) / tps;
+                } else {
+                    /* tick interval is 32-bit so both differences
+                     * should be 32-bit too */
+                    s->ticnto = muldiv64(s->next_planned_tick - cur_clock, s->ticntb,
+                                         s->next_planned_tick - s->last_tick);
+                }
+            } else {
+                s->ticnto = 0;
+            }
+        } else {
+            s->ticnto = 0;
+        }
+        return s->ticnto;
+    case TFCNTB:
+        return s->tfcntb;
+    case ICNTB:
+        return s->icntb - 1;
+    case ICNTO:
+        return s->icnto;
+    case INT_CSTAT:
+        return s->int_cstat;
+    default:
+        hw_error("s5pc1xx.st: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* System Timer write */
+static void s5pc1xx_st_write(void *opaque, target_phys_addr_t offset,
+                             uint32_t value)
+{
+    S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+    switch (offset) {
+    case TCFG:
+        if (value & TICK_SWRST) {
+            s5pc1xx_st_reset(s);
+            break;
+        }
+        s->tcfg = value;
+        s5pc1xx_st_update(s);
+        break;
+
+    case TCON:
+        s5pc1xx_st_irq(s, TWIE, TCON_W_STAT);
+
+        if ((value & TIMER_RUN) > (s->tcon & TIMER_RUN)) {
+            s->base_time = qemu_get_clock(vm_clock);
+            s5pc1xx_st_set_timer(s);
+        } else if ((value & TIMER_RUN) < (s->tcon & TIMER_RUN)) {
+            qemu_del_timer(s->st_timer);
+        }
+        s->tcon = value;
+        break;
+
+    case TICNTB:
+        /* in this case s->ticntb is updated after interrupt raise
+         * since the timer must be stopped first */
+        s5pc1xx_st_irq(s, TIWIE, TICNTB_W_STAT);
+        s->ticntb = value;
+        s5pc1xx_st_update(s);
+        break;
+
+    case TFCNTB:
+        s5pc1xx_st_irq(s, TFWIE, TFCNTB_W_STAT);
+        s->tfcntb = value;
+        s5pc1xx_st_update(s);
+        break;
+
+    case ICNTB:
+        /* in this case s->icntb is updated before interrupt raise
+         * since the value is needed for manual update */
+        s->icntb = value + 1;
+        s5pc1xx_st_irq(s, IWIE, ICNTB_W_STAT);
+        break;
+
+    case INT_CSTAT:
+        /* set INT_CSTAT as value except *_STAT bits */
+        s->int_cstat = (s->int_cstat & ALL_STAT) | (value & ~ALL_STAT);
+        /* clear *_STAT bits if they are set in value */
+        s->int_cstat &= ~(value & ALL_STAT);
+
+        /* lower interrupt */
+        /* TODO: check if IRQ should be lowered for all cases or
+         * only when there are no more stat bits left */
+        if (!(s->int_cstat & ALL_STAT))
+            qemu_irq_lower(s->irq);
+        break;
+
+    default:
+        hw_error("s5pc1xx.st: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_st_readfn[] = {
+   s5pc1xx_st_read,
+   s5pc1xx_st_read,
+   s5pc1xx_st_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_st_writefn[] = {
+   s5pc1xx_st_write,
+   s5pc1xx_st_write,
+   s5pc1xx_st_write
+};
+
+static void s5pc1xx_st_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+    qemu_put_be32s (f, &s->tcfg);
+    qemu_put_be32s (f, &s->tcon);
+    qemu_put_be32s (f, &s->ticntb);
+    qemu_put_be32s (f, &s->ticnto);
+    qemu_put_be32s (f, &s->tfcntb);
+    qemu_put_be32s (f, &s->icntb);
+    qemu_put_sbe32s(f, &s->icnto);
+    qemu_put_be32s (f, &s->int_cstat);
+
+    qemu_put_8s    (f, &s->divider);
+    qemu_put_8s    (f, &s->prescaler);
+    qemu_put_be32s (f, &s->freq_out);
+
+    qemu_put_be64s (f, &s->tick_interval);
+    qemu_put_be64s (f, &s->last_tick);
+    qemu_put_be64s (f, &s->next_planned_tick);
+    qemu_put_be64s (f, &s->base_time);
+
+    qemu_put_timer (f, s->st_timer);
+}
+
+static int s5pc1xx_st_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s (f, &s->tcfg);
+    qemu_get_be32s (f, &s->tcon);
+    qemu_get_be32s (f, &s->ticntb);
+    qemu_get_be32s (f, &s->ticnto);
+    qemu_get_be32s (f, &s->tfcntb);
+    qemu_get_be32s (f, &s->icntb);
+    qemu_get_sbe32s(f, &s->icnto);
+    qemu_get_be32s (f, &s->int_cstat);
+
+    qemu_get_8s    (f, &s->divider);
+    qemu_get_8s    (f, &s->prescaler);
+    qemu_get_be32s (f, &s->freq_out);
+
+    qemu_get_be64s (f, &s->tick_interval);
+    qemu_get_be64s (f, &s->last_tick);
+    qemu_get_be64s (f, &s->next_planned_tick);
+    qemu_get_be64s (f, &s->base_time);
+
+    qemu_get_timer (f, s->st_timer);
+
+    return 0;
+}
+
+/* System Timer init */
+static int s5pc1xx_st_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxSTState *s = FROM_SYSBUS(S5pc1xxSTState, dev);
+
+    s->st_timer = qemu_new_timer(vm_clock, s5pc1xx_st_tick, s);
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_st_readfn, s5pc1xx_st_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_ST_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_st_reset(s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.st", -1, 1,
+                    s5pc1xx_st_save, s5pc1xx_st_load, s);
+    return 0;
+}
+
+static void s5pc1xx_st_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.st", sizeof(S5pc1xxSTState), s5pc1xx_st_init);
+}
+
+device_init(s5pc1xx_st_register_devices)
diff --git a/hw/s5pc1xx_tsadc.c b/hw/s5pc1xx_tsadc.c
new file mode 100644 (file)
index 0000000..737bf02
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * S5PC1XX ADC & TOUCH SCREEN INTERFACE
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "console.h"
+
+
+#define QEMUMAXX 0x7FFF
+#define QEMUMAXY 0x7FFF
+
+#define WAIT_FOR_INT 3
+
+#define S5PC1XX_TSADC_REG_MEM_SIZE 0x24
+
+
+typedef union {
+    /* raw register data */
+    uint32_t v;
+
+    /* register bits */
+    struct tsadccon_bits {
+        unsigned enable_start : 1;
+        unsigned read_start   : 1;
+        unsigned standby      : 1;
+        unsigned reserved3_5  : 3;
+        unsigned prscvl       : 8;
+        unsigned prscen       : 1;
+        unsigned ecflg        : 1; /* (Read only) */
+        unsigned res          : 1;
+        unsigned tssel        : 1;
+    } b;
+} tsadccon_s;
+
+typedef union {
+   /* raw register data */
+   uint32_t v;
+
+   /* register bits */
+   struct tsdat_bits {
+       /*XPDATA or YPDATA*/
+       unsigned pdata        : 12;
+       unsigned xy_pst_val   : 2;
+       unsigned auto_pst_val : 1;
+       unsigned updown       : 1;
+   } b;
+} tsdat_s;
+
+typedef union {
+    /* raw register data */
+    uint32_t v;
+
+    /* register bits */
+    struct tscon_bits {
+        unsigned xy_pst   : 2;
+        unsigned auto_pst : 1;
+        unsigned pull_up  : 1;
+        unsigned xp_sen   : 1;
+        unsigned xm_sen   : 1;
+        unsigned yp_sen   : 1;
+        unsigned ym_sen   : 1;
+        unsigned ud_sen   : 1;
+    } b;
+} tscon_s;
+
+typedef union {
+    /* raw register data */
+    uint32_t v;
+
+    /* register bits */
+    struct tspenstat_bits {
+        unsigned tsc_dn   : 1;
+        unsigned tsc_up   : 1;
+    } b;
+} tspenstat_s;
+
+typedef struct S5pc1xxTSADCState {
+    SysBusDevice busdev;
+
+    /* R/W Specifies the TSn - ADC Control Register  */
+    tsadccon_s tsadccon;
+
+    /* R/W Specifies the TSn - Touch Screen Control Register */
+    tscon_s tscon;
+
+    /* R/W Specifies the TSn - ADC Start or Interval Delay Register */
+    uint32_t tsdly;
+
+    /* R Specifies the TSn - ADC Conversion Data X Register */
+    tsdat_s tsdat0;
+
+    /* R Specifies the TSn - ADC Conversion Data Y Register */
+    tsdat_s tsdat1;
+
+    /* R/W Specifies the TSn - Penn Up or Down Status Register */
+    tspenstat_s tspenstat;
+
+    /* R/W Specifies the Analog input channel selection */
+    uint32_t adcmux;
+
+    /* Internal data */
+
+    /* ID of this device instance */
+    uint32_t id;
+    /* Current pointer coordinates ({0,0} < {x,y} < {QEMUMAXX,QEMUMAXY}) */
+    uint32_t x, y;
+    /* Boundary reported coordinates */
+    uint32_t minx, maxx, miny, maxy;
+    /* Is it a 'new' touchscreen version? */
+    int32_t new;
+    /* Touchscreen resolution, max 12 bit */
+    uint32_t resolution;
+    /* Current mouse buttons state */
+    int32_t pressure;
+    /* Currently reported touchscreen touch state */
+    int32_t report_pressure;
+
+    /* Interrupts */
+    qemu_irq irq_adc;
+    qemu_irq irq_pennd;
+
+    /* Timer to report conversion data periodically */
+    QEMUTimer *timer;
+    uint32_t  conversion_time;
+} S5pc1xxTSADCState;
+
+
+static void s5pc1xx_tsadc_reset(DeviceState *d)
+{
+    S5pc1xxTSADCState *s =
+        FROM_SYSBUS(S5pc1xxTSADCState, sysbus_from_qdev(d));
+
+    s->tsadccon.v  = 0x00003FC4;
+    s->tscon.v     = 0x00000058;
+    s->tsdly       = 0x000000FF;
+    s->tsdat0.v    = 0x00000000;
+    s->tsdat1.v    = 0x00000000;
+    s->tspenstat.v = 0x00000000;
+    s->adcmux      = 0x00000000;
+
+    s->x = s->y = 0;
+    s->pressure = 0;
+    s->report_pressure = 0;
+    s->conversion_time = 0;
+}
+
+static void s5pc1xx_tsadc_conversion_start(S5pc1xxTSADCState *s,
+                                           unsigned int time)
+{
+    /* Do not do anything in standby mode */
+    if (!s->tsadccon.b.standby) {
+        /* Conversion is going... */
+        s->tsadccon.b.ecflg = 0;
+        /* ... and will finish in 'time' QEMU vm_clock ticks */
+        qemu_mod_timer(s->timer,
+                       qemu_get_clock(vm_clock) + time);
+    }
+}
+
+static void s5pc1xx_tsadc_conversion(void *opaque)
+{
+    S5pc1xxTSADCState *s = opaque;
+
+    s->tsadccon.b.ecflg = 1;
+
+    /* Generate IRQ_ADC for any mode except 'Waiting for interrupt' */
+    if (s->tscon.b.xy_pst != WAIT_FOR_INT) {
+        qemu_irq_raise(s->irq_adc);
+    } else {
+        /* If mouse buttons state changed recently - report it */
+        if (s->report_pressure != s->pressure &&
+            ((s->pressure == 1 && s->tscon.b.ud_sen == 0) ||
+             (s->pressure == 0 && s->tscon.b.ud_sen == 1))) {
+            qemu_irq_raise(s->irq_pennd);
+            s->tspenstat.b.tsc_dn |= s->pressure;
+            s->tspenstat.b.tsc_up |= !s->pressure;
+        }
+        s->report_pressure = s->pressure;
+    }
+}
+
+static uint32_t s5pc1xx_tsadc_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxTSADCState *s = (S5pc1xxTSADCState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return s->tsadccon.v;
+    case 0x04:
+        return s->tscon.v;
+    case 0x08:
+        return s->tsdly;
+    case 0x0C:
+        if (s->new) {
+            s->tsdat0.b.pdata =
+                ((1 << s->resolution) - 1) -
+                (s->miny + s->y * (s->maxy - s->miny) / QEMUMAXY);
+        } else {
+            s->tsdat0.b.pdata = s->minx + s->x * (s->maxx - s->minx) / QEMUMAXX;
+        }
+        s->tsdat0.b.updown = s->report_pressure == 0;
+        s->tsdat0.b.auto_pst_val = s->tscon.b.auto_pst;
+        s->tsdat0.b.xy_pst_val = s->tscon.b.xy_pst;
+        if (s->tsadccon.b.read_start)
+            s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+        return s->tsdat0.v;
+    case 0x10:
+        if (s->new) {
+            s->tsdat1.b.pdata =
+                ((1 << s->resolution) - 1) -
+                (s->minx + s->x * (s->maxx - s->minx) / QEMUMAXX);
+        } else {
+            s->tsdat1.b.pdata = s->miny + s->y * (s->maxy - s->miny) / QEMUMAXY;
+        }
+        s->tsdat1.b.updown = s->report_pressure == 0;
+        s->tsdat1.b.auto_pst_val = s->tscon.b.auto_pst;
+        s->tsdat1.b.xy_pst_val = s->tscon.b.xy_pst;
+        if (s->tsadccon.b.read_start)
+            s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+        return s->tsdat1.v;
+    case 0x14:
+        return s->tspenstat.v;
+    case 0x18:
+        return 0x0;
+    case 0x1C:
+        return s->adcmux;
+    case 0x20:
+        return 0x0;
+    default:
+        hw_error("s5pc1xx.tsadc: bad read offset " TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_tsadc_write(void *opaque, target_phys_addr_t offset,
+                                uint32_t val)
+{
+    S5pc1xxTSADCState *s = (S5pc1xxTSADCState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->tsadccon.v = val;
+        if (!s->tsadccon.b.read_start && s->tsadccon.b.enable_start)
+            s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+        s->tsadccon.b.enable_start = 0;
+        /* FIXME: choose the correct clock depending on a register value */
+        s->conversion_time =
+            muldiv64(s->tsdly & 0xFFFF, get_ticks_per_sec(),
+                     s5pc1xx_clk_getrate(s5pc1xx_findclk("pclk_66")) /
+                         (s->tsadccon.b.prscen ? s->tsadccon.b.prscvl + 1: 1));
+        break;
+    case 0x04:
+        s->tscon.v = val;
+        if (s->report_pressure != s->pressure &&
+            s->tscon.b.xy_pst == WAIT_FOR_INT) {
+            /* Raise next IRQ for touch-up in one ms */
+            s5pc1xx_tsadc_conversion_start(s, get_ticks_per_sec() / 1000);
+        }
+        break;
+    case 0x08:
+        s->tsdly = val;
+        /* FIXME: choose the correct clock depending on a register value */
+        s->conversion_time =
+            muldiv64(s->tsdly & 0xFFFF, get_ticks_per_sec(),
+                     s5pc1xx_clk_getrate(s5pc1xx_findclk("pclk_66")) /
+                         (s->tsadccon.b.prscen ? s->tsadccon.b.prscvl + 1: 1));
+        break;
+    case 0x14:
+        s->tspenstat.v = val;
+        break;
+    case 0x18:
+        qemu_irq_lower(s->irq_adc);
+        break;
+    case 0x1C:
+        s->adcmux = val;
+        break;
+    case 0x20:
+        qemu_irq_lower(s->irq_pennd);
+        break;
+    default:
+        hw_error("s5pc1xx.tsadc: bad write offset " TARGET_FMT_plx "\n",
+                 offset);
+        break;
+    }
+}
+
+static void s5pc1xx_touchscreen_event(void *opaque,
+                                      int x, int y, int z, int buttons_state)
+{
+    S5pc1xxTSADCState *s = opaque;
+
+    if (buttons_state) {
+        s->x = x;
+        s->y = y;
+    }
+
+    if (s->pressure == !buttons_state) {
+        s->pressure = !!buttons_state;
+
+        /* Report button state change momentarily if it happens in
+         * 'Waiting for interrupt' mode */
+        if (s->tscon.b.xy_pst == WAIT_FOR_INT && !s->tsadccon.b.standby) {
+            s5pc1xx_tsadc_conversion(s);
+        }
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_tsadc_mm_read[] = {
+    s5pc1xx_tsadc_read,
+    s5pc1xx_tsadc_read,
+    s5pc1xx_tsadc_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_tsadc_mm_write[] = {
+    s5pc1xx_tsadc_write,
+    s5pc1xx_tsadc_write,
+    s5pc1xx_tsadc_write
+};
+
+static void s5pc1xx_tsadc_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxTSADCState *s = opaque;
+
+    qemu_put_be32s (f, &s->tsadccon.v);
+    qemu_put_be32s (f, &s->tscon.v);
+    qemu_put_be32s (f, &s->tsdat0.v);
+    qemu_put_be32s (f, &s->tsdat1.v);
+    qemu_put_be32s (f, &s->tspenstat.v);
+
+    qemu_put_be32s (f, &s->tsdly);
+    qemu_put_be32s (f, &s->adcmux);
+    qemu_put_be32s (f, &s->x);
+    qemu_put_be32s (f, &s->y);
+    qemu_put_sbe32s(f, &s->pressure);
+    qemu_put_sbe32s(f, &s->report_pressure);
+    qemu_put_be32s (f, &s->conversion_time);
+    qemu_put_timer (f, s->timer);
+}
+
+static int s5pc1xx_tsadc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxTSADCState *s = opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s (f, &s->tsadccon.v);
+    qemu_get_be32s (f, &s->tscon.v);
+    qemu_get_be32s (f, &s->tsdat0.v);
+    qemu_get_be32s (f, &s->tsdat1.v);
+    qemu_get_be32s (f, &s->tspenstat.v);
+
+    qemu_get_be32s (f, &s->tsdly);
+    qemu_get_be32s (f, &s->adcmux);
+    qemu_get_be32s (f, &s->x);
+    qemu_get_be32s (f, &s->y);
+    qemu_get_sbe32s(f, &s->pressure);
+    qemu_get_sbe32s(f, &s->report_pressure);
+    qemu_get_be32s (f, &s->conversion_time);
+    qemu_get_timer (f, s->timer);
+
+    return 0;
+}
+
+DeviceState *s5pc1xx_tsadc_init(target_phys_addr_t base, qemu_irq irq_adc,
+                                qemu_irq irq_pennd, int new, int resolution,
+                                int minx, int maxx, int miny, int maxy)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, new ? "s5pc1xx.tsadc.new" : "s5pc1xx.tsadc");
+    qdev_prop_set_uint32(dev, "resolution", resolution);
+    qdev_prop_set_uint32(dev, "minx", minx);
+    qdev_prop_set_uint32(dev, "miny", miny);
+    qdev_prop_set_uint32(dev, "maxx", maxx);
+    qdev_prop_set_uint32(dev, "maxy", maxy);
+
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_connect_irq(s, 0, irq_adc);
+    sysbus_connect_irq(s, 1, irq_pennd);
+    sysbus_mmio_map(s, 0, base);
+
+    return dev;
+}
+
+static int s5pc1xx_tsadc_init1(SysBusDevice *dev)
+{
+    S5pc1xxTSADCState *s = FROM_SYSBUS(S5pc1xxTSADCState, dev);
+    int iomemtype;
+    /* Current number of S3C Touchscreen controllers */
+    static int s5pc1xx_tsadc_number = 0;
+    char name[30];
+
+    s->id = s5pc1xx_tsadc_number++;
+    snprintf(name, 30, "QEMU s5pc1xx Touchscreen %d", s->id);
+    iomemtype = cpu_register_io_memory(s5pc1xx_tsadc_mm_read,
+                                       s5pc1xx_tsadc_mm_write, s,
+                                                                          DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_TSADC_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_tsadc_reset(&s->busdev.qdev);
+
+    sysbus_init_irq(dev, &s->irq_adc);
+    sysbus_init_irq(dev, &s->irq_pennd);
+    s->timer = qemu_new_timer(vm_clock, s5pc1xx_tsadc_conversion, s);
+
+    qemu_add_mouse_event_handler(s5pc1xx_touchscreen_event, s, 1, name);
+    register_savevm(&dev->qdev, "s5pc1xx.tsadc", s->id, 1,
+                    s5pc1xx_tsadc_save, s5pc1xx_tsadc_load, s);
+
+    return 0;
+}
+
+static int s5pc1xx_tsadc_new_init1(SysBusDevice *dev)
+{
+    S5pc1xxTSADCState *s = FROM_SYSBUS(S5pc1xxTSADCState, dev);
+
+    s->new = 1;
+    return s5pc1xx_tsadc_init1(dev);
+}
+
+static SysBusDeviceInfo s5pc1xx_tsadc_info = {
+    .init = s5pc1xx_tsadc_init1,
+    .qdev.name  = "s5pc1xx.tsadc",
+    .qdev.size  = sizeof(S5pc1xxTSADCState),
+    .qdev.reset = s5pc1xx_tsadc_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("resolution", S5pc1xxTSADCState, resolution, 12),
+        DEFINE_PROP_UINT32("minx",       S5pc1xxTSADCState, minx,       0),
+        DEFINE_PROP_UINT32("miny",       S5pc1xxTSADCState, miny,       0),
+        DEFINE_PROP_UINT32("maxx",       S5pc1xxTSADCState, maxx,       480),
+        DEFINE_PROP_UINT32("maxy",       S5pc1xxTSADCState, maxy,       800),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static SysBusDeviceInfo s5pc1xx_tsadc_new_info = {
+    .init = s5pc1xx_tsadc_new_init1,
+    .qdev.name  = "s5pc1xx.tsadc.new",
+    .qdev.size  = sizeof(S5pc1xxTSADCState),
+    .qdev.reset = s5pc1xx_tsadc_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("resolution", S5pc1xxTSADCState, resolution, 12),
+        DEFINE_PROP_UINT32("minx",       S5pc1xxTSADCState, minx,       0),
+        DEFINE_PROP_UINT32("miny",       S5pc1xxTSADCState, miny,       0),
+        DEFINE_PROP_UINT32("maxx",       S5pc1xxTSADCState, maxx,       480),
+        DEFINE_PROP_UINT32("maxy",       S5pc1xxTSADCState, maxy,       800),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_tsadc_register_devices(void)
+{
+    sysbus_register_withprop(&s5pc1xx_tsadc_info);
+    sysbus_register_withprop(&s5pc1xx_tsadc_new_info);
+}
+
+device_init(s5pc1xx_tsadc_register_devices)
diff --git a/hw/s5pc1xx_uart.c b/hw/s5pc1xx_uart.c
new file mode 100644 (file)
index 0000000..b36cf5c
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * S5PC1XX UART Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define QUEUE_SIZE   257
+
+#define INT_RXD     (1 << 0)
+#define INT_ERROR   (1 << 1)
+#define INT_TXD     (1 << 2)
+#define INT_MODEM   (1 << 3)
+
+#define TRSTATUS_TRANSMITTER_READY  (1 << 2)
+#define TRSTATUS_BUFFER_EMPTY       (1 << 1)
+#define TRSTATUS_DATA_READY         (1 << 0)
+
+#define UFSTAT_RX_FIFO_FULL         (1 << 8)
+
+#define UFCON_FIFO_ENABLED          (1 << 0)
+#define UFCON_TX_LEVEL_SHIFT        8
+#define UFCON_TX_LEVEL              (7 << UFCON_TX_LEVEL_SHIFT)
+
+#define UFSTAT_TX_COUNT_SHIT        16
+#define UFSTAT_TX_COUNT             (0xFF << UFSTAT_TX_COUNT_SHIT)
+
+#define QI(x) ((x + 1) % QUEUE_SIZE)
+#define QD(x) ((x - 1 + QUEUE_SIZE) % QUEUE_SIZE)
+
+#define S5PC1XX_UART_REG_MEM_SIZE 0x3C
+
+typedef struct UartQueue {
+    uint8_t queue[QUEUE_SIZE];
+    uint32_t s, t;
+    uint32_t size;
+} UartQueue;
+
+typedef struct S5pc1xxUartState {
+    SysBusDevice busdev;
+
+    UartQueue rx;
+
+    uint32_t ulcon;
+    uint32_t ucon;
+    uint32_t ufcon;
+    uint32_t umcon;
+    uint32_t utrstat;
+    uint32_t uerstat;
+    uint32_t ufstat;
+    uint32_t umstat;
+    uint32_t utxh;
+    uint32_t urxh;
+    uint32_t ubrdiv;
+    uint32_t udivslot;
+    uint32_t uintp;
+    uint32_t uintsp;
+    uint32_t uintm;
+
+    CharDriverState *chr;
+    qemu_irq irq;
+    uint32_t instance;
+} S5pc1xxUartState;
+
+
+static inline int queue_elem_count(const UartQueue *s)
+{
+    if (s->t >= s->s) {
+        return s->t - s->s;
+    } else {
+        return QUEUE_SIZE - s->s + s->t;
+    }
+}
+
+static inline int queue_empty_count(const UartQueue *s)
+{
+    return s->size - queue_elem_count(s) - 1;
+}
+
+static inline int queue_empty(const UartQueue *s)
+{
+    return (queue_elem_count(s) == 0);
+}
+
+static inline void queue_push(UartQueue *s, uint8_t x)
+{
+    s->queue[s->t] = x;
+    s->t = QI(s->t);
+}
+
+static inline uint8_t queue_get(UartQueue *s)
+{
+    uint8_t ret;
+
+    ret = s->queue[s->s];
+    s->s = QI(s->s);
+    return ret;
+}
+
+static inline void queue_reset(UartQueue *s)
+{
+    s->s = 0;
+    s->t = 0;
+}
+
+static void s5pc1xx_uart_update(S5pc1xxUartState *s)
+{
+    if (s->ufcon && UFCON_FIFO_ENABLED) {
+        if (((s->ufstat && UFSTAT_TX_COUNT) >> UFSTAT_TX_COUNT_SHIT) <=
+            ((s->ufcon && UFCON_TX_LEVEL) >> UFCON_TX_LEVEL_SHIFT) * 2 ) {
+            s->uintsp |= INT_TXD;
+        }
+    }
+
+    s->uintp = s->uintsp & ~s->uintm;
+    if (s->uintp) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+/* Read UART by GPIO */
+static uint32_t s5pc1xx_uart_gpio_read(void *opaque,
+                                       int io_index)
+{
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    /* TODO: check if s->uintp should be used instead of s->uintsp */
+    if (io_index == GPIO_UART_RXD(s->instance)) {
+        return (s->uintsp & INT_RXD);
+    }
+    if (io_index == GPIO_UART_TXD(s->instance)) {
+        return (s->uintsp & INT_TXD);
+    }
+
+    /* TODO: check if this is correct */
+    if (io_index == GPIO_UART_CTS(s->instance)) {
+        return ~(s->umstat & 0x1);
+    }
+    if (io_index == GPIO_UART_RTS(s->instance)) {
+        return ~(s->umcon  & 0x1);
+    }
+
+    /* TODO: return correct values */
+    if (io_index == GPIO_UART_AUDIO_RXD) {
+        return 0;
+    }
+    if (io_index == GPIO_UART_AUDIO_TXD) {
+        return 0;
+    }
+
+    return 0;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_uart_gpio_readfn   = s5pc1xx_uart_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_uart_gpio_writefn = s5pc1xx_empty_gpio_write;   /* a gag */
+
+static uint32_t s5pc1xx_uart_mm_read(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t res;
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return s->ulcon;
+    case 0x04:
+        return s->ucon;
+    case 0x08:
+        return s->ufcon;
+    case 0x0C:
+        return s->umcon;
+    case 0x10:
+        return s->utrstat;
+    case 0x14:
+        res = s->uerstat;
+        s->uerstat = 0;
+        return res;
+    case 0x18:
+        s->ufstat = queue_elem_count(&s->rx) & 0xff;
+        if (queue_empty_count(&s->rx) == 0) {
+            s->ufstat |= UFSTAT_RX_FIFO_FULL;
+        }
+        return s->ufstat;
+    case 0x1C:
+        return s->umstat;
+    case 0x24:
+        if (s->ufcon & 1) {
+            if (! queue_empty(&s->rx)) {
+                res = queue_get(&s->rx);
+                if (queue_empty(&s->rx)) {
+                    s->utrstat &= ~TRSTATUS_DATA_READY;
+                } else {
+                    s->utrstat |= TRSTATUS_DATA_READY;
+                }
+            } else {
+                s->uintsp |= INT_ERROR;
+                s5pc1xx_uart_update(s);
+                res = 0;
+            }
+        } else {
+            s->utrstat &= ~TRSTATUS_DATA_READY;
+            res = s->urxh;
+        }
+        return res;
+    case 0x28:
+        return s->ubrdiv;
+    case 0x2C:
+        return s->udivslot;
+    case 0x30:
+        return s->uintp;
+    case 0x34:
+        return s->uintsp;
+    case 0x38:
+        return s->uintm;
+    default:
+        hw_error("s5pc1xx.uart: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_uart_mm_write(void *opaque, target_phys_addr_t offset,
+                                  uint32_t val)
+{
+    uint8_t ch;
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->ulcon = val;
+        break;
+    case 0x04:
+        s->ucon = val;
+        break;
+    case 0x08:
+        s->ufcon = val;
+        if (val & 2) {
+            queue_reset(&s->rx);
+        }
+        s->ufcon &= ~6;
+        break;
+    case 0x0C:
+        s->umcon = val;
+        break;
+    case 0x20:
+        if (s->chr) {
+            s->utrstat &= ~(TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY);
+            ch = (uint8_t)val;
+            qemu_chr_write(s->chr, &ch, 1);
+            s->utrstat |= TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY;
+            s->uintsp |= INT_TXD;
+        }
+        break;
+    case 0x28:
+        s->ubrdiv = val;
+        break;
+    case 0x2C:
+        s->udivslot = val;
+        break;
+    case 0x30:
+        s->uintp &= ~val;
+        s->uintsp &= ~val; /* TODO: does this really work in this way??? */
+        break;
+    case 0x34:
+        s->uintsp = val;
+        break;
+    case 0x38:
+        s->uintm = val;
+        break;
+    default:
+        hw_error("s5pc1xx.uart: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+    s5pc1xx_uart_update(s);
+}
+
+CPUReadMemoryFunc * const s5pc1xx_uart_readfn[] = {
+    s5pc1xx_uart_mm_read,
+    s5pc1xx_uart_mm_read,
+    s5pc1xx_uart_mm_read
+};
+
+CPUWriteMemoryFunc * const s5pc1xx_uart_writefn[] = {
+    s5pc1xx_uart_mm_write,
+    s5pc1xx_uart_mm_write,
+    s5pc1xx_uart_mm_write
+};
+
+static void s5pc1xx_uart_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    qemu_put_buffer(f, s->rx.queue, QUEUE_SIZE);
+    qemu_put_be32s(f, &s->rx.s);
+    qemu_put_be32s(f, &s->rx.t);
+    qemu_put_be32s(f, &s->rx.size);
+
+    qemu_put_be32s(f, &s->ulcon);
+    qemu_put_be32s(f, &s->ucon);
+    qemu_put_be32s(f, &s->ufcon);
+    qemu_put_be32s(f, &s->umcon);
+    qemu_put_be32s(f, &s->utrstat);
+    qemu_put_be32s(f, &s->uerstat);
+    qemu_put_be32s(f, &s->ufstat);
+    qemu_put_be32s(f, &s->umstat);
+    qemu_put_be32s(f, &s->utxh);
+    qemu_put_be32s(f, &s->urxh);
+    qemu_put_be32s(f, &s->ubrdiv);
+    qemu_put_be32s(f, &s->udivslot);
+    qemu_put_be32s(f, &s->uintp);
+    qemu_put_be32s(f, &s->uintsp);
+    qemu_put_be32s(f, &s->uintm);
+}
+
+static int s5pc1xx_uart_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_buffer(f, s->rx.queue, QUEUE_SIZE);
+    qemu_get_be32s(f, &s->rx.s);
+    qemu_get_be32s(f, &s->rx.t);
+    qemu_get_be32s(f, &s->rx.size);
+
+    qemu_get_be32s(f, &s->ulcon);
+    qemu_get_be32s(f, &s->ucon);
+    qemu_get_be32s(f, &s->ufcon);
+    qemu_get_be32s(f, &s->umcon);
+    qemu_get_be32s(f, &s->utrstat);
+    qemu_get_be32s(f, &s->uerstat);
+    qemu_get_be32s(f, &s->ufstat);
+    qemu_get_be32s(f, &s->umstat);
+    qemu_get_be32s(f, &s->utxh);
+    qemu_get_be32s(f, &s->urxh);
+    qemu_get_be32s(f, &s->ubrdiv);
+    qemu_get_be32s(f, &s->udivslot);
+    qemu_get_be32s(f, &s->uintp);
+    qemu_get_be32s(f, &s->uintsp);
+    qemu_get_be32s(f, &s->uintm);
+
+    return 0;
+}
+
+static int s5pc1xx_uart_can_receive(void *opaque)
+{
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+    return queue_empty_count(&s->rx);
+}
+
+static void s5pc1xx_uart_trigger_level(S5pc1xxUartState *s)
+{
+    /* TODO: fix this */
+    if (! queue_empty(&s->rx)) {
+        s->uintsp |= INT_RXD;
+    }
+}
+
+static void s5pc1xx_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    int i;
+    S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+    if (s->ufcon & 1) {
+        if (queue_empty_count(&s->rx) < size) {
+            for (i = 0; i < queue_empty_count(&s->rx); i++) {
+                queue_push(&s->rx, buf[i]);
+            }
+            s->uintp |= INT_ERROR;
+            s->utrstat |= TRSTATUS_DATA_READY;
+        } else {
+            for (i = 0; i < size; i++) {
+                queue_push(&s->rx, buf[i]);
+            }
+            s->utrstat |= TRSTATUS_DATA_READY;
+        }
+        s5pc1xx_uart_trigger_level(s);
+    } else {
+        s->urxh = buf[0];
+        s->uintsp |= INT_RXD;
+        s->utrstat |= TRSTATUS_DATA_READY;
+    }
+    s5pc1xx_uart_update(s);
+}
+
+static void s5pc1xx_uart_event(void *opaque, int event)
+{
+    /* TODO: implement this */
+}
+
+static void s5pc1xx_uart_reset(DeviceState *d)
+{
+    S5pc1xxUartState *s =
+        FROM_SYSBUS(S5pc1xxUartState, sysbus_from_qdev(d));
+
+    s->ulcon    = 0;
+    s->ucon     = 0;
+    s->ufcon    = 0;
+    s->umcon    = 0;
+    s->utrstat  = 0x6;
+    s->uerstat  = 0;
+    s->ufstat   = 0;
+    s->umstat   = 0;
+    s->ubrdiv   = 0;
+    s->udivslot = 0;
+    s->uintp    = 0;
+    s->uintsp   = 0;
+    s->uintm    = 0;
+    queue_reset(&s->rx);
+}
+
+DeviceState *s5pc1xx_uart_init(target_phys_addr_t base, int instance,
+                               int queue_size, qemu_irq irq,
+                               CharDriverState *chr)
+{
+    DeviceState *dev = qdev_create(NULL, "s5pc1xx.uart");
+    char str[] = "s5pc1xx.uart.00";
+
+    if (!chr) {
+        snprintf(str, strlen(str) + 1, "s5pc1xx.uart.%02d", instance % 100);
+        chr = qemu_chr_open(str, "null", NULL);
+    }
+    qdev_prop_set_chr(dev, "chr", chr);
+    qdev_prop_set_uint32(dev, "queue-size", queue_size);
+    qdev_prop_set_uint32(dev, "instance", instance);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
+
+static int s5pc1xx_uart_init1(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxUartState *s = FROM_SYSBUS(S5pc1xxUartState, dev);
+
+    s5pc1xx_uart_reset(&s->busdev.qdev);
+
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_chr_add_handlers(s->chr, s5pc1xx_uart_can_receive,
+                          s5pc1xx_uart_receive, s5pc1xx_uart_event, s);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_uart_readfn, s5pc1xx_uart_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_UART_REG_MEM_SIZE, iomemtype);
+
+    s5pc1xx_gpio_register_io_memory(GPIO_IDX_UART, s->instance,
+                                    s5pc1xx_uart_gpio_readfn,
+                                    s5pc1xx_uart_gpio_writefn, NULL, s);
+
+    register_savevm(&dev->qdev, "s5pc1xx.uart", s->instance, 1,
+                    s5pc1xx_uart_save, s5pc1xx_uart_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_uart_info = {
+    .init = s5pc1xx_uart_init1,
+    .qdev.name  = "s5pc1xx.uart",
+    .qdev.size  = sizeof(S5pc1xxUartState),
+    .qdev.reset = s5pc1xx_uart_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("instance",   S5pc1xxUartState, instance, 0),
+        DEFINE_PROP_UINT32("queue-size", S5pc1xxUartState, rx.size, 16),
+        DEFINE_PROP_CHR("chr", S5pc1xxUartState, chr),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_uart_register(void)
+{
+    sysbus_register_withprop(&s5pc1xx_uart_info);
+}
+
+device_init(s5pc1xx_uart_register)
diff --git a/hw/s5pc1xx_usb_otg.c b/hw/s5pc1xx_usb_otg.c
new file mode 100644 (file)
index 0000000..d699933
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * S5PC1XX UART Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "net.h"
+#include "irq.h"
+#include "hw.h"
+#include "s5pc1xx.h"
+
+
+/* Interrupts */
+#define USB_INT_MODEMIS     (1 <<  1) /* Mode Mismatch Interrupt */
+#define USB_INT_OTGINT      (1 <<  2) /* OTG Interrupt */
+#define USB_INT_SOF         (1 <<  3) /* Start of (micro) Frame */
+#define USB_INT_RXFLVL      (1 <<  4) /* RxFIFO Non-Empty */
+#define USB_INT_NPTXFEMP    (1 <<  5) /* Non-periodic TxFIFO Empty */
+#define USB_INT_GINNAKEFF   (1 <<  6) /* Global IN Non-periodic NAK Effective */
+#define USB_INT_GOUTNAKEFF  (1 <<  7) /* Global OUT NAK Effective */
+#define USB_INT_ERLYSUSP    (1 << 10) /* Early Suspend */
+#define USB_INT_USBSUSP     (1 << 11) /* USB Suspend */
+#define USB_INT_USBRST      (1 << 12) /* USB Reset */
+#define USB_INT_ENUMDONE    (1 << 13) /* Enumeration Done */
+#define USB_INT_ISOUTDROP   (1 << 14) /* Isochronous OUT Packet Dropped */
+#define USB_INT_EOPF        (1 << 15) /* End of Periodic Frame */
+#define USB_INT_IEPINT      (1 << 18) /* IN Endpoints Interrupt */
+#define USB_INT_OEPINT      (1 << 19) /* OUT Endpoints Interrupt */
+#define USB_INT_INCOMPLISOIN \
+                            (1 << 20) /* Incomplete Isochronous IN Transfer */
+#define USB_INT_INCOMPLISOOUT \
+                            (1 << 21) /* Incomplete Isochronous OUT Transfer */
+#define USB_INT_FETSUSP     (1 << 22) /* Data Fetch Suspended */
+//#define USB_INT_PRTINT    (1 << 24) /* Host Port Interrupt */
+//#define USB_INT_HCHINT    (1 << 25) /* Host Channels Interrupt */
+#define USB_INT_PTXFEMP     (1 << 26) /* Periodic TxFIFO Empty */
+#define USB_INT_CONIDSTSCHNG \
+                            (1 << 28) /* Connector ID Status Change */
+#define USB_INT_DISCONINT   (1 << 29) /* Disconnect Detected Interrupt */
+#define USB_INT_SESSREQINT  (1 << 30) /* New Session Detected Interrupt */
+#define USB_INT_WKUPINT     (1 << 31) /* Resume Interrupt */
+
+#define EP_INT_XFERCOMPL    (1 <<  0) /* Transfer complete */
+#define EP_INT_EPDISABLED   (1 <<  1) /* Endpoint disabled */
+#define EP_INT_AHBERR       (1 <<  2) /* AHB error */
+#define EP_INT_SETUP        (1 <<  3) /* [OUT] Setup phase done */
+#define EP_INT_TIMEOUT      (1 <<  3) /* [IN] Timeout */
+#define EP_INT_OUTTKNEPDIS  (1 <<  4) /* [OUT] Token Received When EP Disabled */
+#define EP_INT_INTKNFIFOEMP (1 <<  4) /* [IN] Token Received When FIFO is Empty */
+#define EP_INT_STSPHSERCVD  (1 <<  5) /* [OUT] Status Phase Received For Control Write */
+#define EP_INT_INTTKNEPMIS  (1 <<  5) /* [IN] Token Received With EP Missmatch */
+#define EP_INT_BACK2BACK    (1 <<  6) /* [OUT] Back-to-Back SETUP Packets Receive */
+#define EP_INT_INEPNAKEFF   (1 <<  6) /* [IN] Endpoint NAK Effective */
+#define EP_INT_TXFEMP       (1 <<  7) /* Transmit FIFO Empty */
+#define EP_INT_OUTPKTERR    (1 <<  8) /* [OUT] Packet Error */
+#define EP_INT_TXFIFOUNDRN  (1 <<  8) /* [IN] FIFO Underrun */
+#define EP_INT_BNAINTR      (1 <<  9) /* Buffer not Available */
+
+
+#define OTG_EP_DIR_IN       0x80
+#define OTG_EP_DIR_OUT      0
+
+#define OTG_EP_ENABLE       (1U << 31)
+#define OTG_EP_DISABLE      (1 << 30)
+
+#define OTG_EP_COUNT        16
+
+
+typedef enum {
+    OTG_STATE_START = 0,
+    OTG_STATE_RESET,
+    OTG_STATE_SPEEDDETECT,
+    OTG_STATE_SETCONFIG_S,
+    OTG_STATE_SETCONFIG_W,
+    OTG_STATE_SETCONFIG_D,
+    OTG_STATE_SETIFACE_S,
+    OTG_STATE_SETIFACE_W,
+    OTG_STATE_SETIFACE_D,
+    OTG_STATE_OPERATIONAL
+} OtgLogicalState;
+
+typedef struct S5pc1xxUsbOtgEndPoint {
+    uint32_t n;
+    uint32_t ctrl;
+    uint32_t interrupt;
+    uint32_t transfer_size;
+    uint32_t dma_addr;
+    uint32_t dma_buf;
+    uint32_t in_fifo_size;
+
+    uint8_t dir;
+
+    struct S5pc1xxUsbOtgState *parent;
+} S5pc1xxUsbOtgEndPoint;
+
+typedef struct S5pc1xxUsbOtgState {
+    SysBusDevice busdev;
+
+    struct S5pc1xxPhyState {
+        uint32_t power;
+        uint32_t clock;
+        uint32_t reset;
+        uint32_t tune0;
+        uint32_t tune1;
+    } phy;
+
+    uint32_t gotg_ctl;
+    uint32_t gotg_int;
+    uint32_t gahb_cfg;
+    uint32_t gusb_cfg;
+    uint32_t grst_ctl;
+    uint32_t gint_sts;
+    uint32_t gint_msk;
+    uint32_t grx_stsr;
+    uint32_t grx_stsp;
+    uint32_t grx_fsiz;
+    uint32_t gnptx_fsiz;
+    uint32_t gnptx_sts;
+    uint32_t hnptx_fsiz;
+    uint32_t daint_sts;
+    uint32_t daint_msk;
+    uint32_t diep_msk;
+    uint32_t doep_msk;
+
+    S5pc1xxUsbOtgEndPoint ep_in[OTG_EP_COUNT];
+    S5pc1xxUsbOtgEndPoint ep_out[OTG_EP_COUNT];
+
+    OtgLogicalState state;
+
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+    uint8_t buf[1600];
+    uint32_t buf_size;
+    uint8_t buf_full;
+} S5pc1xxUsbOtgState;
+
+
+static const uint8_t otg_setup_packet[] = {
+    0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00
+};
+static const uint8_t otg_setup_iface[] = {
+    0x01, 0x0B, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00
+};
+static const uint8_t otg_setup_config[] = {
+    0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void s5pc1xx_usb_otg_update_irq(S5pc1xxUsbOtgState *s)
+{
+    if (s->gint_sts & s->gint_msk) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static void s5pc1xx_usb_otg_initial_reset(DeviceState *d)
+{
+    S5pc1xxUsbOtgState *s =
+        FROM_SYSBUS(S5pc1xxUsbOtgState, sysbus_from_qdev(d));
+    int i;
+
+    s->phy.power = 0x000001F9;
+    s->phy.clock = 0x00000000;
+    s->phy.reset = 0x00000009; /* TODO: I believe it should be 0 */
+    s->phy.tune0 = 0x000919B3;
+    s->phy.tune1 = 0x000919B3;
+
+    s->gotg_ctl = 0x00010000;
+    s->gotg_int = 0x00000000;
+    s->gahb_cfg = 0x00000000;
+    s->gusb_cfg = 0x00001408;
+    s->grst_ctl = 0x80000000;
+    s->gint_sts = 0x04000020;
+    s->gint_msk = 0x00000000;
+
+    for (i = 0; i < OTG_EP_COUNT; i++) {
+        s->ep_in[i].parent = s;
+        s->ep_in[i].dir = OTG_EP_DIR_IN;
+        s->ep_in[i].n = i;
+        s->ep_out[i].parent = s;
+        s->ep_out[i].dir = OTG_EP_DIR_OUT;
+        s->ep_out[i].n = i;
+    }
+
+    s->state = OTG_STATE_START;
+    s->buf_full = 0;
+}
+
+static void s5pc1xx_usb_otg_reset(S5pc1xxUsbOtgState *s)
+{
+    s5pc1xx_usb_otg_initial_reset(&s->busdev.qdev);
+    s->state = OTG_STATE_RESET;
+    s->gotg_ctl += 0x000C0000;
+    s->gint_sts |= USB_INT_USBRST;
+}
+
+static uint32_t s5pc1xx_usb_otg_phy_mm_read(void *opaque,
+                                            target_phys_addr_t offset)
+{
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        return s->phy.power;
+    case 0x04:
+        return s->phy.clock;
+    case 0x08:
+        return s->phy.reset;
+    case 0x20:
+        return s->phy.tune0;
+    case 0x24:
+        return s->phy.tune1;
+    default:
+        hw_error("s5pc1xx.usb_otg: bad read offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_usb_otg_phy_mm_write(void *opaque, target_phys_addr_t offset,
+                                         uint32_t val)
+{
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+    switch (offset) {
+    case 0x00:
+        s->phy.power = val;
+        break;
+    case 0x04:
+        s->phy.clock = val;
+        break;
+    case 0x08:
+        /* TODO: actually reset USB OTG */
+        if (val & 0x1f) {
+            s5pc1xx_usb_otg_reset(s);
+            s->gint_sts |= USB_INT_USBRST;
+            s5pc1xx_usb_otg_update_irq(s);
+        }
+        break;
+    case 0x20:
+        s->phy.tune0 = val;
+        break;
+    case 0x24:
+        s->phy.tune1 = val;
+        break;
+    default:
+        hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+                 offset);
+    }
+}
+
+static void s5pc1xx_usb_otg_ep_update_irq(S5pc1xxUsbOtgEndPoint *s)
+{
+    if (s->interrupt) {
+        if (s->dir == OTG_EP_DIR_IN) {
+            s->parent->daint_sts |= (1 << s->n);
+            if (s->parent->daint_sts & s->parent->daint_msk & 0xffff) {
+                s->parent->gint_sts |= USB_INT_IEPINT;
+            } else {
+                s->parent->gint_sts &= ~USB_INT_IEPINT;
+            }
+        } else {
+            s->parent->daint_sts |= (1 << (s->n + 16));
+            if (s->parent->daint_sts & s->parent->daint_msk & 0xffff0000) {
+                s->parent->gint_sts |= USB_INT_OEPINT;
+            } else {
+                s->parent->gint_sts &= ~USB_INT_OEPINT;
+            }
+        }
+    } else {
+        if (s->dir == OTG_EP_DIR_IN) {
+            s->parent->daint_sts &= ~(1 << s->n);
+            if (s->parent->daint_sts & s->parent->daint_msk & 0xffff) {
+                s->parent->gint_sts |= USB_INT_IEPINT;
+            } else {
+                s->parent->gint_sts &= ~USB_INT_IEPINT;
+            }
+        } else {
+            s->parent->daint_sts &= ~(1 << (s->n + 16));
+            if (s->parent->daint_sts & s->parent->daint_msk & 0xffff0000) {
+                s->parent->gint_sts |= USB_INT_OEPINT;
+            } else {
+                s->parent->gint_sts &= ~USB_INT_OEPINT;
+            }
+        }
+    }
+    s5pc1xx_usb_otg_update_irq(s->parent);
+}
+
+static void s5pc1xx_usb_otg_act(S5pc1xxUsbOtgState *s)
+{
+    switch (s->state) {
+        case OTG_STATE_START:
+        case OTG_STATE_RESET:
+        case OTG_STATE_SPEEDDETECT:
+            break;
+        case OTG_STATE_SETCONFIG_S:
+            if (s->ep_out[0].ctrl & OTG_EP_ENABLE) {
+                s->state = OTG_STATE_SETCONFIG_W;
+                cpu_physical_memory_write(s->ep_out[0].dma_addr,
+                                          otg_setup_config, 8);
+                s->ep_out[0].ctrl &= ~OTG_EP_ENABLE;
+                s->ep_out[0].interrupt |= EP_INT_SETUP|EP_INT_XFERCOMPL;
+                s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[0]);
+            }
+            break;
+        case OTG_STATE_SETIFACE_S:
+            if (s->ep_out[0].ctrl & OTG_EP_ENABLE) {
+                s->state = OTG_STATE_SETIFACE_W;
+                cpu_physical_memory_write(s->ep_out[0].dma_addr,
+                                          otg_setup_iface, 8);
+                s->ep_out[0].ctrl &= ~OTG_EP_ENABLE;
+                s->ep_out[0].interrupt |= EP_INT_SETUP|EP_INT_XFERCOMPL;
+                s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[0]);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+static void s5pc1xx_usb_otg_data_tx(S5pc1xxUsbOtgEndPoint *s)
+{
+    uint8_t buf[1600];
+    uint32_t size = s->transfer_size & 0x7ffff;
+
+    cpu_physical_memory_read(s->dma_addr, buf, size);
+    qemu_send_packet(&s->parent->nic->nc, buf, size);
+    s->interrupt |= EP_INT_XFERCOMPL|EP_INT_TXFEMP;
+    s5pc1xx_usb_otg_ep_update_irq(s);
+}
+
+static void s5pc1xx_usb_otg_data_rx(S5pc1xxUsbOtgEndPoint *s)
+{
+    uint32_t size = s->parent->buf_size;
+
+    if (s->parent->buf_size > (s->transfer_size & 0x7ffff)) {
+        s->parent->buf_full = 0;
+        /* Packet dropped */
+        return ;
+    }
+    cpu_physical_memory_write(s->dma_addr, s->parent->buf, size);
+    s->dma_buf = s->dma_addr + size;
+    s->transfer_size -= size;
+    s->ctrl &= ~OTG_EP_ENABLE;
+    s->interrupt |= EP_INT_XFERCOMPL;
+    s->parent->buf_full = 0;
+    s5pc1xx_usb_otg_ep_update_irq(s);
+}
+
+static uint32_t s5pc1xx_usb_otg_ep_read(S5pc1xxUsbOtgEndPoint *s,
+                                        target_phys_addr_t addr)
+{
+    switch (addr) {
+    case 0x00:
+        return s->ctrl;
+    case 0x08:
+        return s->interrupt;
+    case 0x10:
+        return s->transfer_size;
+    case 0x14:
+        return s->dma_addr;
+    case 0x1C:
+        return s->dma_buf;
+    default:
+        hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+                 addr);
+    }
+}
+
+static uint32_t s5pc1xx_usb_otg_ep_write(S5pc1xxUsbOtgEndPoint *s,
+                                         target_phys_addr_t addr, uint32_t val)
+{
+    switch (addr) {
+    case 0x00:
+        if ((val & OTG_EP_DISABLE) && (s->ctrl & OTG_EP_ENABLE)) {
+            s->ctrl &= ~OTG_EP_ENABLE;
+            val &= ~OTG_EP_DISABLE;
+            s->interrupt |= EP_INT_EPDISABLED;
+            s5pc1xx_usb_otg_ep_update_irq(s);
+        }
+        val &= ~OTG_EP_DISABLE;
+        if (val & OTG_EP_ENABLE) {
+            s5pc1xx_usb_otg_act(s->parent);
+            if (s->n == 0 && s->dir == OTG_EP_DIR_IN) {
+                s->interrupt |= EP_INT_XFERCOMPL|EP_INT_TXFEMP;
+                if (s->parent->state == OTG_STATE_SETCONFIG_W) {
+                    s->parent->state = OTG_STATE_SETIFACE_S;
+                } else if (s->parent->state == OTG_STATE_SETIFACE_W) {
+                    s->parent->state = OTG_STATE_OPERATIONAL;
+                }
+                s5pc1xx_usb_otg_ep_update_irq(s);
+            }
+            if (s->n != 0 && s->dir == OTG_EP_DIR_IN) {
+                s5pc1xx_usb_otg_data_tx(s);
+                val &= ~OTG_EP_ENABLE;
+            }
+            if (s->n != 0 && s->dir == OTG_EP_DIR_OUT &&
+                s->parent->buf_full == 1) {
+                s5pc1xx_usb_otg_data_rx(s);
+                val &= ~OTG_EP_ENABLE;
+            }
+        }
+        val &= ~(0xC << 24); /* TODO: handle NAK? */
+        s->ctrl = val;
+        /* TODO: handle control */
+        break;
+    case 0x08:
+        s->interrupt &= ~val;
+        s5pc1xx_usb_otg_ep_update_irq(s);
+        break;
+    case 0x10:
+        s->transfer_size = val;
+        break;
+    case 0x14:
+        s->dma_addr = val;
+        break;
+    default:
+        hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+                 addr);
+    }
+    return 0;
+}
+
+static uint32_t s5pc1xx_usb_otg_read(void *opaque, target_phys_addr_t addr)
+{
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+    if (addr >= 0x100000 && addr < 0x100028) {
+        return s5pc1xx_usb_otg_phy_mm_read(opaque, addr - 0x100000);
+    }
+
+    switch (addr) {
+    case 0x00:
+        return s->gotg_ctl;
+    case 0x04:
+        return s->gotg_int;
+    case 0x08:
+        return s->gahb_cfg;
+    case 0x0C:
+        return s->gusb_cfg;
+    case 0x10:
+        return s->grst_ctl;
+    case 0x14:
+        return s->gint_sts;
+    case 0x18:
+        return s->gint_msk;
+    case 0x1C:
+        return s->grx_stsr;
+    case 0x20:
+        return s->grx_stsp;
+    case 0x24:
+        return s->grx_fsiz;
+    case 0x28:
+        return s->gnptx_fsiz;
+    case 0x2C:
+        return s->gnptx_sts;
+    case 0x30:
+        return s->hnptx_fsiz;
+    case 0x100 ... 0x13C:
+        return s->ep_in[(addr - 0x100) >> 2].in_fifo_size;
+    case 0x810:
+        return s->diep_msk;
+    case 0x814:
+        return s->doep_msk;
+    case 0x818:
+        return s->daint_sts;
+    case 0x81C:
+        return s->daint_msk;
+    case 0x900 ... 0xAFC:
+        addr -= 0x900;
+        return s5pc1xx_usb_otg_ep_read(&s->ep_in[addr >> 5], addr & 0x1f);
+    case 0xB00 ... 0xCFC:
+        addr -= 0xB00;
+        return s5pc1xx_usb_otg_ep_read(&s->ep_out[addr >> 5], addr & 0x1f);
+    }
+    return 0;
+}
+
+static void s5pc1xx_usb_otg_write(void *opaque, target_phys_addr_t addr,
+                                  uint32_t val)
+{
+    int i;
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+    if (addr >= 0x100000 && addr < 0x100028) {
+        s5pc1xx_usb_otg_phy_mm_write(opaque, addr - 0x100000, val);
+        return;
+    }
+
+    switch (addr) {
+    case 0x00:
+        s->gotg_ctl = val;
+        break;
+    case 0x04:
+        s->gotg_int &= ~val;
+        s5pc1xx_usb_otg_update_irq(s);
+        break;
+    case 0x08:
+        s->gahb_cfg = val;
+        break;
+    case 0x0C:
+        s->gusb_cfg = val;
+        break;
+    case 0x10:
+        if (val & 1) {
+            s5pc1xx_usb_otg_reset(s);
+            s->gint_sts |= USB_INT_USBRST;
+            s->state = OTG_STATE_RESET;
+            s5pc1xx_usb_otg_update_irq(s);
+        } else if (val & 0x0f) {
+            s->gint_sts |= USB_INT_USBRST;
+            s->state = OTG_STATE_RESET;
+            s5pc1xx_usb_otg_update_irq(s);
+        }
+        s->grst_ctl = val & (~0x3f);
+        s5pc1xx_usb_otg_update_irq(s);
+        break;
+    case 0x14:
+        val &= ~(7 << 24);
+        val &= ~(3 << 18);
+        val &= ~(0xf << 4);
+        val &= ~0x5;
+        s->gint_sts &= ~val;
+        if (val == 0x1000) {
+            s->gint_sts |= USB_INT_ENUMDONE;
+            s->state = OTG_STATE_SPEEDDETECT;
+            s5pc1xx_usb_otg_act(s);
+        }
+        if (val == USB_INT_ENUMDONE) {
+            s->state = OTG_STATE_SETCONFIG_S;
+            s5pc1xx_usb_otg_act(s);
+        }
+        s5pc1xx_usb_otg_update_irq(s);
+        break;
+    case 0x18:
+        s->gint_msk = val;
+        break;
+    case 0x24:
+        s->grx_fsiz = val;
+        break;
+    case 0x28:
+        s->gnptx_fsiz = val;
+        break;
+    case 0x30:
+        s->hnptx_fsiz = val;
+        break;
+    case 0x100 ... 0x13C:
+        s->ep_in[(addr - 0x100 ) >> 2].in_fifo_size = val;
+        break;
+    case 0x810:
+        s->diep_msk = val;
+        for (i = 0; i < OTG_EP_COUNT; i++) {
+            s5pc1xx_usb_otg_ep_update_irq(&s->ep_in[i]);
+        }
+        break;
+    case 0x814:
+        s->doep_msk = val;
+        for (i = 0; i < OTG_EP_COUNT; i++) {
+            s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[i]);
+        }
+        break;
+    case 0x81C:
+        s->daint_msk = val;
+        if (s->daint_sts & 0xffff & s->daint_msk) {
+            s->gint_sts |= USB_INT_IEPINT;
+        } else {
+            s->gint_sts &= ~USB_INT_IEPINT;
+        }
+        if ((s->daint_sts & s->daint_msk) >> 16) {
+            s->gint_sts |= USB_INT_OEPINT;
+        } else {
+            s->gint_sts &= ~USB_INT_OEPINT;
+        }
+        s5pc1xx_usb_otg_update_irq(s);
+        break;
+    case 0x900 ... 0xAFC:
+        addr -= 0x900;
+        s5pc1xx_usb_otg_ep_write(&s->ep_in[addr >> 5], addr & 0x1f, val);
+        break;
+    case 0xB00 ... 0xCFC:
+        addr -= 0xB00;
+        s5pc1xx_usb_otg_ep_write(&s->ep_out[addr >> 5], addr & 0x1f, val);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_usb_otg_readfn[] = {
+    s5pc1xx_usb_otg_read,
+    s5pc1xx_usb_otg_read,
+    s5pc1xx_usb_otg_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_usb_otg_writefn[] = {
+    s5pc1xx_usb_otg_write,
+    s5pc1xx_usb_otg_write,
+    s5pc1xx_usb_otg_write
+};
+
+static void s5pc1xx_usb_otg_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->phy.power);
+    qemu_put_be32s(f, &s->phy.clock);
+    qemu_put_be32s(f, &s->phy.reset);
+    qemu_put_be32s(f, &s->phy.tune0);
+    qemu_put_be32s(f, &s->phy.tune1);
+
+    qemu_put_be32s(f, &s->gotg_ctl);
+    qemu_put_be32s(f, &s->gotg_int);
+    qemu_put_be32s(f, &s->gahb_cfg);
+    qemu_put_be32s(f, &s->gusb_cfg);
+    qemu_put_be32s(f, &s->grst_ctl);
+    qemu_put_be32s(f, &s->gint_sts);
+    qemu_put_be32s(f, &s->gint_msk);
+    qemu_put_be32s(f, &s->grx_stsr);
+    qemu_put_be32s(f, &s->grx_stsp);
+    qemu_put_be32s(f, &s->grx_fsiz);
+    qemu_put_be32s(f, &s->gnptx_fsiz);
+    qemu_put_be32s(f, &s->gnptx_sts);
+    qemu_put_be32s(f, &s->hnptx_fsiz);
+    qemu_put_be32s(f, &s->daint_sts);
+    qemu_put_be32s(f, &s->daint_msk);
+    qemu_put_be32s(f, &s->diep_msk);
+    qemu_put_be32s(f, &s->doep_msk);
+    qemu_put_byte (f, (uint8_t) s->state);
+
+    for (i = 0; i < OTG_EP_COUNT; i++) {
+        qemu_put_be32s(f, &s->ep_in[i].ctrl);
+        qemu_put_be32s(f, &s->ep_in[i].interrupt);
+        qemu_put_be32s(f, &s->ep_in[i].transfer_size);
+        qemu_put_be32s(f, &s->ep_in[i].dma_addr);
+        qemu_put_be32s(f, &s->ep_in[i].dma_buf);
+        qemu_put_be32s(f, &s->ep_in[i].in_fifo_size);
+
+        qemu_put_be32s(f, &s->ep_out[i].ctrl);
+        qemu_put_be32s(f, &s->ep_out[i].interrupt);
+        qemu_put_be32s(f, &s->ep_out[i].transfer_size);
+        qemu_put_be32s(f, &s->ep_out[i].dma_addr);
+        qemu_put_be32s(f, &s->ep_out[i].dma_buf);
+        qemu_put_be32s(f, &s->ep_out[i].in_fifo_size);
+    }
+    /* FIXME: must the structure below be saved?
+     * NICState *nic; */
+    qemu_put_be32s (f, &s->buf_size);
+    qemu_put_buffer(f, s->buf, s->buf_size);
+    qemu_put_8s    (f, &s->buf_full);
+}
+
+static int s5pc1xx_usb_otg_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    qemu_get_be32s(f, &s->phy.power);
+    qemu_get_be32s(f, &s->phy.clock);
+    qemu_get_be32s(f, &s->phy.reset);
+    qemu_get_be32s(f, &s->phy.tune0);
+    qemu_get_be32s(f, &s->phy.tune1);
+
+    qemu_get_be32s(f, &s->gotg_ctl);
+    qemu_get_be32s(f, &s->gotg_int);
+    qemu_get_be32s(f, &s->gahb_cfg);
+    qemu_get_be32s(f, &s->gusb_cfg);
+    qemu_get_be32s(f, &s->grst_ctl);
+    qemu_get_be32s(f, &s->gint_sts);
+    qemu_get_be32s(f, &s->gint_msk);
+    qemu_get_be32s(f, &s->grx_stsr);
+    qemu_get_be32s(f, &s->grx_stsp);
+    qemu_get_be32s(f, &s->grx_fsiz);
+    qemu_get_be32s(f, &s->gnptx_fsiz);
+    qemu_get_be32s(f, &s->gnptx_sts);
+    qemu_get_be32s(f, &s->hnptx_fsiz);
+    qemu_get_be32s(f, &s->daint_sts);
+    qemu_get_be32s(f, &s->daint_msk);
+    qemu_get_be32s(f, &s->diep_msk);
+    qemu_get_be32s(f, &s->doep_msk);
+    s->state = qemu_get_byte(f);
+
+    for (i = 0; i < OTG_EP_COUNT; i++) {
+        qemu_get_be32s(f, &s->ep_in[i].ctrl);
+        qemu_get_be32s(f, &s->ep_in[i].interrupt);
+        qemu_get_be32s(f, &s->ep_in[i].transfer_size);
+        qemu_get_be32s(f, &s->ep_in[i].dma_addr);
+        qemu_get_be32s(f, &s->ep_in[i].dma_buf);
+        qemu_get_be32s(f, &s->ep_in[i].in_fifo_size);
+
+        qemu_get_be32s(f, &s->ep_out[i].ctrl);
+        qemu_get_be32s(f, &s->ep_out[i].interrupt);
+        qemu_get_be32s(f, &s->ep_out[i].transfer_size);
+        qemu_get_be32s(f, &s->ep_out[i].dma_addr);
+        qemu_get_be32s(f, &s->ep_out[i].dma_buf);
+        qemu_get_be32s(f, &s->ep_out[i].in_fifo_size);
+    }
+    /* FIXME: NICState *nic; */
+    qemu_get_be32s (f, &s->buf_size);
+    qemu_get_buffer(f, s->buf, s->buf_size);
+    qemu_get_8s    (f, &s->buf_full);
+
+    return 0;
+}
+
+static int s5pc1xx_usb_otg_can_receive(VLANClientState *nc)
+{
+    S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+
+    return !s->buf_full;
+}
+
+static ssize_t s5pc1xx_usb_otg_receive(VLANClientState *nc, const uint8_t *buf,
+                                       size_t size)
+{
+    S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+    int i;
+
+    s->buf_full = 1;
+    s->buf_size = size;
+    memcpy(s->buf, buf, size);
+    for (i = 0; i < OTG_EP_COUNT; i++) {
+        if (s->ep_out[i].ctrl & OTG_EP_ENABLE) {
+            s5pc1xx_usb_otg_data_rx(&s->ep_out[i]);
+            break;
+        }
+    }
+    return size;
+}
+
+static void s5pc1xx_usb_otg_cleanup(VLANClientState *nc)
+{
+    S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_s5pc1xx_usb_otg_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = s5pc1xx_usb_otg_can_receive,
+    .receive = s5pc1xx_usb_otg_receive,
+    .cleanup = s5pc1xx_usb_otg_cleanup,
+};
+
+static int s5pc1xx_usb_otg_init1(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxUsbOtgState *s = FROM_SYSBUS(S5pc1xxUsbOtgState, dev);
+
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_usb_otg_readfn,
+                               s5pc1xx_usb_otg_writefn, s, DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, 0x100030, iomemtype);
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s5pc1xx_usb_otg_initial_reset(&s->busdev.qdev);
+
+    s->nic = qemu_new_nic(&net_s5pc1xx_usb_otg_info, &s->conf,
+                          dev->qdev.info->name, dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+    register_savevm(&dev->qdev, "s5pc1xx.usb.otg", -1, 1,
+                    s5pc1xx_usb_otg_save, s5pc1xx_usb_otg_load, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_usb_otg_info = {
+    .init = s5pc1xx_usb_otg_init1,
+    .qdev.name  = "s5pc1xx.usb.otg",
+    .qdev.size  = sizeof(S5pc1xxUsbOtgState),
+    .qdev.reset = s5pc1xx_usb_otg_initial_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_NIC_PROPERTIES(S5pc1xxUsbOtgState, conf),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void s5pc1xx_usb_otg_register_devices(void)
+{
+    sysbus_register_withprop(&s5pc1xx_usb_otg_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void s5pc1xx_usb_otg_init(NICInfo *nd, target_phys_addr_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "s5pc1xx-usb-otg");
+    dev = qdev_create(NULL, "s5pc1xx.usb.otg");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+device_init(s5pc1xx_usb_otg_register_devices)
diff --git a/hw/s5pc1xx_wdt.c b/hw/s5pc1xx_wdt.c
new file mode 100644 (file)
index 0000000..f248d44
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Watchdog Timer for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "sysemu.h"
+
+
+#define WTCON       0x00    /* R/W Watchdog Timer Control Register */
+#define WTDAT       0x04    /* R/W Watchdog Timer Data Register */
+#define WTCNT       0x08    /* R/W Watchdog Timer Count Register */
+#define WTCLRINT    0x0C    /* W Watchdog Timer Interrupt Clear Register */
+
+#define PRESCALER_SHIFT (8)
+#define WDT_EN          (1 << 5)
+#define CLK_SEL_SHIFT   (3)
+#define INT_EN          (1 << 2)
+#define RESET_EN        (1 << 0)
+
+#define S5PC1XX_WDT_REG_MEM_SIZE 0x14
+
+
+typedef struct S5pc1xxWDTState {
+    SysBusDevice busdev;
+
+    qemu_irq  irq;
+    uint32_t  regs[WTCNT + 1];
+
+    QEMUTimer *wdt_timer;
+    uint32_t  freq_out;
+    uint64_t  ticnto_last_tick;
+} S5pc1xxWDTState;
+
+
+/* Timer step */
+static void s5pc1xx_wdt_tick(void *opaque)
+{
+    S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+    uint64_t next_wdt_time;
+
+    if (s->regs[WTCON] & WDT_EN) {
+        if (s->regs[WTCON] & INT_EN)
+            qemu_irq_raise(s->irq);
+
+        if (s->regs[WTCON] & RESET_EN) {
+            qemu_system_reset_request();
+        }
+
+        s->ticnto_last_tick = qemu_get_clock(vm_clock);
+        next_wdt_time = s->ticnto_last_tick +
+            muldiv64(s->regs[WTDAT], get_ticks_per_sec(), s->freq_out);
+        qemu_mod_timer(s->wdt_timer, next_wdt_time);
+    } else {
+        s->ticnto_last_tick = 0;
+        qemu_del_timer(s->wdt_timer);
+    }
+}
+
+/* Perform timer step, update frequency and compute next time for update */
+static void s5pc1xx_wdt_update(S5pc1xxWDTState *s)
+{
+    short div_fac;
+    short prescaler;
+    S5pc1xxClk clk;
+
+    clk = s5pc1xx_findclk("pclk_66");
+    div_fac = 16 << (s->regs[WTCON] >> CLK_SEL_SHIFT & 0x3);
+    prescaler = s->regs[WTCON] >> PRESCALER_SHIFT & 0xff;
+
+    s->freq_out = s5pc1xx_clk_getrate(clk) / (prescaler + 1) / div_fac;
+
+    if (!s->freq_out)
+        hw_error("s5pc1xx.wdt: timer update input frequency is zero\n");
+}
+
+/* WDT read */
+static uint32_t s5pc1xx_wdt_read(void *opaque, target_phys_addr_t offset)
+{
+    S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+
+    /* check if offset is correct, WTCLRINT is not for read */
+    if (offset <= WTCNT) {
+        if (offset == WTCNT) {
+            if (s->freq_out && s->ticnto_last_tick && s->regs[WTDAT]) {
+                s->regs[WTCNT] = s->regs[WTDAT] -
+                    muldiv64(qemu_get_clock(vm_clock) - s->ticnto_last_tick,
+                             s->freq_out, get_ticks_per_sec()) %
+                        s->regs[WTDAT];
+            } else
+                s->regs[WTCNT] = 0;
+        }
+        return s->regs[offset];
+    } else {
+        hw_error("s5pc1xx.wdt: bad read offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+/* WDT write */
+static void s5pc1xx_wdt_write(void *opaque, target_phys_addr_t offset,
+                              uint32_t value)
+{
+    S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+
+    switch (offset) {
+    case WTCON:
+        if ((value & WDT_EN) > (s->regs[WTCON] & WDT_EN)) {
+            s->regs[WTCON] = value;
+            s5pc1xx_wdt_update(s);
+            s5pc1xx_wdt_tick(s);
+        }
+        s->regs[WTCON] = value;
+        s5pc1xx_wdt_update(s);
+        break;
+    case WTDAT:
+    case WTCNT:
+        s->regs[offset] = value;
+        break;
+    case WTCLRINT:
+        qemu_irq_lower(s->irq);
+        break;
+    default:
+        hw_error("s5pc1xx.wdt: bad write offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_wdt_readfn[] = {
+    s5pc1xx_wdt_read,
+    s5pc1xx_wdt_read,
+    s5pc1xx_wdt_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_wdt_writefn[] = {
+    s5pc1xx_wdt_write,
+    s5pc1xx_wdt_write,
+    s5pc1xx_wdt_write
+};
+
+static void s5pc1xx_wdt_save(QEMUFile *f, void *opaque)
+{
+    S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+    int i;
+
+    for (i = 0; i <= WTCNT; i++) {
+        qemu_put_be32s(f, &s->regs[i]);
+    }
+
+    qemu_put_be64s(f, &s->ticnto_last_tick);
+    qemu_put_be32s(f, &s->freq_out);
+    qemu_put_timer(f, s->wdt_timer);
+}
+
+static int s5pc1xx_wdt_load(QEMUFile *f, void *opaque, int version_id)
+{
+    S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (i = 0; i <= WTCNT; i++) {
+        qemu_get_be32s(f, &s->regs[i]);
+    }
+
+    qemu_get_be64s(f, &s->ticnto_last_tick);
+    qemu_get_be32s(f, &s->freq_out);
+    qemu_get_timer(f, s->wdt_timer);
+
+    return 0;
+}
+
+/* WDT init */
+static int s5pc1xx_wdt_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    S5pc1xxWDTState *s = FROM_SYSBUS(S5pc1xxWDTState, dev);
+
+    sysbus_init_irq(dev, &s->irq);
+    iomemtype =
+        cpu_register_io_memory(s5pc1xx_wdt_readfn, s5pc1xx_wdt_writefn, s,
+                               DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, S5PC1XX_WDT_REG_MEM_SIZE, iomemtype);
+
+    s->wdt_timer = qemu_new_timer(vm_clock, s5pc1xx_wdt_tick, s);
+
+    s->regs[WTDAT] = 0x00008000;
+    s->regs[WTCNT] = 0x00008000;
+    /* initially WDT is stopped */
+    s5pc1xx_wdt_write(s, WTCON, 0x00008001);
+
+    register_savevm(&dev->qdev, "s5pc1xx.wdt", -1, 1,
+                    s5pc1xx_wdt_save, s5pc1xx_wdt_load, s);
+
+    return 0;
+}
+
+static void s5pc1xx_wdt_register_devices(void)
+{
+    sysbus_register_dev("s5pc1xx.wdt", sizeof(S5pc1xxWDTState),
+                        s5pc1xx_wdt_init);
+}
+
+device_init(s5pc1xx_wdt_register_devices)
diff --git a/hw/smb380.c b/hw/smb380.c
new file mode 100644 (file)
index 0000000..067f335
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * SMB380 Sensor Emulation
+ *
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#else
+#include <windows.h>
+#include <winsock2.h>
+#include <sys/types.h>
+#endif
+
+#include "i2c-addressable.h"
+
+//#define DEBUG
+
+typedef struct SensorState {
+    I2CAddressableState i2c_addressable;
+       char data[7];
+
+       int16_t x;
+       int16_t y;
+       int16_t z;
+    int idx_out, req_out;
+}SensorState;
+
+SensorState glob_accel_state;
+
+#define BITS_PER_BYTE 8
+#define SMB380_ACCEL_BITS 10
+
+int sensor_update(uint16_t x, uint16_t y, uint16_t z)
+{
+       glob_accel_state.x = x;
+       glob_accel_state.y = y;
+       glob_accel_state.z = z;
+
+       return 0;
+}
+
+static int sensor_xyz_set(struct SensorState *s, uint16_t x, uint16_t y, uint16_t z)
+{
+       /* remains right 10bit */
+
+       x <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+       x >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+       /* data[1] : 2 ~ 10  (bit)
+        * data[0] : 0 ~ 1 (bit) */
+
+       s->data[0] = (0x3 & x) << 6;
+       s->data[1] = x >> 2;
+       s->data[2] = (0x3 & y) << 6;
+       s->data[3] = y >> 2;
+       s->data[4] = (0x3 & z) << 6;
+       s->data[5] = z >> 2;
+
+       return 0;
+}
+
+static void smb380_reset(struct SensorState *s)
+{
+    s->idx_out = 0;
+
+       glob_accel_state.x = 0;
+       glob_accel_state.y = -256;
+       glob_accel_state.z = 0;
+
+       s->data[0] = 0;
+       s->data[1] = 0;
+       s->data[2] = 0;
+       s->data[3] = 0;
+       s->data[4] = 0;
+       s->data[5] = 0;
+       s->idx_out = 0;
+
+       return ;
+}
+
+static uint8_t smb380_read(void *opaque, uint32_t address, uint8_t offset)
+{
+    SensorState *s = (SensorState *)opaque;
+       int index = 0;
+
+    index = s->idx_out;
+
+#ifdef DEBUG
+       printf("smb380_read IDX = %d, Data=%d\n", index, s->data[index]);
+#endif
+
+       s->idx_out ++;
+
+       return s->data[index];
+}
+
+#ifdef DEBUG
+static int print_hex(char *data, int len)
+{
+       return 0;
+}
+#endif
+
+static int parse_val(char *buff, unsigned char find_data, char *parsebuff)
+{
+
+       int vvvi = 0;
+       while (1) {
+               if (vvvi > 40) {
+                       return -1;
+               }
+               if (buff[vvvi] == find_data) {
+                               
+                       vvvi++;
+                       strncpy(parsebuff, buff, vvvi);
+                       return vvvi;
+               }
+               vvvi++;
+       }
+
+       return 0;
+}
+
+#define  SENSOR_BUF                            16
+#define  SENSOR_VALUE_BUF              56
+static int get_from_ide(struct SensorState *accel_state) 
+{
+       int client_socket;
+       struct  sockaddr_in   server_addr;
+       char    buff[SENSOR_VALUE_BUF + 1];
+       char    buff2[SENSOR_VALUE_BUF + 1];
+       char    tmpbuf[SENSOR_VALUE_BUF + 1];
+       int             len = 0, len1 = 0;
+       int             result = 0;
+       const char *command0 = "readSensor()\n";
+       const char *command1 = "accelerometer\n";
+
+#ifdef _WIN32
+       WSADATA wsaData;
+       if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
+               return -1;
+
+       client_socket = socket(AF_INET, SOCK_STREAM, 0);
+#else
+       client_socket = socket(PF_INET, SOCK_STREAM, 0);
+#endif
+       if (client_socket == -1) {
+               return -1;
+       }
+
+       memset(&server_addr, 0, sizeof( server_addr));
+       server_addr.sin_family     = AF_INET;
+       server_addr.sin_port       = htons(8010);
+       server_addr.sin_addr.s_addr= inet_addr("127.0.0.1");
+       if( -1 == connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) {
+               close( client_socket);
+               return -1;
+       }
+
+       if(read (client_socket, buff, SENSOR_BUF) < 0) {
+               close(client_socket);
+               return -1;
+       }
+
+       if(1) {
+               if(write(client_socket, command0, strlen(command0)) < 0) {
+               close(client_socket);
+               return -1;
+               }
+               if(write(client_socket, command1, strlen(command1)) < 0) {
+               close(client_socket);
+               return -1;
+               }
+
+               memset(buff, '\0', sizeof(buff));
+               memset(buff2, '\0', sizeof(buff2));
+
+               result = read(client_socket, buff2, SENSOR_VALUE_BUF);
+               if (result <= (SENSOR_VALUE_BUF)/2) {
+                       close( client_socket);
+                       return -1;
+               }
+               memcpy(buff, buff2, result);
+       }
+
+#ifdef DEBUG
+       print_hex(buff2, 90);
+#endif
+
+       /* start */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len = parse_val(buff2, 0x33, tmpbuf);
+
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len += parse_val(buff2+len, 0x0a, tmpbuf);
+
+       /* first data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       //accel.read_accelx = atof(tmpbuf);
+       accel_state->x = (int)(atof(tmpbuf) * (-26.2)); // 26 ~= 256 / 9.8
+       if (accel_state->x > 512)
+               accel_state->x = 512;
+       if (accel_state->x < -512)
+               accel_state->x = -512;
+
+       /* second data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       accel_state->y = (int)(atof(tmpbuf) * 26.2);
+       if (accel_state->y > 512)
+               accel_state->y = 512;
+       if (accel_state->y < -512)
+               accel_state->y = -512;
+
+       /* third data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       accel_state->z = (int)(atof(tmpbuf) * 26);
+
+       if (accel_state->z > 512)
+               accel_state->z = 512;
+       if (accel_state->z < -512)
+               accel_state->z = -512;
+
+#ifdef DEBUG
+       printf("accel_state->x=%d %d %d\n", accel_state->x, accel_state->y, accel_state->z);
+#endif
+
+       close( client_socket);
+
+       return 0;
+}
+
+static void smb380_write(void *opaque, uint32_t address, uint8_t offset, uint8_t val)
+{
+    SensorState *s = (SensorState *)opaque;
+
+#ifdef DEBUG
+       printf("smb380_write\n");
+#endif
+
+       get_from_ide (&glob_accel_state);
+
+       sensor_xyz_set (s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+       s->idx_out = 0;
+
+       return;
+}
+
+static int smb380_init(I2CAddressableState *s)
+{
+       SensorState *t = FROM_I2CADDR_SLAVE(SensorState, s);
+
+    smb380_reset(s);
+
+    return 0;
+}
+
+#if 0
+#define DEFAULT_TIME 500000000
+/* get sensor info from IDE when performance problem occurs */
+
+static void sensor_timer (void *opaque)
+{
+    SensorState *s = opaque;
+    int64_t expire_time = DEFAULT_TIME;
+
+#ifdef DEBUG
+    //printf("sensor timer\n");
+#endif
+
+    /* 1 sec : 500000000 */
+
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + expire_time);
+}
+
+
+/* set timer for getting sensor info */
+
+SensorState* sensor_init()
+{
+    SensorState *s = &glob_accel_state;
+
+    s->ts = qemu_new_timer (vm_clock, sensor_timer, s);
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + DEFAULT_TIME);
+
+    return s;
+
+    //s->ts = qemu_new_timer (vm_clock, accel_timer, s);
+}
+#endif
+
+
+static I2CAddressableDeviceInfo smb380_info = {
+    .i2c.qdev.name = "smb380",
+    .i2c.qdev.size = sizeof(SensorState),
+    .init = smb380_init,
+    .read = smb380_read,
+    .write = smb380_write,
+    .size = 1,
+    .rev = 0
+};
+
+static void smb380_register_devices(void)
+{
+    i2c_addressable_register_device(&smb380_info);
+}
+
+
+device_init(smb380_register_devices)
diff --git a/hw/smbus_smb380.c b/hw/smbus_smb380.c
new file mode 100644 (file)
index 0000000..b73b3f5
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * SMB380 Sensor Emulation
+ *
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#else
+#include <windows.h>
+#include <winsock2.h>
+#include <sys/types.h>
+#endif
+
+#include "hw.h"
+#include "i2c.h"
+#include "smbus.h"
+
+//#define DEBUG
+
+typedef struct SMBusSENSORDevice {
+       SMBusDevice smbusedev;
+       uint8_t data[7];
+
+       int16_t x;
+       int16_t y;
+       int16_t z;
+       int idx_out, req_out;
+}SMBusSENSORDevice;
+
+SMBusSENSORDevice glob_accel_state;
+
+static int sensor_xyz_set(struct SMBusSENSORDevice *s, uint16_t x, uint16_t y, uint16_t z);
+
+#define BITS_PER_BYTE 8
+#define SMB380_ACCEL_BITS 10
+
+static void smb380_quick_cmd(SMBusDevice *dev, uint8_t read)
+{
+#ifdef DEBUG
+       printf("sensor quick cmd : addr=0x%02x read%d\n", dev->i2c.address, read);
+#endif
+}
+
+static void smb380_send_byte(SMBusDevice *dev, uint8_t val)
+{
+#ifdef DEBUG
+    SMBusSENSORDevice *s = (SMBusSENSORDevice *) dev;
+    printf("smb380_send_byte: addr=0x%02x val=0x%02x index=%d\n",dev->i2c.address, val, s->idx_out);
+#endif
+       
+}
+
+static uint8_t smb380_receive_byte(SMBusDevice *dev, uint8_t address)
+{
+    SMBusSENSORDevice *s = (SMBusSENSORDevice *) dev;
+    int index = (int) address;
+    if (index != 0)    
+           return s->data[index-2];
+    else
+           return sensor_xyz_set(s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+}
+
+void sensor_update(uint16_t x, uint16_t y, uint16_t z)
+{
+       glob_accel_state.x = x;
+       glob_accel_state.y = y;
+       glob_accel_state.z = z;
+}
+
+static int sensor_xyz_set(struct SMBusSENSORDevice *s, uint16_t x, uint16_t y, uint16_t z)
+{
+       /* remains right 10bit */
+
+       x <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+       x >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+       y <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+       y >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+       z <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+       z >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+       /* data[1] : 2 ~ 10  (bit)
+        * data[0] : 0 ~ 1 (bit) */
+
+       s->data[0] = (0x3 & x) << 6;
+       s->data[1] = x >> 2;
+       s->data[2] = (0x3 & y) << 6;
+       s->data[3] = y >> 2;
+       s->data[4] = (0x3 & z) << 6;
+       s->data[5] = z >> 2;
+
+       return 0;
+}
+
+static void smb380_reset(struct SMBusSENSORDevice *s)
+{
+       s->idx_out = 0;
+
+       glob_accel_state.x = 0;
+       glob_accel_state.y = -256;
+       glob_accel_state.z = 0;
+
+       s->data[0] = 0;
+       s->data[1] = 0;
+       s->data[2] = 0;
+       s->data[3] = 0;
+       s->data[4] = 0;
+       s->data[5] = 0;
+       s->idx_out = 0;
+
+       return ;
+}
+
+static uint8_t smb380_read_data(SMBusDevice *opaque, uint8_t address, uint8_t offset)
+{
+    return smb380_receive_byte(opaque, address);
+}
+
+#ifdef DEBUG
+static int print_hex(char *data, int len)
+{
+       return 0;
+}
+#endif
+
+static int parse_val(char *buff, unsigned char find_data, char *parsebuff)
+{
+       int vvvi = 0;
+       while (1) {
+               if (vvvi > 40) {
+                       return -1;
+               }
+               if (buff[vvvi] == find_data) {
+                               
+                       vvvi++;
+                       strncpy(parsebuff, buff, vvvi);
+                       return vvvi;
+               }
+               vvvi++;
+       }
+
+       return 0;
+}
+
+#define  SENSOR_BUF                            16
+#define  SENSOR_VALUE_BUF              56
+static int get_from_ide(struct SMBusSENSORDevice *accel_state) 
+{
+       int client_socket;
+       struct  sockaddr_in   server_addr;
+       char    buff[SENSOR_VALUE_BUF + 1];
+       char    buff2[SENSOR_VALUE_BUF + 1];
+       char    tmpbuf[SENSOR_VALUE_BUF + 1];
+       int             len = 0, len1 = 0;
+       int             result = 0;
+       const char *command0 = "readSensor()\n";
+       const char *command1 = "accelerometer\n";
+
+#ifdef _WIN32
+       WSADATA wsaData;
+       if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
+               return -1;
+
+       client_socket = socket(AF_INET, SOCK_STREAM, 0);
+#else
+       client_socket = socket(PF_INET, SOCK_STREAM, 0);
+#endif
+       if (client_socket == -1) {
+               return -1;
+       }
+
+       memset(&server_addr, 0, sizeof( server_addr));
+       server_addr.sin_family     = AF_INET;
+       server_addr.sin_port       = htons(8010);
+       server_addr.sin_addr.s_addr= inet_addr("127.0.0.1");
+       if( -1 == connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) {
+               close( client_socket);
+               return -1;
+       }
+
+       if(read (client_socket, buff, SENSOR_BUF) < 0) {
+               close(client_socket);
+               return -1;
+       }
+
+       if(1) {
+               if(write(client_socket, command0, strlen(command0)) < 0) {
+               close(client_socket);
+               return -1;
+               }
+               if(write(client_socket, command1, strlen(command1)) < 0) {
+               close(client_socket);
+               return -1;
+               }
+
+               memset(buff, '\0', sizeof(buff));
+               memset(buff2, '\0', sizeof(buff2));
+
+               result = read(client_socket, buff2, SENSOR_VALUE_BUF);
+               if (result <= (SENSOR_VALUE_BUF)/2) {
+                       close( client_socket);
+                       return -1;
+               }
+               memcpy(buff, buff2, result);
+       }
+
+#ifdef DEBUG
+       print_hex(buff2, 90);
+#endif
+
+       /* start */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len = parse_val(buff2, 0x33, tmpbuf);
+
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len += parse_val(buff2+len, 0x0a, tmpbuf);
+
+       /* first data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       //accel.read_accelx = atof(tmpbuf);
+       accel_state->x = (int)(atof(tmpbuf) * (-26.2)); // 26 ~= 256 / 9.8
+       if (accel_state->x > 512)
+               accel_state->x = 512;
+       if (accel_state->x < -512)
+               accel_state->x = -512;
+
+       /* second data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       accel_state->y = (int)(atof(tmpbuf) * 26.2);
+       if (accel_state->y > 512)
+               accel_state->y = 512;
+       if (accel_state->y < -512)
+               accel_state->y = -512;
+
+       /* third data */
+       memset(tmpbuf, '\0', sizeof(tmpbuf));
+       len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+       len += len1;
+#ifdef DEBUG
+       print_hex(tmpbuf, len1);
+#endif
+       accel_state->z = (int)(atof(tmpbuf) * 26);
+
+       if (accel_state->z > 512)
+               accel_state->z = 512;
+       if (accel_state->z < -512)
+               accel_state->z = -512;
+
+#ifdef DEBUG
+       printf("accel_state->x=%d %d %d\n", accel_state->x, accel_state->y, accel_state->z);
+#endif
+
+       close( client_socket);
+
+       return 0;
+}
+
+static void smb380_write_data(SMBusDevice *dev, uint8_t address, uint8_t offset, uint8_t val)
+{
+    SMBusSENSORDevice *s = (SMBusSENSORDevice *)dev;
+
+#ifdef DEBUG
+       printf("smb380_write\n");
+#endif
+       get_from_ide (&glob_accel_state);
+               
+       sensor_xyz_set (s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+               
+
+#ifdef DEBUG
+       printf("smb380_send_byte: addr=0x%02x val=0x%02x\n",address, val);
+#endif
+       smb380_send_byte(dev,val);
+       
+}
+
+static int smb380_init(SMBusDevice *s)
+{
+    SMBusSENSORDevice *t = (SMBusSENSORDevice *)s;
+
+    smb380_reset(t);
+    return 0;
+}
+
+#if 0
+#define DEFAULT_TIME 500000000
+/* get sensor info from IDE when performance problem occurs */
+
+static void sensor_timer (void *opaque)
+{
+    SensorState *s = opaque;
+    int64_t expire_time = DEFAULT_TIME;
+
+#ifdef DEBUG
+    //printf("sensor timer\n");
+#endif
+
+    /* 1 sec : 500000000 */
+
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + expire_time);
+}
+
+
+/* set timer for getting sensor info */
+
+SensorState* sensor_init()
+{
+    SensorState *s = &glob_accel_state;
+
+    s->ts = qemu_new_timer (vm_clock, sensor_timer, s);
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + DEFAULT_TIME);
+
+    return s;
+
+    //s->ts = qemu_new_timer (vm_clock, accel_timer, s);
+}
+#endif
+
+
+static SMBusDeviceInfo smb380_info = {
+    .i2c.qdev.name = "smb380",
+    .i2c.qdev.size = sizeof(SMBusSENSORDevice),
+    .init = smb380_init,
+    .quick_cmd = smb380_quick_cmd,
+    .send_byte = smb380_send_byte,
+    .receive_byte = smb380_receive_byte,
+    .read_data = smb380_read_data,
+    .write_data = smb380_write_data
+};
+
+static void smb380_register_devices(void)
+{
+    smbus_register_device(&smb380_info);
+}
+
+device_init(smb380_register_devices)
index 5b1e42d..3bc004e 100644 (file)
@@ -380,6 +380,7 @@ static void spitz_keyboard_handler(void *opaque, int keycode)
     }
 
     QUEUE_KEY((code & 0x7f) | (keycode & 0x80));
+    return 0;
 }
 
 static void spitz_keyboard_tick(void *opaque)
@@ -505,6 +506,8 @@ static int spitz_keyboard_init(SysBusDevice *dev)
                 s->keymap[spitz_keymap[i][j]] = (i << 4) | j;
 
     spitz_keyboard_pre_map(s);
+    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) spitz_keyboard_handler, s,
+            "Spitz keyboard");
 
     s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s);
     qdev_init_gpio_in(&dev->qdev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM);
index 16aae96..ec69965 100644 (file)
@@ -22,7 +22,7 @@ typedef struct {
     int extension;
 } gamepad_state;
 
-static void stellaris_gamepad_put_key(void * opaque, int keycode)
+static int stellaris_gamepad_put_key(void * opaque, int keycode)
 {
     gamepad_state *s = (gamepad_state *)opaque;
     int i;
@@ -30,7 +30,7 @@ static void stellaris_gamepad_put_key(void * opaque, int keycode)
 
     if (keycode == 0xe0 && !s->extension) {
         s->extension = 0x80;
-        return;
+        return 0;
     }
 
     down = (keycode & 0x80) == 0;
@@ -45,6 +45,7 @@ static void stellaris_gamepad_put_key(void * opaque, int keycode)
     }
 
     s->extension = 0;
+    return 0;
 }
 
 static void stellaris_gamepad_save(QEMUFile *f, void *opaque)
@@ -85,7 +86,7 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
         s->buttons[i].keycode = keycode[i];
     }
     s->num_buttons = n;
-    qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
+    qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s, "Stellaris Gamepad");
     register_savevm(NULL, "stellaris_gamepad", -1, 1,
                     stellaris_gamepad_save, stellaris_gamepad_load, s);
 }
index d295e99..d2f6b5a 100644 (file)
@@ -133,7 +133,7 @@ static CPUWriteMemoryFunc * const syborg_keyboard_writefn[] = {
      syborg_keyboard_write
 };
 
-static void syborg_keyboard_event(void *opaque, int keycode)
+static int syborg_keyboard_event(void *opaque, int keycode)
 {
     SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
     int slot;
@@ -143,7 +143,7 @@ static void syborg_keyboard_event(void *opaque, int keycode)
     if (keycode == 0xe0 && !s->extension_bit) {
         DPRINTF("Extension bit\n");
         s->extension_bit = 0x80;
-        return;
+        return 0;
     }
     val = (keycode & 0x7f) | s->extension_bit;
     if (keycode & 0x80)
@@ -163,6 +163,7 @@ static void syborg_keyboard_event(void *opaque, int keycode)
     }
 
     syborg_keyboard_update(s);
+    return 0;
 }
 
 static void syborg_keyboard_save(QEMUFile *f, void *opaque)
@@ -219,7 +220,7 @@ static int syborg_keyboard_init(SysBusDevice *dev)
     }
     s->key_fifo = qemu_mallocz(s->fifo_size * sizeof(s->key_fifo[0]));
 
-    qemu_add_kbd_event_handler(syborg_keyboard_event, s);
+    qemu_add_kbd_event_handler(syborg_keyboard_event, s, "Syborg Keyboard");
 
     register_savevm(&dev->qdev, "syborg_keyboard", -1, 1,
                     syborg_keyboard_save, syborg_keyboard_load, s);
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
new file mode 100644 (file)
index 0000000..a58ba0d
--- /dev/null
@@ -0,0 +1,2143 @@
+/*
+ * QEMU USB EHCI Emulation
+ *
+ * Copyright(c) 2008  Emutex Ltd. (address@hidden)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or(at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * TODO:
+ *  o Downstream port handoff
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "sysbus.h"
+#include "usb-ehci.h"
+#include "monitor.h"
+
+#define EHCI_DEBUG   0
+#define STATE_DEBUG  0       /* state transitions  */
+
+#if EHCI_DEBUG || STATE_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#if STATE_DEBUG
+#define DPRINTF_ST DPRINTF
+#else
+#define DPRINTF_ST(...)
+#endif
+
+/* internal processing - reset HC to try and recover */
+#define USB_RET_PROCERR   (-99)
+
+#define MMIO_SIZE        0x1000
+
+/* Capability Registers Base Address - section 2.2 */
+#define CAPREGBASE       0x0000        
+#define CAPLENGTH        CAPREGBASE + 0x0000  // 1-byte, 0x0001 reserved
+#define HCIVERSION       CAPREGBASE + 0x0002  // 2-bytes, i/f version #
+#define HCSPARAMS        CAPREGBASE + 0x0004  // 4-bytes, structural params 
+#define HCCPARAMS        CAPREGBASE + 0x0008  // 4-bytes, capability params
+#define EECP             HCCPARAMS + 1
+#define HCSPPORTROUTE1   CAPREGBASE + 0x000c
+#define HCSPPORTROUTE2   CAPREGBASE + 0x0010
+
+#define OPREGBASE        0x0020        // Operational Registers Base Address
+
+#define USBCMD           OPREGBASE + 0x0000
+#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
+#define USBCMD_HCRESET   (1 << 1)      // HC Reset
+#define USBCMD_FLS       (3 << 2)      // Frame List Size
+#define USBCMD_FLS_SH    2             // Frame List Size Shift
+#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
+#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
+#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
+#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
+#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
+#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
+#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
+#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
+
+#define USBSTS           OPREGBASE + 0x0004
+#define USBSTS_RO_MASK   0x0000003f
+#define USBSTS_INT       (1 << 0)      // USB Interrupt
+#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
+#define USBSTS_PCD       (1 << 2)      // Port Change Detect
+#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
+#define USBSTS_HSE       (1 << 4)      // Host System Error
+#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
+#define USBSTS_HALT      (1 << 12)     // HC Halted
+#define USBSTS_REC       (1 << 13)     // Reclamation
+#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
+#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
+
+/*
+ *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
+ *  so no need to redefine here.
+ */
+#define USBINTR              OPREGBASE + 0x0008
+#define USBINTR_MASK         0x0000003f
+
+#define FRINDEX              OPREGBASE + 0x000c
+#define CTRLDSSEGMENT        OPREGBASE + 0x0010
+#define PERIODICLISTBASE     OPREGBASE + 0x0014
+#define ASYNCLISTADDR        OPREGBASE + 0x0018
+#define ASYNCLISTADDR_MASK   0xffffffe0
+
+#define CONFIGFLAG           OPREGBASE + 0x0040
+
+#define PORTSC               (OPREGBASE + 0x0044)
+#define PORTSC_BEGIN         PORTSC
+#define PORTSC_END           (PORTSC + 4 * NB_PORTS)
+/*
+ * Bits that are reserverd or are read-only are masked out of values
+ * written to us by software
+ */
+#define PORTSC_RO_MASK       0x007021c5
+#define PORTSC_RWC_MASK      0x0000002a
+#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
+#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
+#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
+#define PORTSC_PTC           (15 << 16)   // Port Test Control
+#define PORTSC_PTC_SH        16           // Port Test Control shift
+#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
+#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
+#define PORTSC_POWNER        (1 << 13)    // Port Owner
+#define PORTSC_PPOWER        (1 << 12)    // Port Power
+#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
+#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
+#define PORTSC_PRESET        (1 << 8)     // Port Reset
+#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
+#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
+#define PORTSC_OCC           (1 << 5)     // Over Current Change
+#define PORTSC_OCA           (1 << 4)     // Over Current Active
+#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
+#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
+#define PORTSC_CSC           (1 << 1)     // Connect Status Change
+#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
+
+//#define EHCI_NOMICROFRAMES
+
+#ifdef EHCI_NOMICROFRAMES
+#define FRAME_TIMER_FREQ 1000
+#else
+#define FRAME_TIMER_FREQ 8000
+#endif
+#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
+
+#define NB_MAXINTRATE    8        // Max rate at which controller issues ints
+#define NB_PORTS         4        // Number of downstream ports
+#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
+#define MAX_ITERATIONS   20       // Max number of QH before we break the loop
+#define MAX_QH           100      // Max allowable queue heads in a chain
+
+/*  Internal periodic / asynchronous schedule state machine states
+ */
+typedef enum {
+    EST_INACTIVE = 1000,
+    EST_ACTIVE,
+    EST_EXECUTING,
+    EST_SLEEPING,
+    /*  The following states are internal to the state machine function
+    */
+    EST_WAITLISTHEAD,
+    EST_FETCHENTRY,
+    EST_FETCHQH,
+    EST_FETCHITD,
+    EST_ADVANCEQUEUE,
+    EST_FETCHQTD,
+    EST_EXECUTE,
+    EST_WRITEBACK,
+    EST_HORIZONTALQH
+} EHCI_STATES;
+
+/* macros for accessing fields within next link pointer entry */
+#define NLPTR_GET(x)             ((x) & 0xffffffe0)
+#define NLPTR_TYPE_GET(x)        (((x) >> 1) & 3)
+#define NLPTR_TBIT(x)            ((x) & 1)  // 1=invalid, 0=valid
+
+/* link pointer types */
+#define NLPTR_TYPE_ITD           0     // isoc xfer descriptor
+#define NLPTR_TYPE_QH            1     // queue head
+#define NLPTR_TYPE_STITD         2     // split xaction, isoc xfer descriptor
+#define NLPTR_TYPE_FSTN          3     // frame span traversal node
+
+
+/*  EHCI spec version 1.0 Section 3.3
+ */
+typedef struct EHCIitd {
+    uint32_t next;
+
+    uint32_t transact[8];
+#define ITD_XACT_ACTIVE          (1 << 31)
+#define ITD_XACT_DBERROR         (1 << 30)
+#define ITD_XACT_BABBLE          (1 << 29)
+#define ITD_XACT_XACTERR         (1 << 28)
+#define ITD_XACT_LENGTH_MASK     0x0fff0000
+#define ITD_XACT_LENGTH_SH       16
+#define ITD_XACT_IOC             (1 << 15)
+#define ITD_XACT_PGSEL_MASK      0x00007000
+#define ITD_XACT_PGSEL_SH        12
+#define ITD_XACT_OFFSET_MASK     0x00000fff
+
+    uint32_t bufptr[7];
+#define ITD_BUFPTR_MASK          0xfffff000
+#define ITD_BUFPTR_SH            12
+#define ITD_BUFPTR_EP_MASK       0x00000f00
+#define ITD_BUFPTR_EP_SH         8
+#define ITD_BUFPTR_DEVADDR_MASK  0x0000007f
+#define ITD_BUFPTR_DEVADDR_SH    0
+#define ITD_BUFPTR_DIRECTION     (1 << 11)
+#define ITD_BUFPTR_MAXPKT_MASK   0x000007ff
+#define ITD_BUFPTR_MAXPKT_SH     0
+#define ITD_BUFPTR_MULT_MASK     0x00000003
+} EHCIitd;
+
+/*  EHCI spec version 1.0 Section 3.4
+ */
+typedef struct EHCIsitd {
+    uint32_t next;                  // Standard next link pointer
+    uint32_t epchar;
+#define SITD_EPCHAR_IO              (1 << 31)
+#define SITD_EPCHAR_PORTNUM_MASK    0x7f000000
+#define SITD_EPCHAR_PORTNUM_SH      24
+#define SITD_EPCHAR_HUBADD_MASK     0x007f0000
+#define SITD_EPCHAR_HUBADDR_SH      16
+#define SITD_EPCHAR_EPNUM_MASK      0x00000f00
+#define SITD_EPCHAR_EPNUM_SH        8
+#define SITD_EPCHAR_DEVADDR_MASK    0x0000007f
+
+    uint32_t uframe;
+#define SITD_UFRAME_CMASK_MASK      0x0000ff00
+#define SITD_UFRAME_CMASK_SH        8
+#define SITD_UFRAME_SMASK_MASK      0x000000ff
+
+    uint32_t results;
+#define SITD_RESULTS_IOC              (1 << 31)
+#define SITD_RESULTS_PGSEL            (1 << 30)
+#define SITD_RESULTS_TBYTES_MASK      0x03ff0000
+#define SITD_RESULTS_TYBYTES_SH       16
+#define SITD_RESULTS_CPROGMASK_MASK   0x0000ff00
+#define SITD_RESULTS_CPROGMASK_SH     8
+#define SITD_RESULTS_ACTIVE           (1 << 7)
+#define SITD_RESULTS_ERR              (1 << 6)
+#define SITD_RESULTS_DBERR            (1 << 5)
+#define SITD_RESULTS_BABBLE           (1 << 4)
+#define SITD_RESULTS_XACTERR          (1 << 3)
+#define SITD_RESULTS_MISSEDUF         (1 << 2)
+#define SITD_RESULTS_SPLITXSTATE      (1 << 1)
+
+    uint32_t bufptr[2];
+#define SITD_BUFPTR_MASK              0xfffff000
+#define SITD_BUFPTR_CURROFF_MASK      0x00000fff
+#define SITD_BUFPTR_TPOS_MASK         0x00000018
+#define SITD_BUFPTR_TPOS_SH           3
+#define SITD_BUFPTR_TCNT_MASK         0x00000007
+
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIsitd;
+
+/*  EHCI spec version 1.0 Section 3.5
+ */
+typedef struct EHCIqtd {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t altnext;                 // Standard next link pointer
+    uint32_t token;
+#define QTD_TOKEN_DTOGGLE             (1 << 31)
+#define QTD_TOKEN_TBYTES_MASK         0x7fff0000
+#define QTD_TOKEN_TBYTES_SH           16
+#define QTD_TOKEN_IOC                 (1 << 15)
+#define QTD_TOKEN_CPAGE_MASK          0x00007000
+#define QTD_TOKEN_CPAGE_SH            12
+#define QTD_TOKEN_CERR_MASK           0x00000c00
+#define QTD_TOKEN_CERR_SH             10
+#define QTD_TOKEN_PID_MASK            0x00000300
+#define QTD_TOKEN_PID_SH              8
+#define QTD_TOKEN_ACTIVE              (1 << 7)
+#define QTD_TOKEN_HALT                (1 << 6)
+#define QTD_TOKEN_DBERR               (1 << 5)
+#define QTD_TOKEN_BABBLE              (1 << 4)
+#define QTD_TOKEN_XACTERR             (1 << 3)
+#define QTD_TOKEN_MISSEDUF            (1 << 2)
+#define QTD_TOKEN_SPLITXSTATE         (1 << 1)
+#define QTD_TOKEN_PING                (1 << 0)
+
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define QTD_BUFPTR_MASK               0xfffff000
+} EHCIqtd;
+
+/*  EHCI spec version 1.0 Section 3.6
+ */
+typedef struct EHCIqh {
+    uint32_t next;                    // Standard next link pointer
+
+    /* endpoint characteristics */
+    uint32_t epchar;
+#define QH_EPCHAR_RL_MASK             0xf0000000
+#define QH_EPCHAR_RL_SH               28
+#define QH_EPCHAR_C                   (1 << 27)
+#define QH_EPCHAR_MPLEN_MASK          0x07FF0000
+#define QH_EPCHAR_MPLEN_SH            16
+#define QH_EPCHAR_H                   (1 << 15)
+#define QH_EPCHAR_DTC                 (1 << 14)
+#define QH_EPCHAR_EPS_MASK            0x00003000
+#define QH_EPCHAR_EPS_SH              12
+#define EHCI_QH_EPS_FULL              0
+#define EHCI_QH_EPS_LOW               1
+#define EHCI_QH_EPS_HIGH              2
+#define EHCI_QH_EPS_RESERVED          3
+
+#define QH_EPCHAR_EP_MASK             0x00000f00
+#define QH_EPCHAR_EP_SH               8
+#define QH_EPCHAR_I                   (1 << 7)
+#define QH_EPCHAR_DEVADDR_MASK        0x0000007f
+#define QH_EPCHAR_DEVADDR_SH          0
+
+    /* endpoint capabilities */
+    uint32_t epcap;
+#define QH_EPCAP_MULT_MASK            0xc0000000
+#define QH_EPCAP_MULT_SH              30
+#define QH_EPCAP_PORTNUM_MASK         0x3f800000
+#define QH_EPCAP_PORTNUM_SH           23
+#define QH_EPCAP_HUBADDR_MASK         0x007f0000
+#define QH_EPCAP_HUBADDR_SH           16
+#define QH_EPCAP_CMASK_MASK           0x0000ff00
+#define QH_EPCAP_CMASK_SH             8
+#define QH_EPCAP_SMASK_MASK           0x000000ff
+#define QH_EPCAP_SMASK_SH             0
+
+    uint32_t current_qtd;             // Standard next link pointer
+    uint32_t next_qtd;                // Standard next link pointer
+    uint32_t altnext_qtd;
+#define QH_ALTNEXT_NAKCNT_MASK        0x0000001e
+#define QH_ALTNEXT_NAKCNT_SH          1
+
+    uint32_t token;                   // Same as QTD token
+    uint32_t bufptr[5];               // Standard buffer pointer
+#define BUFPTR_CPROGMASK_MASK         0x000000ff
+#define BUFPTR_FRAMETAG_MASK          0x0000001f
+#define BUFPTR_SBYTES_MASK            0x00000fe0
+#define BUFPTR_SBYTES_SH              5
+} EHCIqh;
+
+/*  EHCI spec version 1.0 Section 3.7
+ */
+typedef struct EHCIfstn {
+    uint32_t next;                    // Standard next link pointer
+    uint32_t backptr;                 // Standard next link pointer
+} EHCIfstn;
+
+typedef struct {
+    qemu_irq irq;
+    target_phys_addr_t mem_base;
+    int mem;
+    int num_ports;
+    /*
+     *  EHCI spec version 1.0 Section 2.3
+     *  Host Controller Operational Registers
+     */
+    union {
+        uint8_t mmio[MMIO_SIZE];
+        struct {
+            uint8_t cap[OPREGBASE];
+            uint32_t usbcmd;
+            uint32_t usbsts;
+            uint32_t usbintr;
+            uint32_t frindex;
+            uint32_t ctrldssegment;
+            uint32_t periodiclistbase;
+            uint32_t asynclistaddr;
+            uint32_t notused[9];
+            uint32_t configflag;
+            uint32_t portsc[NB_PORTS];
+        };
+    };
+    /*
+     *  Internal states, shadow registers, etc
+     */
+    uint32_t sofv;
+    QEMUTimer *frame_timer;
+    int attach_poll_counter;
+    int astate;                        // Current state in asynchronous schedule
+    int pstate;                        // Current state in periodic schedule
+    USBPort ports[NB_PORTS];
+    uint8_t buffer[BUFF_SIZE];
+
+    /* cached data from guest - needs to be flushed 
+     * when guest removes an entry (doorbell, handshake sequence)
+     */
+    EHCIqh qh;             // copy of current QH (being worked on)
+    uint32_t qhaddr;       // address QH read from
+
+    EHCIqtd qtd;           // copy of current QTD (being worked on)
+    uint32_t qtdaddr;      // address QTD read from
+
+    uint32_t itdaddr;      // current ITD
+
+    uint32_t fetch_addr;   // which address to look at next
+
+    USBBus bus;
+    USBPacket usb_packet;
+    int async_port_in_progress;
+    int async_complete;
+    uint32_t tbytes;
+    int pid;
+    int exec_status;
+    int isoch_pause;
+    uint32_t last_run_usec;
+    uint32_t frame_end_usec;
+} EHCIState;
+
+#define SET_LAST_RUN_CLOCK(s) \
+    (s)->last_run_usec = qemu_get_clock(vm_clock) / 1000;
+
+/* nifty macros from Arnon's EHCI version  */
+#define get_field(data, field) \
+    (((data) & field##_MASK) >> field##_SH)
+
+#define set_field(data, newval, field) do { \
+    uint32_t val = *data; \
+    val &= ~ field##_MASK; \
+    val |= ((newval) << field##_SH) & field##_MASK; \
+    *data = val; \
+    } while(0)
+
+
+#if EHCI_DEBUG
+static const char *addr2str(unsigned addr)
+{
+    const char *r = "   unknown";
+
+    switch(addr) {
+        case CAPLENGTH:
+            r = " CAPLENGTH";
+            break;
+
+        case HCIVERSION:
+            r = "HCIVERSION";
+            break;
+
+        case HCSPARAMS:
+            r = " HCSPARAMS";
+            break;
+
+        case HCCPARAMS:
+            r = " HCCPARAMS";
+            break;
+
+        case USBCMD:
+            r = "   COMMAND";
+            break;
+
+        case USBSTS:
+            r = "    STATUS";
+            break;
+
+        case USBINTR:
+            r = " INTERRUPT";
+            break;
+
+        case FRINDEX:
+            r = " FRAME IDX";
+            break;
+    
+        case PERIODICLISTBASE:
+            r = "P-LIST BASE";
+            break;
+    
+        case ASYNCLISTADDR:
+            r = "A-LIST ADDR";
+            break;
+
+        case PORTSC_BEGIN ... PORTSC_END:
+            r = "PORT STATUS";
+            break;
+
+        case CONFIGFLAG:
+            r = "CONFIG FLAG";
+            break;
+    }
+
+    return r;
+}
+#endif
+
+
+static inline void ehci_set_interrupt(EHCIState *s, int intr)
+{
+    int level = 0;
+
+    // TODO honour interrupt threshold requests
+
+    s->usbsts |= intr;
+
+    if ((s->usbsts & USBINTR_MASK) & s->usbintr)
+        level = 1;
+
+    qemu_set_irq(s->irq, level);
+}
+
+/* Attach or detach a device on root hub */
+
+static void ehci_attach(USBPort *port, USBDevice *dev)
+{
+    EHCIState *s = port->opaque;
+    uint32_t *portsc = &s->portsc[port->index];
+
+    DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, desc %s\n",
+           port->index, *portsc, dev ? dev->product_desc : "undefined");
+
+    if (dev) {
+        if (port->dev) {
+            usb_attach(port, NULL);
+        }
+
+        *portsc |= PORTSC_CONNECT;
+
+        usb_send_msg(dev, USB_MSG_ATTACH);
+        port->dev = dev;
+    } else {
+        *portsc &= ~PORTSC_CONNECT;
+
+        if (port->dev) {
+            dev = port->dev;
+            usb_send_msg(dev, USB_MSG_DETACH);
+        }
+
+        port->dev = NULL;
+    }
+
+    *portsc |= PORTSC_CSC;
+
+    /*
+     *  If a high speed device is attached then we own this port(indicated
+     *  by zero in the PORTSC_POWNER bit field) so set the status bit
+     *  and set an interrupt if enabled.
+     */
+    if ( !(*portsc & PORTSC_POWNER)) {
+        ehci_set_interrupt(s, USBSTS_PCD);
+    }
+}
+
+/* 4.1 host controller initialization */
+static void ehci_reset(void *opaque)
+{
+    EHCIState *s = opaque;
+    int i;
+
+    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
+
+    s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
+    s->usbsts = USBSTS_HALT;
+
+    s->astate = EST_INACTIVE;
+    s->pstate = EST_INACTIVE;
+    s->async_port_in_progress = -1;
+    s->async_complete = 0;
+    s->isoch_pause = -1;
+    s->attach_poll_counter = 0;
+
+    for (i = 0; i < NB_PORTS; i++) {
+        s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+
+        if (s->ports[i].dev)
+            ehci_attach(&s->ports[i], s->ports[i].dev);
+    }
+}
+
+static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr];
+
+    return val;
+}
+
+static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
+
+    return val;
+}
+
+static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+    EHCIState *s = ptr;
+    uint32_t val;
+
+    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
+
+    return val;
+}
+
+static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
+    exit(1);
+}
+
+static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
+    exit(1);
+}
+
+static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+{
+    uint32_t *portsc = &s->portsc[port];
+    int rwc;
+    USBDevice *dev = s->ports[port].dev;
+
+    DPRINTF("port_status_write: "
+            "PORTSC (port %d) curr %08X new %08X rw-clear %08X rw %08X\n",
+            port, *portsc, val, (val & PORTSC_RWC_MASK), val & PORTSC_RO_MASK);
+
+    rwc = val & PORTSC_RWC_MASK;
+    val &= PORTSC_RO_MASK;
+
+    // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
+
+    *portsc &= ~rwc;
+
+    if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
+        DPRINTF("port_status_write: USBTRAN Port %d reset begin\n", port);
+    }
+
+    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
+        DPRINTF("port_status_write: USBTRAN Port %d reset done\n", port);
+        ehci_attach(&s->ports[port], dev);
+
+        // TODO how to handle reset of ports with no device
+        if (dev)
+            usb_send_msg(dev, USB_MSG_RESET);
+
+        if (s->ports[port].dev) {
+            DPRINTF("port_status_write: "
+                    "Device was connected before reset, clearing CSC bit\n");
+            *portsc &= ~PORTSC_CSC;
+        }
+
+        /*  Table 2.16 Set the enable bit(and enable bit change) to indicate
+         *  to SW that this port has a high speed device attached
+         *
+         *  TODO - when to disable?
+         */
+        val |= PORTSC_PED;
+        val |= PORTSC_PEDC;
+    }
+
+    *portsc &= ~PORTSC_RO_MASK;
+    *portsc |= val;
+    DPRINTF("port_status_write: Port %d status set to 0x%08x\n", port, *portsc);
+}
+
+static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    EHCIState *s = ptr;
+    int i;
+#if EHCI_DEBUG
+    const char *str;
+#endif
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
+            TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
+        handle_port_status_write(s, (addr-PORTSC)/4, val);
+        return;
+    }
+
+    if (addr < OPREGBASE) {
+        fprintf(stderr, "usb-ehci: write attempt to read-only register"
+            TARGET_FMT_plx "\n", addr);
+        return;
+    }
+
+
+    /* Do any register specific pre-write processing here.  */
+#if EHCI_DEBUG
+    str = addr2str((unsigned) addr);
+#endif
+    switch(addr)
+    {
+    case USBCMD:
+        DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n",
+                val, s->usbcmd);
+
+        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
+            DPRINTF("ehci_mem_writel: %s run, clear halt\n", str);
+            qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock));
+            SET_LAST_RUN_CLOCK(s);
+            s->usbsts &= ~USBSTS_HALT;
+        }
+
+        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            DPRINTF("                         ** STOP **\n");
+            qemu_del_timer(s->frame_timer);
+            // TODO - should finish out some stuff before setting halt
+            s->usbsts |= USBSTS_HALT;
+        }
+
+        if (val & USBCMD_HCRESET) {
+            DPRINTF("ehci_mem_writel: %s run, resetting\n", str);
+            ehci_reset(s);
+            val &= ~USBCMD_HCRESET;
+        }
+
+        /* not supporting dynamic frame list size at the moment */
+        if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+            fprintf(stderr, "attempt to set frame list size -- value %d\n",
+                    val & USBCMD_FLS);
+            val &= ~USBCMD_FLS;
+        }
+#if EHCI_DEBUG
+        if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) {
+            DPRINTF("periodic scheduling enabled\n");
+        }
+        if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) {
+            DPRINTF("periodic scheduling disabled\n");
+        }
+        if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) {
+            DPRINTF("asynchronous scheduling enabled\n");
+        }
+        if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) {
+            DPRINTF("asynchronous scheduling disabled\n");
+        }
+        if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) {
+            DPRINTF("doorbell request received\n");
+        }
+        if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) {
+            DPRINTF("light host controller reset received\n");
+        }
+        if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) {
+            DPRINTF("interrupt threshold control set to %x\n",
+                    (val & USBCMD_ITC)>>USBCMD_ITC_SH);
+        }
+#endif
+        break;
+
+
+    case USBSTS:
+        val &= USBSTS_RO_MASK;              // bits 6 thru 31 are RO
+        DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val);
+
+        val = (s->usbsts &= ~val);         // bits 0 thru 5 are R/WC
+
+        DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str);
+        ehci_set_interrupt(s, 0);
+        break;
+
+
+    case USBINTR:
+        val &= USBINTR_MASK;
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        break;
+
+    case FRINDEX:
+        s->sofv = val >> 3;
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        break;
+
+    case CONFIGFLAG:
+        DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+        val &= 0x1;
+        if (val) {
+            for (i = 0; i < NB_PORTS; i++)
+                s->portsc[i] &= ~PORTSC_POWNER;
+        }
+        break;
+
+    case PERIODICLISTBASE:
+        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: PERIODIC list base register set while periodic schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val);
+        break;
+
+    case ASYNCLISTADDR:
+        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+            fprintf(stderr,
+              "ehci: ASYNC list address register set while async schedule\n"
+              "      is enabled and HC is enabled\n");
+        }
+        DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val);
+        break;
+    }
+
+    *(uint32_t *)(&s->mmio[addr]) = val;
+}
+
+
+// TODO : Put in common header file, duplication from usb-ohci.c
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0);
+        *buf = le32_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint32_t tmp = cpu_to_le32(*buf);
+        cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1);
+    }
+
+    return 1;
+}
+
+// 4.10.2
+
+static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
+{
+    int i;
+    int dtoggle;
+    int ping;
+    int eps;
+    int reload;
+
+    // remember values in fields to preserve in qh after overlay
+
+    dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
+    ping    = qh->token & QTD_TOKEN_PING;
+
+    DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd,
+            ehci->qtdaddr);
+    qh->current_qtd = ehci->qtdaddr;
+    qh->next_qtd    = qtd->next;
+    qh->altnext_qtd = qtd->altnext;
+    qh->token       = qtd->token;
+
+
+    eps = get_field(qh->epchar, QH_EPCHAR_EPS);
+    if (eps == EHCI_QH_EPS_HIGH) {
+        qh->token &= ~QTD_TOKEN_PING;
+        qh->token |= ping;
+    }
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+
+    for (i = 0; i < 5; i++) {
+        qh->bufptr[i] = qtd->bufptr[i];
+    }
+
+    if (!(qh->epchar & QH_EPCHAR_DTC)) {
+        // preserve QH DT bit
+        qh->token &= ~QTD_TOKEN_DTOGGLE;
+        qh->token |= dtoggle;
+    }
+
+    qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
+    qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
+
+    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    return 0;
+}
+
+static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
+{
+    int bufpos = 0;
+    int cpage, offset;
+    uint32_t head;
+    uint32_t tail;
+
+
+    if (!bytes)
+        return 0;
+
+    cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
+    if (cpage > 4) {
+        fprintf(stderr, "cpage out of range (%d)\n", cpage);
+        return USB_RET_PROCERR;
+    }
+
+    offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
+    DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset %d\n",
+           rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset);
+
+    do {
+        /* start and end of this page */
+        head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
+        tail = head + ~QTD_BUFPTR_MASK + 1;
+        /* add offset into page */
+        head |= offset;
+
+        if (bytes <= (tail - head)) {
+            tail = head + bytes;
+        }
+
+        DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n",
+                rw ? "WRITE" : "READ ", cpage, head, tail, bufpos);
+
+        cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
+
+        bufpos += (tail - head);
+        bytes -= (tail - head);
+
+        if (bytes > 0) {
+            cpage++;
+            offset = 0;
+        }
+    } while (bytes > 0);
+
+    /* save cpage */
+    set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
+
+    /* save offset into cpage */
+    offset = tail - head;
+    qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
+    qh->bufptr[0] |= offset;
+
+    return 0;
+}
+
+static void ehci_async_complete_packet(USBPacket *packet, void *opaque)
+{
+    EHCIState *ehci = opaque;
+
+    DPRINTF("Async packet complete\n");
+    ehci->async_complete = 1;
+    ehci->exec_status = packet->len;
+}
+
+static int ehci_execute_complete(EHCIState *ehci,
+                                  EHCIqh *qh,
+                                  int ret)
+{
+    int i, c_err, reload;
+
+    if (ret == USB_RET_ASYNC && !ehci->async_complete) {
+        DPRINTF("not done yet\n");
+        return ret;
+    }
+
+    ehci->async_complete = 0;
+    i = ehci->async_port_in_progress;
+    ehci->async_port_in_progress = -1;
+
+    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
+           ehci->qhaddr, qh->next, ehci->qtdaddr, ret);
+
+    if (ret < 0) {
+err:
+        /* TO-DO: put this is in a function that can be invoked below as well */
+        c_err = get_field(qh->token, QTD_TOKEN_CERR);
+        c_err--;
+        set_field(&qh->token, c_err, QTD_TOKEN_CERR);
+
+        switch(ret) {
+        case USB_RET_NODEV:
+            fprintf(stderr, "USB no device\n");
+            break;
+        case USB_RET_STALL:
+            fprintf(stderr, "USB stall\n");
+            qh->token |= QTD_TOKEN_HALT;
+            break;
+        case USB_RET_NAK:
+            /* 4.10.3 */
+            reload = get_field(qh->epchar, QH_EPCHAR_RL);
+            if ((ehci->pid == USB_TOKEN_IN) && reload) {
+                int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+                nakcnt--;
+                set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+            } else if (!reload) {
+                return USB_RET_NAK;
+            }
+            break;
+        case USB_RET_BABBLE:
+            fprintf(stderr, "USB babble TODO\n");
+            qh->token |= QTD_TOKEN_BABBLE;
+            break;
+        default:
+            fprintf(stderr, "USB invalid response %d to handle\n", ret);
+            /* TO-DO: transaction error */
+            ret = USB_RET_PROCERR;
+            break;
+        }
+    } else {
+        // DPRINTF("Short packet condition\n");
+        // TODO check 4.12 for splits
+
+        if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) {
+            ret = USB_RET_BABBLE;
+            goto err;
+        }
+
+        if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) {
+            if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) {
+                return USB_RET_PROCERR;
+            }
+            ehci->tbytes -= ret;
+        } else {
+            ehci->tbytes = 0;
+        }
+
+        DPRINTF("updating tbytes to %d\n", ehci->tbytes);
+        set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
+    }
+
+    qh->token ^= QTD_TOKEN_DTOGGLE;
+    qh->token &= ~QTD_TOKEN_ACTIVE;
+
+    if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
+        // TODO should do this after writeback to memory
+        ehci_set_interrupt(ehci, USBSTS_INT);
+    }
+
+    return ret;
+}
+
+// 4.10.3
+
+static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
+{
+    USBPort *port;
+    USBDevice *dev;
+    int ret;
+    int i;
+    int endp;
+    int devadr;
+
+    if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
+        fprintf(stderr, "Attempting to execute inactive QH\n");
+        return USB_RET_PROCERR;
+    }
+
+    ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+    if (ehci->tbytes > BUFF_SIZE) {
+        fprintf(stderr, "Request for more bytes than allowed\n");
+        return USB_RET_PROCERR;
+    }
+
+    ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+    switch(ehci->pid) {
+        case 0: ehci->pid = USB_TOKEN_OUT; break;
+        case 1: ehci->pid = USB_TOKEN_IN; break;
+        case 2: ehci->pid = USB_TOKEN_SETUP; break;
+        default: fprintf(stderr, "bad token\n"); break;
+    }
+
+    if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
+        (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
+        return USB_RET_PROCERR;
+    }
+
+    endp = get_field(qh->epchar, QH_EPCHAR_EP);
+    devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
+
+    ret = USB_RET_NODEV;
+
+    // TO-DO: associating device with ehci port
+    for (i = 0; i < NB_PORTS; i++) {
+        port = &ehci->ports[i];
+        dev = port->dev;
+
+        // TODO sometime we will also need to check if we are the port owner
+
+        if (!(ehci->portsc[i] &(PORTSC_CONNECT))) {
+            DPRINTF("Port %d, no exec, not connected(%08X)\n",
+                    i, ehci->portsc[i]);
+            continue;
+        }
+
+        ehci->usb_packet.pid = ehci->pid;
+        ehci->usb_packet.devaddr = devadr;
+        ehci->usb_packet.devep = endp;
+        ehci->usb_packet.data = ehci->buffer;
+        ehci->usb_packet.len = ehci->tbytes;
+        ehci->usb_packet.complete_cb = ehci_async_complete_packet;
+        ehci->usb_packet.complete_opaque = ehci;
+
+        ret = dev->info->handle_packet(dev, &ehci->usb_packet);
+
+        DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+                ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid,
+                ehci->usb_packet.len, ehci->tbytes, endp, ret);
+
+        if (ret != USB_RET_NODEV)
+            break;
+    }
+
+    if (ret > BUFF_SIZE) {
+        fprintf(stderr, "ret from handle packet > BUFF_SIZE\n");
+        return USB_RET_PROCERR;
+    }
+
+    if (ret == USB_RET_ASYNC) {
+        ehci->async_port_in_progress = i;
+        ehci->async_complete = 0;
+    }
+
+    return ret;
+}
+
+/*  4.7.2
+ */
+
+static int ehci_process_itd(EHCIState *ehci,
+                             EHCIitd *itd)
+{
+    USBPort *port;
+    USBDevice *dev;
+    int ret;
+    int i, j;
+    int ptr;
+    int pid;
+    int pg;
+    int len;
+    int dir;
+    int devadr;
+    int endp;
+    int maxpkt;
+
+    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
+    devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
+    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
+    maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
+
+#ifdef EHCI_NOMICROFRAMES
+    for(i = 0; i < 8; i++) {
+#else
+    i =(ehci->frindex & 7);
+#endif
+
+    if (itd->transact[i] & ITD_XACT_ACTIVE) {
+        DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
+                ehci->frindex >> 3, i);
+
+        pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
+        ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
+                  (itd->transact[i] & ITD_XACT_OFFSET_MASK);
+        len = get_field(itd->transact[i], ITD_XACT_LENGTH);
+
+        if (len > BUFF_SIZE) {
+            return USB_RET_PROCERR;
+        }
+
+        DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
+
+        if (!dir) {
+            cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
+            pid = USB_TOKEN_OUT;
+        } else
+            pid = USB_TOKEN_IN;
+
+        ret = USB_RET_NODEV;
+
+        for(j = 0; j < NB_PORTS; j++) {
+            port = &ehci->ports[j];
+            dev = port->dev;
+
+            // TODO sometime we will also need to check if we are the port owner
+
+            if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
+                DPRINTF("Port %d, no exec, not connected(%08X)\n",
+                        j, ehci->portsc[j]);
+                continue;
+            }
+
+            ehci->usb_packet.pid = ehci->pid;
+            ehci->usb_packet.devaddr = devadr;
+            ehci->usb_packet.devep = endp;
+            ehci->usb_packet.data = ehci->buffer;
+            ehci->usb_packet.len = len;
+            ehci->usb_packet.complete_cb = ehci_async_complete_packet;
+            ehci->usb_packet.complete_opaque = ehci;
+
+            DPRINTF("calling dev->info->handle_packet\n");
+            ret = dev->info->handle_packet(dev, &ehci->usb_packet);
+
+            if (ret != USB_RET_NODEV)
+                break;
+        }
+
+        /*  In isoch, there is no facility to indicate a NAK so let's
+         *  instead just complete a zero-byte transaction.  Setting
+         *  DBERR seems too draconian.
+         */
+
+        if (ret == USB_RET_NAK) {
+            if (ehci->isoch_pause > 0) {
+                DPRINTF("ISOCH: received a NAK but paused so returning\n");
+                ehci->isoch_pause--;
+                return 0;
+            } else if (ehci->isoch_pause == -1) {
+                DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
+                // Pause frindex for up to 50 msec waiting for data from
+                // remote
+                ehci->isoch_pause = 50;
+                return 0;
+            } else {
+                DPRINTF("ISOCH: isoch pause timeout! return 0\n");
+                ret = 0;
+            }
+        } else {
+            DPRINTF("ISOCH: received ACK, clearing pause\n");
+            ehci->isoch_pause = -1;
+        }
+
+        if (ret >= 0) {
+            itd->transact[i] &= ~ITD_XACT_ACTIVE;
+
+            if (itd->transact[i] & ITD_XACT_IOC) {
+                // TODO should do this after writeback to memory
+                ehci_set_interrupt(ehci, USBSTS_INT);
+            }
+        }
+
+        if (ret >= 0 && dir) {
+            cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
+
+            if (ret != len) {
+                DPRINTF("ISOCH IN expected %d, got %d\n",
+                        len, ret);
+                set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+            }
+        }
+    }
+
+#ifdef EHCI_NOMICROFRAMES
+    }
+#endif
+    return 0;
+}
+
+/*  This state is the entry point for asynchronous schedule
+ *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
+ */
+static int ehci_state_waitlisthead(EHCIState *ehci,  int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int i = 0;
+    int again = 0;
+    uint32_t entry = ehci->asynclistaddr;
+
+    /* set reclamation flag at start event (4.8.6) */
+    if (async) {
+        ehci->usbsts |= USBSTS_REC;
+    }
+
+    /*  Find the head of the list (4.9.1.1) */
+    for(i = 0; i < MAX_QH; i++) {
+        get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+        if (qh->epchar & QH_EPCHAR_H) {
+            DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
+                       entry);
+            if (async)
+                entry |= (NLPTR_TYPE_QH << 1);
+
+            ehci->fetch_addr = entry;
+            *state = EST_FETCHENTRY;
+            again = 1;
+            goto out;
+        }
+
+        DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
+                   entry);
+        entry = qh->next;
+        if (entry == ehci->asynclistaddr) {
+            DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
+            break;
+        }
+    }
+
+    /* no head found for list. */
+
+    *state = EST_ACTIVE;
+
+out:
+    return again;
+}
+
+
+/*  This state is the entry point for periodic schedule processing as 
+ *  well as being a continuation state for async processing.
+ */
+static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
+{
+    int again = 0;
+    uint32_t entry = ehci->fetch_addr;
+
+#if EHCI_DEBUG == 0
+    if (qemu_get_clock(vm_clock) / 1000 >= ehci->frame_end_usec) {
+        if (async) {
+            DPRINTF("FETCHENTRY: FRAME timer elapsed, exit state machine\n");
+            goto out;
+        } else {
+            DPRINTF("FETCHENTRY: WARNING "
+                    "- frame timer elapsed during periodic\n");
+        }
+    }
+#endif
+    if (entry < 0x1000) {
+        DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
+        *state = EST_ACTIVE;
+        goto out;
+    }
+
+    /* section 4.8, only QH in async schedule */
+    if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
+        fprintf(stderr, "non queue head request in async schedule\n");
+        return -1;
+    }
+
+    switch (NLPTR_TYPE_GET(entry)) {
+    case NLPTR_TYPE_QH:
+        DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry);
+        *state = EST_FETCHQH;
+        ehci->qhaddr = entry;
+        again = 1;
+        break;
+
+    case NLPTR_TYPE_ITD:
+        DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry);
+        *state = EST_FETCHITD;
+        ehci->itdaddr = entry;
+        again = 1;
+        break;
+
+    default:
+        // TODO: handle siTD and FSTN types
+        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
+                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
+        return -1;
+    }
+
+out:
+    return again;
+}
+
+static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int reload;
+    int again = 0;
+
+    get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    if (async && (qh->epchar & QH_EPCHAR_H)) {
+
+        /*  EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
+        if (ehci->usbsts & USBSTS_REC) {
+            ehci->usbsts &= ~USBSTS_REC;
+        } else {
+            DPRINTF("FETCHQH:  QH 0x%08x. H-bit set, reclamation status reset"
+                       " - done processing\n", ehci->qhaddr);
+            *state = EST_ACTIVE;
+            goto out;
+        }
+    }
+
+#if EHCI_DEBUG
+    if (ehci->qhaddr != qh->next) {
+    DPRINTF("FETCHQH:  QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
+               ehci->qhaddr, 
+               qh->epchar & QH_EPCHAR_H,
+               qh->token & QTD_TOKEN_HALT,
+               qh->token & QTD_TOKEN_ACTIVE,
+               qh->next);
+    }
+#endif
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    if (reload) {
+        DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
+        set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+    }
+
+    if (qh->token & QTD_TOKEN_HALT) {
+        DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n");
+        *state = EST_HORIZONTALQH;
+        again = 1;
+
+    } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
+        DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
+        ehci->qtdaddr = qh->current_qtd;
+        *state = EST_FETCHQTD;
+        again = 1;
+
+    } else {
+        /*  EHCI spec version 1.0 Section 4.10.2 */
+        DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n");
+        *state = EST_ADVANCEQUEUE;
+        again = 1;
+    }
+
+out:
+    return again;
+}
+
+static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
+{
+    EHCIitd itd;
+
+    get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
+               sizeof(EHCIitd) >> 2);
+    DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n",
+               ehci->itdaddr, itd.next);
+
+    if (ehci_process_itd(ehci, &itd) != 0)
+        return -1;
+
+    put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
+                sizeof(EHCIitd) >> 2);
+    ehci->itdaddr = itd.next;
+    *state = EST_FETCHITD;
+
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 3 */
+static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
+{
+#if 0
+    /* TO-DO: 4.10.2 - paragraph 2
+     * if I-bit is set to 1 and QH is not active
+     * go to horizontal QH
+     */
+    if (I-bit set) {
+        *state = EST_HORIZONTALQH;
+        goto out;
+    }
+#endif
+
+    /* 
+     * want data and alt-next qTD is valid
+     */
+    if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
+        (ehci->qh.altnext_qtd > 0x1000) &&
+        (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
+        DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
+                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+                   ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+                   ehci->qh.next_qtd, ehci->qh.next);
+        ehci->qtdaddr = ehci->qh.altnext_qtd;
+        *state = EST_FETCHQTD;
+
+    /*
+     *  next qTD is valid
+     */
+    } else if ((ehci->qh.next_qtd > 0x1000) &&
+               (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
+        DPRINTF_ST("ADVQUEUE: next qTD. "
+                   "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+                   ehci->qh.current_qtd, ehci->qh.altnext_qtd, 
+                   ehci->qh.next_qtd, ehci->qh.next);
+        ehci->qtdaddr = ehci->qh.next_qtd;
+        *state = EST_FETCHQTD;
+
+    /*
+     *  no valid qTD, try next QH
+     */
+    } else {
+        DPRINTF_ST("ADVQUEUE: go to horizontal QH\n");
+        *state = EST_HORIZONTALQH;
+    }
+
+    return 1;
+}
+
+/* Section 4.10.2 - paragraph 4 */
+static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
+{
+    EHCIqtd *qtd = &ehci->qtd;
+    int again = 0;
+
+    get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
+
+    if (qtd->token & QTD_TOKEN_ACTIVE) {
+        *state = EST_EXECUTE;
+        again = 1;
+    } else {
+        *state = EST_HORIZONTALQH;
+        again = 1;
+    }
+
+    return again;
+}
+
+static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
+{
+    int again = 0;
+
+    if (ehci->fetch_addr != ehci->qh.next) {
+        ehci->fetch_addr = ehci->qh.next;
+        *state = EST_FETCHENTRY;
+        again = 1;
+    } else {
+        *state = EST_ACTIVE;
+    }
+
+    return again;
+}
+
+static int ehci_state_execute(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    EHCIqtd *qtd = &ehci->qtd;
+    int again = 0;
+    int reload, nakcnt;
+    int smask;
+
+    if (async) {
+        DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n",
+                  ehci->qhaddr, ehci->qtdaddr);
+    } else {
+        DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n");
+    }
+
+    if (ehci_qh_do_overlay(ehci, qh, qtd) != 0)
+        return -1;
+
+    smask = get_field(qh->epcap, QH_EPCAP_SMASK);
+#ifndef EHCI_NOMICROFRAMES
+    if (smask && (smask & (1 << (ehci->frindex & 7))) == 0) {
+        DPRINTF_ST("PERIODIC active not interval: mask %x, frindex %d,%d\n",
+                   smask, (ehci->frindex >> 3),(ehci->frindex & 7));
+
+        *state = EST_HORIZONTALQH;
+        again = 1;
+        goto out;
+    }
+#endif
+
+    if (!smask) {
+        reload = get_field(qh->epchar, QH_EPCHAR_RL);
+        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+        if (reload && !nakcnt) {
+            DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n");
+            *state = EST_HORIZONTALQH;
+            again = 1;
+            goto out;
+        }
+    }
+
+    // TODO verify enough time remains in the uframe as in 4.4.1.1
+
+    // TODO write back ptr to async list when done or out of time
+
+    // TODO Windows does not seem to ever set the MULT field
+
+
+    if (!async)
+    {
+        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+        if (!transactCtr) {
+            DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
+            *state = EST_HORIZONTALQH;
+            again = 1;
+            goto out;
+        }
+    }
+
+
+    if (async)
+        ehci->usbsts |= USBSTS_REC;
+
+    ehci->exec_status = ehci_execute(ehci, qh);
+    if (ehci->exec_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+    *state = EST_EXECUTING;
+
+    if (ehci->exec_status != USB_RET_ASYNC)
+        again = 1;
+
+out:
+    return again;
+}
+
+static int ehci_state_executing(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int again = 0;
+    int reload, nakcnt;
+
+    ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
+    if (ehci->exec_status == USB_RET_ASYNC) {
+        goto out;
+    }
+    if (ehci->exec_status == USB_RET_PROCERR) {
+        again = -1;
+        goto out;
+    }
+
+    // 4.10.3
+    if (!async) {
+        int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+        transactCtr--;
+        set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
+        // 4.10.3, bottom of page 82, should exit this state when transaction
+        // counter decrements to 0
+    }
+
+
+    reload = get_field(qh->epchar, QH_EPCHAR_RL);
+    if (reload) {
+        nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+        if (ehci->exec_status == USB_RET_NAK) {
+            if (nakcnt) {
+                nakcnt--;
+            }
+            DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
+                    nakcnt);
+        } else {
+            nakcnt = reload;
+            DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
+                       nakcnt);
+        }
+        set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+    }
+
+    /*
+     *  Write the qh back to guest physical memory.  This step isn't
+     *  in the EHCI spec but we need to do it since we don't share
+     *  physical memory with our guest VM.
+     */
+
+    DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n",
+              ehci->qhaddr, qh->next);
+    put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+    /* 4.10.5 */
+    if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
+        *state = EST_HORIZONTALQH;
+    } else {
+        *state = EST_WRITEBACK;
+    }
+
+    again = 1;
+
+out:
+    return again;
+}
+
+
+static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
+{
+    EHCIqh *qh = &ehci->qh;
+    int again = 0;
+
+    /*  Write back the QTD from the QH area */
+    DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
+    put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
+                sizeof(EHCIqtd) >> 2);
+
+    /* TODO confirm next state.  For now, keep going if async
+     * but stop after one qtd if periodic
+     */
+    //if (async) {
+        *state = EST_ADVANCEQUEUE;
+        again = 1;
+    //} else {
+    //    *state = EST_ACTIVE;
+    //}
+    return again;
+}
+
+/* 
+ * This is the state machine that is common to both async and periodic 
+ */
+
+static int ehci_advance_state(EHCIState *ehci,
+                                int async,
+                                int state)
+{
+    int again;
+    int iter = 0;
+
+    do {
+        if (state == EST_FETCHQH) {
+            iter++;
+            /* if we are roaming a lot of QH without executing a qTD
+             * something is wrong with the linked list. TO-DO: why is
+             * this hack needed?
+             */
+            if (iter > MAX_ITERATIONS) {
+                DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
+                state = EST_ACTIVE;
+                break;
+            }
+        }
+        switch(state) {
+        case EST_WAITLISTHEAD:
+            again = ehci_state_waitlisthead(ehci, async, &state);
+            break;
+
+        case EST_FETCHENTRY:
+            again = ehci_state_fetchentry(ehci, async, &state);
+            break;
+
+        case EST_FETCHQH:
+            again = ehci_state_fetchqh(ehci, async, &state);
+            break;
+
+        case EST_FETCHITD:
+            again = ehci_state_fetchitd(ehci, async, &state);
+            break;
+
+        case EST_ADVANCEQUEUE:
+            again = ehci_state_advqueue(ehci, async, &state);
+            break;
+
+        case EST_FETCHQTD:
+            again = ehci_state_fetchqtd(ehci, async, &state);
+            break;
+
+        case EST_HORIZONTALQH:
+            again = ehci_state_horizqh(ehci, async, &state);
+            break;
+
+        case EST_EXECUTE:
+            iter = 0;
+            again = ehci_state_execute(ehci, async, &state);
+            break;
+
+        case EST_EXECUTING:
+            again = ehci_state_executing(ehci, async, &state);
+            break;
+
+        case EST_WRITEBACK:
+            again = ehci_state_writeback(ehci, async, &state);
+            break;
+
+        default:
+            fprintf(stderr, "Bad state!\n");
+            again = -1;
+            break;
+        }
+
+        if (again < 0) {
+            fprintf(stderr, "processing error - resetting ehci HC\n");
+            ehci_reset(ehci);
+            again = 0;
+        }
+    }
+    while (again);
+
+    return state;
+}
+
+static void ehci_advance_async_state(EHCIState *ehci)
+{
+    EHCIqh qh;
+    int state = ehci->astate;
+
+    switch(state) {
+    case EST_INACTIVE:
+        if (!(ehci->usbcmd & USBCMD_ASE)) {
+            break;
+        }
+        ehci->usbsts |= USBSTS_ASS;
+        ehci->astate = EST_ACTIVE;
+        // No break, fall through to ACTIVE
+
+    case EST_ACTIVE:
+        if ( !(ehci->usbcmd & USBCMD_ASE)) {
+            ehci->usbsts &= ~USBSTS_ASS;
+            ehci->astate = EST_INACTIVE;
+            break;
+        }
+
+        /* If the doorbell is set, the guest wants to make a change to the
+         * schedule. The host controller needs to release cached data.
+         * (section 4.8.2)
+         */
+        if (ehci->usbcmd & USBCMD_IAAD) {
+            DPRINTF("ASYNC: doorbell request acknowledged\n");
+            ehci->usbcmd &= ~USBCMD_IAAD;
+            ehci_set_interrupt(ehci, USBSTS_IAA);
+            break;
+        }
+
+        /* make sure guest has acknowledged */
+        /* TO-DO: is this really needed? */
+        if (ehci->usbsts & USBSTS_IAA) {
+            DPRINTF("IAA status bit still set.\n");
+            break;
+        }
+
+        DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
+                ehci->asynclistaddr);
+        /* check that address register has been set */
+        if (ehci->asynclistaddr == 0) {
+            break;
+        }
+
+        state = EST_WAITLISTHEAD;
+        /* fall through */
+
+    case EST_FETCHENTRY:
+        /* fall through */
+
+    case EST_EXECUTING:
+        get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
+                   sizeof(EHCIqh) >> 2);
+        ehci->astate = ehci_advance_state(ehci, 1, state);
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad asynchronous state %d. "
+                "Resetting to active\n", ehci->astate);
+        ehci->astate = EST_ACTIVE;
+    }
+}
+
+static void ehci_advance_periodic_state(EHCIState *ehci)
+{
+    uint32_t entry;
+    uint32_t list;
+
+    // 4.6
+
+    switch(ehci->pstate) {
+    case EST_INACTIVE:
+        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
+            DPRINTF("PERIODIC going active\n");
+            ehci->usbsts |= USBSTS_PSS;
+            ehci->pstate = EST_ACTIVE;
+            // No break, fall through to ACTIVE
+        } else
+            break;
+
+    case EST_ACTIVE:
+        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+            DPRINTF("PERIODIC going inactive\n");
+            ehci->usbsts &= ~USBSTS_PSS;
+            ehci->pstate = EST_INACTIVE;
+            break;
+        }
+
+        list = ehci->periodiclistbase & 0xfffff000;
+        /* check that register has been set */
+        if (list == 0) {
+            break;
+        }
+        list |= ((ehci->frindex & 0x1ff8) >> 1);
+
+        cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0);
+        entry = le32_to_cpu(entry);
+
+        DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
+                ehci->frindex / 8, list, entry);
+        ehci->fetch_addr = entry;
+        ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY);
+        break;
+
+    case EST_EXECUTING:
+        DPRINTF("PERIODIC state adv for executing\n");
+        ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
+        break;
+
+    default:
+        /* this should only be due to a developer mistake */
+        fprintf(stderr, "ehci: Bad periodic state %d. "
+                "Resetting to active\n", ehci->pstate);
+        ehci->pstate = EST_ACTIVE;
+    }
+}
+
+static void ehci_frame_timer(void *opaque)
+{
+    EHCIState *ehci = opaque;
+    int64_t expire_time, t_now;
+    int usec_elapsed;
+    int frames;
+    int usec_now;
+    int i;
+    int skipped_frames = 0;
+
+
+    t_now = qemu_get_clock(vm_clock);
+    expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+    if (expire_time == t_now)
+        expire_time++;
+
+    usec_now = t_now / 1000;
+    usec_elapsed = usec_now - ehci->last_run_usec;
+    frames = usec_elapsed / FRAME_TIMER_USEC;
+    ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10;
+
+    for(i = 0; i < frames; i++) {
+        if ( !(ehci->usbsts & USBSTS_HALT)) {
+            if (ehci->isoch_pause <= 0) {
+#ifdef EHCI_NOMICROFRAMES
+                ehci->frindex += 8;
+#else
+                ehci->frindex++;
+#endif
+            }
+
+            if (ehci->frindex > 0x00001fff) {
+                ehci->frindex = 0;
+                ehci_set_interrupt(ehci, USBSTS_FLR);
+            }
+
+            ehci->sofv = (ehci->frindex - 1) >> 3;
+            ehci->sofv &= 0x000003ff;
+        }
+
+        if (frames - i > 10)
+            skipped_frames++;
+        else {
+            // TODO could this cause periodic frames to get skipped if async
+            // active?
+            if (ehci->astate != EST_EXECUTING)
+                ehci_advance_periodic_state(ehci);
+        }
+
+        ehci->last_run_usec += FRAME_TIMER_USEC;
+    }
+
+#if 0
+    if (skipped_frames)
+        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+#endif
+
+    /*  Async is not inside loop since it executes everything it can once
+     *  called
+     */
+    if (ehci->pstate != EST_EXECUTING)
+        ehci_advance_async_state(ehci);
+
+    qemu_mod_timer(ehci->frame_timer, expire_time);
+}
+
+static CPUReadMemoryFunc *ehci_readfn[3]={
+    ehci_mem_readb,
+    ehci_mem_readw,
+    ehci_mem_readl
+};
+
+static CPUWriteMemoryFunc *ehci_writefn[3]={
+    ehci_mem_writeb,
+    ehci_mem_writew,
+    ehci_mem_writel
+};
+
+static void usb_ehci_save(QEMUFile *f, void *opaque)
+{
+    USBPort   *ports = (USBPort *)opaque;
+    EHCIState *port_info;
+    uint32_t  portsc;
+    int i;
+
+    for (i = 0; i < NB_PORTS; i++) {
+        port_info = ports[i].opaque;
+        portsc    = port_info->portsc[ports[i].index];
+        if (portsc & PORTSC_CONNECT) {
+            hw_error("usb-ehci: some device is attached to usb-port. "
+                     "SaveVM is not possible\n");
+        }
+    }
+}
+
+static int usb_ehci_load(QEMUFile *f, void *opaque, int version_id)
+{
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static void usb_ehci_init(EHCIState *s, DeviceState *dev, qemu_irq irq)
+{
+    int i;
+
+    fprintf(stderr, "\n\n*** EHCI support is under development *** \n\n");
+    s->irq = irq;
+
+    // 2.2 host controller interface version
+    s->mmio[0x00] = (uint8_t) OPREGBASE;
+    s->mmio[0x01] = 0x00;
+    s->mmio[0x02] = 0x00;
+    s->mmio[0x03] = 0x01;        // HC version
+    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
+    s->mmio[0x05] = 0x00;        // No companion ports at present
+    s->mmio[0x06] = 0x00;
+    s->mmio[0x07] = 0x00;
+    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
+    s->mmio[0x09] = 0x68;        // EECP
+    s->mmio[0x0a] = 0x00;
+    s->mmio[0x0b] = 0x00;
+
+    // TODO - port registration is going to need an overhaul since ports
+    // can be low, full or high speed and are not tied to UHCI or EHCI.
+    // This works for now since we register last so are top of the free
+    // list but really all ports need to be owned by EHCI and it should
+    // hand off to companion controllers if device is full or low speed.
+
+    DPRINTF("ehci_init : registering USB ports with no device attached\n");
+
+    // TODO come up with a better port allocation scheme
+    // added ehci->bus, need to find ehci->DeviceState
+    usb_bus_new(&s->bus, dev);
+    for (i = 0; i < NB_PORTS; i++) {
+        usb_register_port(&s->bus, &s->ports[i], s, i, ehci_attach, 
+                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        s->ports[i].dev = 0;
+    }
+
+    s->frame_timer = qemu_new_timer(vm_clock, ehci_frame_timer, s);
+
+    DPRINTF("ehci_init: calling ehci_reset\n");
+    qemu_register_reset(ehci_reset, s);
+
+    s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+    register_savevm(dev, "usb-ehci", -1, 1,
+                    usb_ehci_save, usb_ehci_load, s->ports);
+}
+
+typedef struct {
+    PCIDevice dev;
+    EHCIState state;
+} EHCIPCIState;
+
+static void ehci_map(PCIDevice *pci_dev, int region_num,
+                     pcibus_t addr, pcibus_t size, int type)
+{
+    EHCIPCIState *s = (EHCIPCIState *)pci_dev;
+
+    DPRINTF("ehci_map: region %d, addr %08llX, size %lld, s->mem %08X\n",
+            region_num, addr, size, s->state.mem);
+    s->state.mem_base = addr;
+    cpu_register_physical_memory(addr, size, s->state.mem);
+}
+
+static int usb_ehci_initfn_pci(PCIDevice *dev);
+
+static PCIDeviceInfo ehci_info[] = {
+    {
+        .qdev.name    = "pci-ehci",
+        .qdev.size    = sizeof(EHCIPCIState),
+        .init         = usb_ehci_initfn_pci,
+    },{
+        /* end of list */
+    }
+};
+
+static int usb_ehci_initfn_pci(PCIDevice *dev)
+{
+    EHCIPCIState *s = DO_UPCAST(EHCIPCIState, dev, dev);
+    uint8_t *pci_conf = s->dev.config;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D);
+    pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10);
+    pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
+    pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
+    pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL);
+
+    /* capabilities pointer */
+    pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
+    //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
+
+    pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3
+    pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
+    pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
+
+    // pci_conf[0x50] = 0x01; // power management caps
+
+    pci_set_byte(&pci_conf[0x60], 0x20);  // spec release number (2.1.4)
+    pci_set_byte(&pci_conf[0x61], 0x20);  // frame length adjustment (2.1.5)
+    pci_set_word(&pci_conf[0x62], 0x00);  // port wake up capability (2.1.6)
+
+    pci_conf[0x64] = 0x00;
+    pci_conf[0x65] = 0x00;
+    pci_conf[0x66] = 0x00;
+    pci_conf[0x67] = 0x00;
+    pci_conf[0x68] = 0x01;
+    pci_conf[0x69] = 0x00;
+    pci_conf[0x6a] = 0x00;
+    pci_conf[0x6b] = 0x00;  // USBLEGSUP
+    pci_conf[0x6c] = 0x00;
+    pci_conf[0x6d] = 0x00;
+    pci_conf[0x6e] = 0x00;
+    pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
+
+    usb_ehci_init(&s->state, &dev->qdev, s->dev.irq[3]);
+
+    DPRINTF("ehci_init: registering MMIO size %d\n", MMIO_SIZE);
+    pci_register_bar(&s->dev, 0, MMIO_SIZE,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, ehci_map);
+    return 0;
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    EHCIState state;
+} EHCISysBusState;
+
+static int usb_ehci_initfn_sysbus(SysBusDevice *dev)
+{
+    EHCISysBusState *s = FROM_SYSBUS(EHCISysBusState, dev);
+
+    sysbus_init_irq(dev, &s->state.irq);
+    usb_ehci_init(&s->state, &dev->qdev, s->state.irq);
+    sysbus_init_mmio(dev, 0x1000, s->state.mem);
+
+    return 0;
+}
+
+static void ehci_register(void)
+{
+    pci_qdev_register_many(ehci_info);
+    sysbus_register_dev("usb-ehci", sizeof(EHCISysBusState),
+                        usb_ehci_initfn_sysbus);
+}
+device_init(ehci_register);
+
+void usb_ehci_init_pci(PCIBus *bus, int devfn)
+{
+    pci_create_simple(bus, devfn, "pci-ehci");
+}
+
+/*
+ * vim: expandtab ts=4
+ */
diff --git a/hw/usb-ehci.h b/hw/usb-ehci.h
new file mode 100644 (file)
index 0000000..d3ade36
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef QEMU_USB_EHCI_H
+#define QEMU_USB_EHCI_H
+
+#include "qemu-common.h"
+
+void usb_ehci_init_pci(PCIBus *bus, int devfn);
+
+#endif
index b559c50..100b3bb 100644 (file)
@@ -66,6 +66,8 @@ typedef struct USBKeyboardState {
     uint8_t leds;
     uint8_t key[16];
     int32_t keys;
+    int keyboard_grabbed;
+    QEMUPutKbdEntry *eh_entry;
 } USBKeyboardState;
 
 typedef struct USBHIDState {
@@ -491,7 +493,7 @@ static void usb_pointer_event(void *opaque,
     usb_hid_changed(hs);
 }
 
-static void usb_keyboard_event(void *opaque, int keycode)
+static int usb_keyboard_event(void *opaque, int keycode)
 {
     USBHIDState *hs = opaque;
     USBKeyboardState *s = &hs->kbd;
@@ -499,21 +501,22 @@ static void usb_keyboard_event(void *opaque, int keycode)
 
     if (hs->n == QUEUE_LENGTH) {
         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
-        return;
+        return 0;
     }
     slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
     s->keycodes[slot] = keycode;
     usb_hid_changed(hs);
+       return 0;
 }
 
-static void usb_keyboard_process_keycode(USBHIDState *hs)
+static int usb_keyboard_process_keycode(USBHIDState *hs)
 {
     USBKeyboardState *s = &hs->kbd;
     uint8_t hid_code, key;
     int i, keycode, slot;
 
     if (hs->n == 0) {
-        return;
+        return 0;
     }
     slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
     keycode = s->keycodes[slot];
@@ -524,24 +527,24 @@ static void usb_keyboard_process_keycode(USBHIDState *hs)
 
     switch (hid_code) {
     case 0x00:
-        return;
+        return 0;
 
     case 0xe0:
         if (s->modifiers & (1 << 9)) {
             s->modifiers ^= 3 << 8;
             usb_hid_changed(hs);
-            return;
+            return 0;
         }
     case 0xe1 ... 0xe7:
         if (keycode & (1 << 7)) {
             s->modifiers &= ~(1 << (hid_code & 0x0f));
             usb_hid_changed(hs);
-            return;
+            return 0;
         }
     case 0xe8 ... 0xef:
         s->modifiers |= 1 << (hid_code & 0x0f);
         usb_hid_changed(hs);
-        return;
+        return 0;
     }
 
     if (keycode & (1 << 7)) {
@@ -552,7 +555,7 @@ static void usb_keyboard_process_keycode(USBHIDState *hs)
                 break;
             }
         if (i < 0)
-            return;
+            return 0;
     } else {
         for (i = s->keys - 1; i >= 0; i --)
             if (s->key[i] == hid_code)
@@ -561,8 +564,11 @@ static void usb_keyboard_process_keycode(USBHIDState *hs)
             if (s->keys < sizeof(s->key))
                 s->key[s->keys ++] = hid_code;
         } else
-            return;
+            return 0;
     }
+
+    usb_hid_changed(hs);
+    return 0;
 }
 
 static inline int int_clamp(int val, int vmin, int vmax)
@@ -664,6 +670,10 @@ static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len)
         return 0;
 
     usb_keyboard_process_keycode(hs);
+//    if (!s->keyboard_grabbed) {
+//        qemu_activate_keyboard_event_handler(s->eh_entry);
+//        s->keyboard_grabbed = 1;
+//    }
 
     buf[0] = s->modifiers & 0xff;
     buf[1] = 0;
@@ -710,7 +720,7 @@ static void usb_keyboard_handle_reset(USBDevice *dev)
 {
     USBHIDState *s = (USBHIDState *)dev;
 
-    qemu_add_kbd_event_handler(usb_keyboard_event, s);
+    qemu_add_kbd_event_handler(usb_keyboard_event, s, "USB Keyboard");
     memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes));
     s->head = 0;
     s->n = 0;
@@ -845,7 +855,7 @@ static void usb_hid_handle_destroy(USBDevice *dev)
 
     switch(s->kind) {
     case USB_KEYBOARD:
-        qemu_remove_kbd_event_handler();
+        qemu_remove_kbd_event_handler(s->kbd.eh_entry);
         break;
     default:
         qemu_remove_mouse_event_handler(s->ptr.eh_entry);
index 09ea0b6..9ec5f3c 100644 (file)
@@ -618,7 +618,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
 
     starting_frame = OHCI_BM(iso_td.flags, TD_SF);
     frame_count = OHCI_BM(iso_td.flags, TD_FC);
-    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
+    relative_frame_number = USUB(ohci->frame_number, starting_frame);
 
 #ifdef DEBUG_ISOCH
     printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
@@ -632,8 +632,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
            iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
            iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
            iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
-           ohci->frame_number, starting_frame, 
-           frame_count, relative_frame_number,         
+           ohci->frame_number, starting_frame,
+           frame_count, relative_frame_number,
            OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
 #endif
 
@@ -643,7 +643,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     } else if (relative_frame_number > frame_count) {
         /* ISO TD expired - retire the TD to the Done Queue and continue with
            the next ISO TD of the same ED */
-        DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
+        DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number,
                frame_count);
         OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
         ed->head &= ~OHCI_DPTR_MASK;
@@ -690,8 +690,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     start_offset = iso_td.offset[relative_frame_number];
     next_offset = iso_td.offset[relative_frame_number + 1];
 
-    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
-        ((relative_frame_number < frame_count) && 
+    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) ||
+        ((relative_frame_number < frame_count) &&
          !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
         printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
                start_offset, next_offset);
@@ -756,7 +756,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
             if (ret != USB_RET_NODEV)
                 break;
         }
-    
+
         if (ret == USB_RET_ASYNC) {
             return 1;
         }
@@ -1668,6 +1668,30 @@ static USBPortOps ohci_port_ops = {
     .attach = ohci_attach,
     .detach = ohci_detach,
 };
+static void usb_ohci_save(QEMUFile *f, void *opaque)
+{
+    USBPort  *portl = (USBPort *)opaque;
+    OHCIState *port_info;
+    uint32_t  port_state;
+    int i;
+
+    for (i = 0; i < OHCI_MAX_PORTS; i++) {
+        port_info  = portl[i].opaque;
+        port_state = port_info->rhport[portl[i].index].ctrl;
+        if (port_state & OHCI_PORT_CCS) {
+            hw_error("usb-ohci: some device is attached to usb-port. "
+                     "SaveVM is not possible\n");
+        }
+    }
+}
+
+static int usb_ohci_load(QEMUFile *f, void *opaque, int version_id)
+{
+    if (version_id != 1)
+        return -EINVAL;
+
+    return 0;
+}
 
 static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
                           int num_ports, uint32_t localmem_base)
@@ -1706,6 +1730,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
 
     ohci->async_td = 0;
     qemu_register_reset(ohci_reset, ohci);
+
+    register_savevm(dev, "usb-ohci", -1, 1,
+                    usb_ohci_save, usb_ohci_load, ohci->rhport);
 }
 
 typedef struct {
diff --git a/hw/wm8994.c b/hw/wm8994.c
new file mode 100644 (file)
index 0000000..d56a502
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * WM8994 Audio Codec
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *                Dmitry Zhurikhin <zhur@ispras.ru>
+ *                Vladimir Monakhov <vmonakhov@ispras.ru>
+ */
+
+#include "i2c-addressable.h"
+#include "audio/audio.h"
+#include "wm8994_reg.h"
+
+
+#define CODEC       "wm8994"
+
+
+typedef struct WM8994State {
+
+    I2CAddressableState i2c_addressable;
+
+    uint16_t registers[WM8994_REGISTER_MEM_SIZE];
+
+    void (*data_req)(void *, int);
+    void *opaque;
+
+    SWVoiceOut *dac_voice;
+    QEMUSoundCard card;
+
+    uint8_t data_out[4 * 4096]; /* magic */
+
+    int idx_out, req_out;
+} WM8994State;
+
+
+static inline uint8_t wm8994_volume(WM8994State *s, uint16_t reg)
+{
+    return s->registers[reg] & 0x3F;
+}
+
+static inline uint8_t wm8994_mute(WM8994State *s, uint16_t reg)
+{
+    return (s->registers[reg] >> 6) & 1;
+}
+
+static inline uint8_t wm8994_outvol_transform(WM8994State *s, uint16_t reg)
+{
+    return 0xFF * wm8994_volume(s, reg) / 0x3F;
+}
+
+static inline int wm8994_rate(WM8994State *s, uint16_t reg)
+{
+    switch ((s->registers[reg] >> 4) & 0xF) {
+    case  0: return  8000;
+    case  1: return 11025;
+    case  2: return 12000;
+    case  3: return 16000;
+    case  4: return 22050;
+    case  5: return 24000;
+    case  6: return 32000;
+    case  7: return 44100;
+    case  8: return 48000;
+    case  9: return 88200;
+    case 10: return 96000;
+
+    default:
+        return 0;
+    }
+}
+
+static inline audfmt_e wm8994_format(WM8994State *s, uint16_t reg)
+{
+    switch ((s->registers[reg] >> 5) & 0x3) {
+    case 0: return AUD_FMT_S16;
+    case 3: return AUD_FMT_S32;
+    case 1:
+        /*TODO: implement conversion */
+        hw_error("wm8994: unsupported format (20 bits for channel)\n");
+        return AUD_FMT_S16;
+    case 2:
+        /*TODO: implement conversion */
+        hw_error("wm8994: unsupported format (24 bits for channel)\n");
+        return AUD_FMT_S16;
+    default:
+        hw_error("wm8994: unknown format\n");
+        return AUD_FMT_S16;
+    }
+}
+
+static inline void wm8994_out_flush(WM8994State *s)
+{
+    int sent = 0;
+
+    if (!s->dac_voice)
+        return;
+    while (sent < s->idx_out) {
+        sent +=
+            AUD_write(s->dac_voice, s->data_out + sent, s->idx_out - sent) ?:
+            s->idx_out;
+    }
+    s->idx_out = 0;
+}
+
+static void wm8994_audio_out_cb(void *opaque, int free_b)
+{
+    WM8994State *s = (WM8994State *) opaque;
+
+    if (s->idx_out >= free_b) {
+        s->idx_out = free_b;
+        s->req_out = 0;
+        wm8994_out_flush(s);
+    } else {
+        s->req_out = free_b - s->idx_out;
+    }
+
+    if (s->data_req) {
+        s->data_req(s->opaque, s->req_out >> 2);
+    }
+}
+
+static void wm8994_vol_update(WM8994State *s)
+{
+    int volume_left  = wm8994_volume(s, WM8994_SPEAKER_VOLUME_LEFT);
+    int volume_right = wm8994_volume(s, WM8994_SPEAKER_VOLUME_RIGHT);
+
+    /* Speaker */
+    AUD_set_volume_out(s->dac_voice, 1, volume_left, volume_right);
+}
+
+static void wm8994_set_format(WM8994State *s)
+{
+    struct audsettings out_fmt;
+
+    wm8994_out_flush(s);
+
+    if (s->dac_voice) {
+        AUD_set_active_out(s->dac_voice, 0);
+    }
+
+    if (s->dac_voice) {
+        AUD_close_out(&s->card, s->dac_voice);
+        s->dac_voice = NULL;
+    }
+
+    /* Setup output */
+    out_fmt.endianness = 0;
+    out_fmt.nchannels = 2;
+    out_fmt.freq = wm8994_rate(s, WM8994_AIF1_RATE);
+    out_fmt.fmt = wm8994_format(s, WM8994_AIF1_CONTROL_1);
+
+    s->dac_voice =
+        AUD_open_out(&s->card, s->dac_voice, CODEC ".speaker",
+                     s, wm8994_audio_out_cb, &out_fmt);
+    wm8994_vol_update(s);
+
+    if (s->dac_voice) {
+        AUD_set_active_out(s->dac_voice, 1);
+    }
+}
+
+static void wm8994_reset(DeviceState *d)
+{
+    WM8994State *s =
+        FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+    memset(s->registers, 0, sizeof(s->registers));
+    s->registers[WM8994_SOFTWARE_RESET] = 0x8994;
+
+    s->registers[WM8994_POWER_MANAGEMENT_2] = 0x6000;
+
+    s->registers[WM8994_SPEAKER_VOLUME_LEFT]  = 0x79;
+    s->registers[WM8994_SPEAKER_VOLUME_RIGHT] = 0x79;
+
+    s->registers[WM8994_AIF1_RATE] = 0x73;
+
+    s->idx_out = 0;
+    s->req_out = 0;
+    s->dac_voice = NULL;
+
+    wm8994_set_format(s);
+}
+
+static uint8_t wm8994_read(void *opaque, uint32_t address, uint8_t offset)
+{
+    WM8994State *s = (WM8994State *)opaque;
+
+    if (offset >= 2) {
+        /* FIXME: there should be an error but kernel wants to read more
+         * than allowed; so just pretend there's nothing here */
+        /* hw_error("wm8994: too much data requested"); */
+        return 0;
+    }
+    if (address < WM8994_REGISTER_MEM_SIZE) {
+        return (s->registers[address] >> ((1 - offset) * 8)) & 0xFF;
+    } else {
+        hw_error("wm8994: illegal read offset 0x%x\n", address + offset);
+    }
+}
+
+static void wm8994_write(void *opaque, uint32_t address, uint8_t offset,
+                         uint8_t val)
+{
+    WM8994State *s = (WM8994State *)opaque;
+
+    address += offset / 2;
+    offset %= 2;
+
+    if (address < WM8994_REGISTER_MEM_SIZE) {
+        s->registers[address] &= ~(0xFF << ((1 - offset) * 8));
+        s->registers[address] |= val << ((1 - offset) * 8);
+
+        if (offset == 1) {
+            switch (address) {
+            case WM8994_SOFTWARE_RESET:
+                wm8994_reset(&s->i2c_addressable.i2c.qdev);
+                break;
+
+            case WM8994_SPEAKER_VOLUME_LEFT:
+            case WM8994_SPEAKER_VOLUME_RIGHT:
+                wm8994_vol_update(s);
+                break;
+
+            case WM8994_AIF1_RATE:
+            case WM8994_AIF1_CONTROL_1:
+                wm8994_set_format(s);
+                break;
+
+            default:
+                break;
+            }
+        }
+    } else {
+        hw_error("wm8994: illegal write offset 0x%x\n", address + offset);
+    }
+}
+
+void wm8994_data_req_set(DeviceState *dev, void (*data_req)(void *, int),
+                         void *opaque)
+{
+    WM8994State *s =
+        FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+    s->data_req = data_req;
+    s->opaque = opaque;
+}
+
+void *wm8994_dac_buffer(DeviceState *dev, int samples)
+{
+    WM8994State *s =
+        FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+    /* XXX: Should check if there are <i>samples</i> free samples available */
+    void *ret = s->data_out + s->idx_out;
+
+    s->idx_out += samples << 2;
+    s->req_out -= samples << 2;
+    return ret;
+}
+
+void wm8994_dac_dat(DeviceState *dev, uint32_t sample)
+{
+    WM8994State *s =
+        FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+    *(uint32_t *) &s->data_out[s->idx_out] = sample;
+    s->req_out -= 4;
+    s->idx_out += 4;
+    if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) {
+        wm8994_out_flush(s);
+    }
+}
+
+void wm8994_dac_commit(DeviceState *dev)
+{
+    WM8994State *s =
+        FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+    return wm8994_out_flush(s);
+}
+
+static int wm8994_init(I2CAddressableState *i2c)
+{
+    WM8994State *s = FROM_I2CADDR_SLAVE(WM8994State, i2c);
+
+    AUD_register_card(CODEC, &s->card);
+
+    wm8994_reset(&i2c->i2c.qdev);
+
+    return 0;
+}
+
+static I2CAddressableDeviceInfo wm8994_info = {
+    .i2c.qdev.name  = "wm8994",
+    .i2c.qdev.size  = sizeof(WM8994State),
+    .i2c.qdev.reset = wm8994_reset,
+    .init = wm8994_init,
+    .read = wm8994_read,
+    .write = wm8994_write,
+    .size = 2,
+    .rev = 1
+};
+
+static void wm8994_register_devices(void)
+{
+    i2c_addressable_register_device(&wm8994_info);
+}
+
+device_init(wm8994_register_devices)
diff --git a/hw/wm8994_reg.h b/hw/wm8994_reg.h
new file mode 100644 (file)
index 0000000..daa109c
--- /dev/null
@@ -0,0 +1,232 @@
+#ifndef hw_s5pc1xx_wm8994_reg_h
+#define hw_s5pc1xx_wm8994_reg_h     "s5pc1xx_wm8994_reg.h"
+
+#define WM8994_SOFTWARE_RESET                   0x00
+#define WM8994_POWER_MANAGEMENT_1               0x01
+#define WM8994_POWER_MANAGEMENT_2               0x02
+#define WM8994_POWER_MANAGEMENT_3               0x03
+#define WM8994_POWER_MANAGEMENT_4               0x04
+#define WM8994_POWER_MANAGEMENT_5               0x05
+#define WM8994_POWER_MANAGEMENT_6               0x06
+#define WM8994_INPUT_MIXER_1                    0x15
+#define WM8994_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8994_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8994_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8994_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8994_LEFT_OUTPUT_VOLUME               0x1C /* Headphone */
+#define WM8994_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8994_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8994_HPOUT2_VOLUME                    0x1F
+#define WM8994_LEFT_OPGA_VOLUME                 0x20
+#define WM8994_RIGHT_OPGA_VOLUME                0x21
+#define WM8994_SPKMIXL_ATTENUATION              0x22
+#define WM8994_SPKMIXR_ATTENUATION              0x23
+#define WM8994_SPKOUT_MIXERS                    0x24
+#define WM8994_CLASSD                           0x25
+#define WM8994_SPEAKER_VOLUME_LEFT              0x26 /* Speaker */
+#define WM8994_SPEAKER_VOLUME_RIGHT             0x27
+#define WM8994_INPUT_MIXER_2                    0x28
+#define WM8994_INPUT_MIXER_3                    0x29
+#define WM8994_INPUT_MIXER_4                    0x2A
+#define WM8994_INPUT_MIXER_5                    0x2B
+#define WM8994_INPUT_MIXER_6                    0x2C
+#define WM8994_OUTPUT_MIXER_1                   0x2D
+#define WM8994_OUTPUT_MIXER_2                   0x2E
+#define WM8994_OUTPUT_MIXER_3                   0x2F
+#define WM8994_OUTPUT_MIXER_4                   0x30
+#define WM8994_OUTPUT_MIXER_5                   0x31
+#define WM8994_OUTPUT_MIXER_6                   0x32
+#define WM8994_HPOUT2_MIXER                     0x33
+#define WM8994_LINE_MIXER_1                     0x34
+#define WM8994_LINE_MIXER_2                     0x35
+#define WM8994_SPEAKER_MIXER                    0x36
+#define WM8994_ADDITIONAL_CONTROL               0x37
+#define WM8994_ANTIPOP_1                        0x38
+#define WM8994_ANTIPOP_2                        0x39
+#define WM8994_MICBIAS                          0x3A
+#define WM8994_LDO_1                            0x3B
+#define WM8994_LDO_2                            0x3C
+#define WM8994_CHARGE_PUMP_1                    0x4C
+#define WM8994_CLASS_W_1                        0x51
+#define WM8994_DC_SERVO_1                       0x54
+#define WM8994_DC_SERVO_2                       0x55
+#define WM8994_DC_SERVO_4                       0x57
+#define WM8994_DC_SERVO_READBACK                0x58
+#define WM8994_DC_SERVO_ANA_1                   0x5B
+#define WM8994_DC_SERVO_ANA_2                   0x5C
+#define WM8994_ANALOGUE_HP_1                    0x60
+#define WM8994_REVISION             0x100
+#define WM8994_CONTROL_INTERFACE                0x101
+#define WM8994_WRITE_SEQUENCER_CTRL_1           0x110
+#define WM8994_WRITE_SEQUENCER_CTRL_2           0x111
+#define WM8994_AIF1_CLOCKING_1                  0x200
+#define WM8994_AIF1_CLOCKING_2                  0x201
+#define WM8994_AIF2_CLOCKING_1                  0x204
+#define WM8994_AIF2_CLOCKING_2                  0x205
+#define WM8994_CLOCKING_1                       0x208
+#define WM8994_CLOCKING_2                       0x209
+#define WM8994_AIF1_RATE                        0x210
+#define WM8994_AIF2_RATE                        0x211
+#define WM8994_RATE_STATUS                      0x212
+#define WM8994_FLL1_CONTROL_1                   0x220
+#define WM8994_FLL1_CONTROL_2                   0x221
+#define WM8994_FLL1_CONTROL_3                   0x222
+#define WM8994_FLL1_CONTROL_4                   0x223
+#define WM8994_FLL1_CONTROL_5                   0x224
+#define WM8994_FLL2_CONTROL_1                   0x240
+#define WM8994_FLL2_CONTROL_2                   0x241
+#define WM8994_FLL2_CONTROL_3                   0x242
+#define WM8994_FLL2_CONTROL_4                   0x243
+#define WM8994_FLL2_CONTROL_5                   0x244
+#define WM8994_AIF1_CONTROL_1                   0x300
+#define WM8994_AIF1_CONTROL_2                   0x301
+#define WM8994_AIF1_MASTER_SLAVE                0x302
+#define WM8994_AIF1_BCLK                        0x303
+#define WM8994_AIF1ADC_LRCLK                    0x304
+#define WM8994_AIF1DAC_LRCLK                    0x305
+#define WM8994_AIF1DAC_DATA                     0x306
+#define WM8994_AIF1ADC_DATA                     0x307
+#define WM8994_AIF2_CONTROL_1                   0x310
+#define WM8994_AIF2_CONTROL_2                   0x311
+#define WM8994_AIF2_MASTER_SLAVE                0x312
+#define WM8994_AIF2_BCLK                        0x313
+#define WM8994_AIF2ADC_LRCLK                    0x314
+#define WM8994_AIF2DAC_LRCLK                    0x315
+#define WM8994_AIF2DAC_DATA                     0x316
+#define WM8994_AIF2ADC_DATA                     0x317
+#define WM8994_AIF1_ADC1_LEFT_VOLUME            0x400
+#define WM8994_AIF1_ADC1_RIGHT_VOLUME           0x401
+#define WM8994_AIF1_DAC1_LEFT_VOLUME            0x402
+#define WM8994_AIF1_DAC1_RIGHT_VOLUME           0x403
+#define WM8994_AIF1_ADC2_LEFT_VOLUME            0x404
+#define WM8994_AIF1_ADC2_RIGHT_VOLUME           0x405
+#define WM8994_AIF1_DAC2_LEFT_VOLUME            0x406
+#define WM8994_AIF1_DAC2_RIGHT_VOLUME           0x407
+#define WM8994_AIF1_ADC1_FILTERS                0x410
+#define WM8994_AIF1_ADC2_FILTERS                0x411
+#define WM8994_AIF1_DAC1_FILTERS_1              0x420
+#define WM8994_AIF1_DAC1_FILTERS_2              0x421
+#define WM8994_AIF1_DAC2_FILTERS_1              0x422
+#define WM8994_AIF1_DAC2_FILTERS_2              0x423
+#define WM8994_AIF1_DRC1_1                      0x440
+#define WM8994_AIF1_DRC1_2                      0x441
+#define WM8994_AIF1_DRC1_3                      0x442
+#define WM8994_AIF1_DRC1_4                      0x443
+#define WM8994_AIF1_DRC1_5                      0x444
+#define WM8994_AIF1_DRC2_1                      0x450
+#define WM8994_AIF1_DRC2_2                      0x451
+#define WM8994_AIF1_DRC2_3                      0x452
+#define WM8994_AIF1_DRC2_4                      0x453
+#define WM8994_AIF1_DRC2_5                      0x454
+#define WM8994_AIF1_DAC1_EQ_GAINS_1             0x480
+#define WM8994_AIF1_DAC1_EQ_GAINS_2             0x481
+#define WM8994_AIF1_DAC1_EQ_BAND_1_A            0x482
+#define WM8994_AIF1_DAC1_EQ_BAND_1_B            0x483
+#define WM8994_AIF1_DAC1_EQ_BAND_1_PG           0x484
+#define WM8994_AIF1_DAC1_EQ_BAND_2_A            0x485
+#define WM8994_AIF1_DAC1_EQ_BAND_2_B            0x486
+#define WM8994_AIF1_DAC1_EQ_BAND_2_C            0x487
+#define WM8994_AIF1_DAC1_EQ_BAND_2_PG           0x488
+#define WM8994_AIF1_DAC1_EQ_BAND_3_A            0x489
+#define WM8994_AIF1_DAC1_EQ_BAND_3_B            0x48A
+#define WM8994_AIF1_DAC1_EQ_BAND_3_C            0x48B
+#define WM8994_AIF1_DAC1_EQ_BAND_3_PG           0x48C
+#define WM8994_AIF1_DAC1_EQ_BAND_4_A            0x48D
+#define WM8994_AIF1_DAC1_EQ_BAND_4_B            0x48E
+#define WM8994_AIF1_DAC1_EQ_BAND_4_C            0x48F
+#define WM8994_AIF1_DAC1_EQ_BAND_4_PG           0x490
+#define WM8994_AIF1_DAC1_EQ_BAND_5_A            0x491
+#define WM8994_AIF1_DAC1_EQ_BAND_5_B            0x492
+#define WM8994_AIF1_DAC1_EQ_BAND_5_PG           0x493
+#define WM8994_AIF1_DAC2_EQ_GAINS_1             0x4A0
+#define WM8994_AIF1_DAC2_EQ_GAINS_2             0x4A1
+#define WM8994_AIF1_DAC2_EQ_BAND_1_A            0x4A2
+#define WM8994_AIF1_DAC2_EQ_BAND_1_B            0x4A3
+#define WM8994_AIF1_DAC2_EQ_BAND_1_PG           0x4A4
+#define WM8994_AIF1_DAC2_EQ_BAND_2_A            0x4A5
+#define WM8994_AIF1_DAC2_EQ_BAND_2_B            0x4A6
+#define WM8994_AIF1_DAC2_EQ_BAND_2_C            0x4A7
+#define WM8994_AIF1_DAC2_EQ_BAND_2_PG           0x4A8
+#define WM8994_AIF1_DAC2_EQ_BAND_3_A            0x4A9
+#define WM8994_AIF1_DAC2_EQ_BAND_3_B            0x4AA
+#define WM8994_AIF1_DAC2_EQ_BAND_3_C            0x4AB
+#define WM8994_AIF1_DAC2_EQ_BAND_3_PG           0x4AC
+#define WM8994_AIF1_DAC2_EQ_BAND_4_A            0x4AD
+#define WM8994_AIF1_DAC2_EQ_BAND_4_B            0x4AE
+#define WM8994_AIF1_DAC2_EQ_BAND_4_C            0x4AF
+#define WM8994_AIF1_DAC2_EQ_BAND_4_PG           0x4B0
+#define WM8994_AIF1_DAC2_EQ_BAND_5_A            0x4B1
+#define WM8994_AIF1_DAC2_EQ_BAND_5_B            0x4B2
+#define WM8994_AIF1_DAC2_EQ_BAND_5_PG           0x4B3
+#define WM8994_AIF2_ADC_LEFT_VOLUME             0x500
+#define WM8994_AIF2_ADC_RIGHT_VOLUME            0x501
+#define WM8994_AIF2_DAC_LEFT_VOLUME             0x502
+#define WM8994_AIF2_DAC_RIGHT_VOLUME            0x503
+#define WM8994_AIF2_ADC_FILTERS                 0x510
+#define WM8994_AIF2_DAC_FILTERS_1               0x520
+#define WM8994_AIF2_DAC_FILTERS_2               0x521
+#define WM8994_AIF2_DRC_1                       0x540
+#define WM8994_AIF2_DRC_2                       0x541
+#define WM8994_AIF2_DRC_3                       0x542
+#define WM8994_AIF2_DRC_4                       0x543
+#define WM8994_AIF2_DRC_5                       0x544
+#define WM8994_AIF2_EQ_GAINS_1                  0x580
+#define WM8994_AIF2_EQ_GAINS_2                  0x581
+#define WM8994_AIF2_EQ_BAND_1_A                 0x582
+#define WM8994_AIF2_EQ_BAND_1_B                 0x583
+#define WM8994_AIF2_EQ_BAND_1_PG                0x584
+#define WM8994_AIF2_EQ_BAND_2_A                 0x585
+#define WM8994_AIF2_EQ_BAND_2_B                 0x586
+#define WM8994_AIF2_EQ_BAND_2_C                 0x587
+#define WM8994_AIF2_EQ_BAND_2_PG                0x588
+#define WM8994_AIF2_EQ_BAND_3_A                 0x589
+#define WM8994_AIF2_EQ_BAND_3_B                 0x58A
+#define WM8994_AIF2_EQ_BAND_3_C                 0x58B
+#define WM8994_AIF2_EQ_BAND_3_PG                0x58C
+#define WM8994_AIF2_EQ_BAND_4_A                 0x58D
+#define WM8994_AIF2_EQ_BAND_4_B                 0x58E
+#define WM8994_AIF2_EQ_BAND_4_C                 0x58F
+#define WM8994_AIF2_EQ_BAND_4_PG                0x590
+#define WM8994_AIF2_EQ_BAND_5_A                 0x591
+#define WM8994_AIF2_EQ_BAND_5_B                 0x592
+#define WM8994_AIF2_EQ_BAND_5_PG                0x593
+#define WM8994_DAC1_MIXER_VOLUMES               0x600
+#define WM8994_DAC1_LEFT_MIXER_ROUTING          0x601
+#define WM8994_DAC1_RIGHT_MIXER_ROUTING         0x602
+#define WM8994_DAC2_MIXER_VOLUMES               0x603
+#define WM8994_DAC2_LEFT_MIXER_ROUTING          0x604
+#define WM8994_DAC2_RIGHT_MIXER_ROUTING         0x605
+#define WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING     0x606
+#define WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING    0x607
+#define WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING     0x608
+#define WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING    0x609
+#define WM8994_DAC1_LEFT_VOLUME                 0x610
+#define WM8994_DAC1_RIGHT_VOLUME                0x611
+#define WM8994_DAC2_LEFT_VOLUME                 0x612
+#define WM8994_DAC2_RIGHT_VOLUME                0x613
+#define WM8994_DAC_SOFTMUTE                     0x614
+#define WM8994_OVERSAMPLING                     0x620
+#define WM8994_SIDETONE                         0x621
+#define WM8994_GPIO_1                           0x700
+#define WM8994_GPIO_2                           0x701
+#define WM8994_GPIO_3                           0x702
+#define WM8994_GPIO_4                           0x703
+#define WM8994_GPIO_5                           0x704
+#define WM8994_GPIO_6                           0x705
+#define WM8994_GPIO_7                           0x706
+#define WM8994_GPIO_8                           0x707
+#define WM8994_GPIO_9                           0x708
+#define WM8994_GPIO_10                          0x709
+#define WM8994_GPIO_11                          0x70A
+#define WM8994_DIGITAL_PULLS                    0x720
+#define WM8994_INTERRUPT_STATUS_1               0x730
+#define WM8994_INTERRUPT_STATUS_2               0x731
+#define WM8994_INTERRUPT_STATUS_1_MASK          0x738
+#define WM8994_INTERRUPT_STATUS_2_MASK          0x739
+#define WM8994_INTERRUPT_CONTROL                0x740
+#define WM8994_IRQ_DEBOUNCE                     0x748
+#define WM8994_IRQ_POLARITY                     0x749
+
+#define WM8994_REGISTER_MEM_SIZE                0x74A
+
+#endif /* hw_s5pc1xx_wm8994_reg_h */
index da5297b..d4523c5 100644 (file)
@@ -67,6 +67,7 @@ struct XenInput {
     int button_state;       /* Last seen pointer button state */
     int extended;
     QEMUPutMouseEntry *qmouse;
+    QEMUPutKbdEntry *qkbd;
 };
 
 #define UP_QUEUE 8
@@ -297,7 +298,7 @@ static void xenfb_key_event(void *opaque, int scancode)
 
     if (scancode == 0xe0) {
        xenfb->extended = 1;
-       return;
+       return 0;
     } else if (scancode & 0x80) {
        scancode &= 0x7f;
        down = 0;
@@ -307,6 +308,7 @@ static void xenfb_key_event(void *opaque, int scancode)
        xenfb->extended = 0;
     }
     xenfb_send_key(xenfb, down, scancode2linux[scancode]);
+    return 0;
 }
 
 /*
@@ -372,7 +374,7 @@ static int input_connect(struct XenDevice *xendev)
     if (rc != 0)
        return rc;
 
-    qemu_add_kbd_event_handler(xenfb_key_event, in);
+    in->qkbd = qemu_add_kbd_event_handler(xenfb_key_event, in, "Xen Keyboard");
     in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
                                              in->abs_pointer_wanted,
                                              "Xen PVFB Mouse");
@@ -387,7 +389,7 @@ static void input_disconnect(struct XenDevice *xendev)
        qemu_remove_mouse_event_handler(in->qmouse);
        in->qmouse = NULL;
     }
-    qemu_add_kbd_event_handler(NULL, NULL);
+    qemu_remove_kbd_event_handler(in->qkbd);
     common_unbind(&in->c);
 }
 
diff --git a/input.c b/input.c
index 7e5fa1a..e2d71fb 100644 (file)
--- a/input.c
+++ b/input.c
 #include "console.h"
 #include "qjson.h"
 
-static QEMUPutKBDEvent *qemu_put_kbd_event;
-static void *qemu_put_kbd_event_opaque;
-static QEMUPutKBDEvent *qemu_put_ps2kbd_event;
-static void *qemu_put_ps2kbd_event_opaque;
+static QTAILQ_HEAD(, QEMUPutKbdEntry) kbd_handlers =
+    QTAILQ_HEAD_INITIALIZER(kbd_handlers);
 static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers);
 static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
     QTAILQ_HEAD_INITIALIZER(mouse_handlers);
 static NotifierList mouse_mode_notifiers = 
     NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
 
-void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
-{
-    qemu_put_kbd_event_opaque = opaque;
-    qemu_put_kbd_event = func;
-}
-
-void qemu_remove_kbd_event_handler(void)
-{
-    qemu_put_kbd_event_opaque = NULL;
-    qemu_put_kbd_event = NULL;
-}
-
-void qemu_add_ps2kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
-{
-    qemu_add_kbd_event_handler(func,opaque); // temporary code for compatibility with Xserver
-    qemu_put_ps2kbd_event_opaque = opaque;
-    qemu_put_ps2kbd_event = func;
-}
-
-void qemu_remove_ps2kbd_event_handler(void)
-{
-    qemu_put_ps2kbd_event_opaque = NULL;
-    qemu_put_ps2kbd_event = NULL;
-}
-
 static void check_mode_change(void)
 {
     static int current_is_absolute, current_has_absolute;
@@ -81,6 +54,38 @@ static void check_mode_change(void)
     current_has_absolute = has_absolute;
 }
 
+QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func,
+                                            void *opaque,
+                                            const char *name)
+{
+    static int index = 0;
+    QEMUPutKbdEntry *s, *cursor;
+
+    QTAILQ_FOREACH(cursor, &kbd_handlers, node) {
+        if (cursor->qemu_put_kbd_event == func &&
+            cursor->qemu_put_kbd_event_opaque == opaque) {
+            return cursor;
+        }
+    }
+
+    s = qemu_mallocz(sizeof(QEMUPutKbdEntry));
+
+    s->qemu_put_kbd_event_opaque = opaque;
+    s->qemu_put_kbd_event = func;
+    s->qemu_put_kbd_name = qemu_strdup(name);
+    s->index = index++;
+
+    QTAILQ_INSERT_TAIL(&kbd_handlers, s, node);
+
+    return s;
+}
+
+void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+{
+    QTAILQ_REMOVE(&kbd_handlers, entry, node);
+    qemu_free(entry);
+}
+
 QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
                                                 void *opaque, int absolute,
                                                 const char *name)
@@ -144,15 +149,12 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
 
 void kbd_put_keycode(int keycode)
 {
-    if (qemu_put_kbd_event) {
-        qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode);
-    }
-}
-
-void ps2kbd_put_keycode(int keycode)
-{
-    if (qemu_put_ps2kbd_event) {
-        qemu_put_ps2kbd_event(qemu_put_ps2kbd_event_opaque, keycode);
+    QEMUPutKbdEntry *entry;
+    QTAILQ_FOREACH(entry, &kbd_handlers, node) {
+        if (!entry->qemu_put_kbd_event(entry->qemu_put_kbd_event_opaque,
+                                       keycode)) {
+            break;
+        }
     }
 }
 
@@ -308,3 +310,114 @@ void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
 {
     notifier_list_remove(&mouse_mode_notifiers, notify);
 }
+
+static void info_keyboard_iter(QObject *data, void *opaque)
+{
+    QDict *kbd;
+    Monitor *mon = opaque;
+
+    kbd = qobject_to_qdict(data);
+    monitor_printf(mon, "%c Keyboard #%" PRId64 ": %s\n",
+                  (qdict_get_bool(kbd, "current") ? '*' : ' '),
+                  qdict_get_int(kbd, "index"), qdict_get_str(kbd, "name"));
+}
+
+void do_info_keyboard_print(Monitor *mon, const QObject *data)
+{
+    QList *kbd_list;
+
+    kbd_list = qobject_to_qlist(data);
+    if (qlist_empty(kbd_list)) {
+        monitor_printf(mon, "No keyboard devices connected\n");
+        return;
+    }
+
+    qlist_iter(kbd_list, info_keyboard_iter, mon);
+}
+
+void qemu_activate_keyboard_event_handler(QEMUPutKbdEntry *entry)
+{
+    QTAILQ_REMOVE(&kbd_handlers, entry, node);
+    QTAILQ_INSERT_HEAD(&kbd_handlers, entry, node);
+}
+
+/*
+ * do_info_keyboard(): Show VM keyboard information
+ *
+ * Each keyboard is represented by a QDict, the returned QObject is
+ * a QList of all keyboards.
+ *
+ * The keyboard QDict contains the following:
+ *
+ * - "name": keyboard's name
+ * - "index": keyboard's index
+ * - "current": true if this keyboard is receiving events, false otherwise
+ *
+ * Example:
+ *
+ * [ { "name": "QEMU USB Keyboard", "index": 0, "current": false },
+ *   { "name": "QEMU PS/2 Keyboard", "index": 1, "current": true } ]
+ */
+void do_info_keyboard(Monitor *mon, QObject **ret_data)
+{
+    QEMUPutKbdEntry *cursor;
+    QList *kbd_list;
+    int current;
+
+    kbd_list = qlist_new();
+
+    if (QTAILQ_EMPTY(&kbd_handlers)) {
+        goto out;
+    }
+
+    current = QTAILQ_FIRST(&kbd_handlers)->index;
+    QTAILQ_FOREACH(cursor, &kbd_handlers, node) {
+        QObject *obj;
+        obj = qobject_from_jsonf("{ 'name': %s,"
+                                 "  'index': %d,"
+                                 "  'current': %i }",
+                                 cursor->qemu_put_kbd_name,
+                                 cursor->index,
+                                 current == cursor->index);
+        qlist_append_obj(kbd_list, obj);
+    }
+out:
+    *ret_data = QOBJECT(kbd_list);
+}
+
+/*
+ * do_keyboard_set(): Set active keyboard
+ *
+ * Argument qdict contains
+ * - "index": the keyboard index to set
+ *
+ * Example:
+ *
+ * { "index": "0" }
+ */
+int do_keyboard_set(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    QEMUPutKbdEntry *cursor;
+    int index = qdict_get_int(qdict, "index");
+    int found = 0;
+
+    if (QTAILQ_EMPTY(&kbd_handlers)) {
+        qerror_report(QERR_DEVICE_NOT_FOUND, "keyboard");
+        return -1;
+    }
+
+    QTAILQ_FOREACH(cursor, &kbd_handlers, node) {
+        if (cursor->index == index) {
+            qemu_activate_keyboard_event_handler(cursor);
+            found = 1;
+            break;
+        }
+    }
+
+    if (!found) {
+        qerror_report(QERR_INVALID_PARAMETER, "index");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/linux-user/elfload32.c b/linux-user/elfload32.c
new file mode 100644 (file)
index 0000000..4b4648c
--- /dev/null
@@ -0,0 +1,30 @@
+#define TARGET_ABI32
+#define load_elf_binary load_elf_binary32
+#define do_init_thread do_init_thread32
+
+#include "elfload.c"
+
+#undef load_elf_binary
+#undef do_init_thread
+
+int load_elf_binary(struct linux_binprm *bprm, struct target_pt_regs *regs,
+                    struct image_info *info);
+
+int load_elf_binary_multi(struct linux_binprm *bprm,
+                          struct target_pt_regs *regs,
+                          struct image_info *info)
+{
+    struct elfhdr *elf_ex;
+    int retval;
+
+    elf_ex = (struct elfhdr *) bprm->buf;          /* exec-header */
+    if (elf_ex->e_ident[EI_CLASS] == ELFCLASS64) {
+        retval = load_elf_binary(bprm, regs, info);
+    } else {
+        retval = load_elf_binary32(bprm, regs, info);
+        if (personality(info->personality) == PER_LINUX)
+            info->personality = PER_LINUX32;
+    }
+
+    return retval;
+}
index 7fc311d..f6b8622 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -25,6 +25,7 @@
 #include "hw/hw.h"
 #include "hw/qdev.h"
 #include "hw/usb.h"
+#include "hw/usb-ehci.h"
 #include "hw/pcmcia.h"
 #include "hw/pc.h"
 #include "hw/pci.h"
@@ -3003,6 +3004,14 @@ static const mon_cmd_t info_cmds[] = {
         .mhandler.info_new = do_info_mice,
     },
     {
+        .name       = "keyboard",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show which guest keyboard is receiving events",
+        .user_print = do_info_keyboard_print,
+        .mhandler.info_new = do_info_keyboard,
+    },
+    {
         .name       = "vnc",
         .args_type  = "",
         .params     = "",
index 945edf3..1bf4ef4 100644 (file)
@@ -1018,6 +1018,18 @@ STEXI
 @end table
 ETEXI
 
+#ifdef CONFIG_GLES2
+STEXI
+@table @option
+ETEXI
+DEF("gles2-quality", HAS_ARG, QEMU_OPTION_gles2_quality,
+    "-gles2-quality  set GLES 2.0 rendering quality [0 ... 100]\n",
+    QEMU_ARCH_ARM)
+STEXI
+@end table
+ETEXI
+#endif
+
 DEFHEADING(Network options:)
 STEXI
 @table @option
index 47957eb..a7fa241 100755 (executable)
@@ -6,7 +6,7 @@ case $targetos in
 Linux*)
 echo "checking for os... targetos $targetos"
 exec ./configure \
- --target-list=i386-softmmu \
+ --target-list=i386-softmmu,arm-softmmu \
  --disable-werror \
  --audio-drv-list=pa \
  --enable-mixemu \
index df40a3d..355a020 100644 (file)
@@ -366,6 +366,112 @@ Note: CPUs' indexes are obtained with the 'query-cpus' command.
 EQMP
 
     {
+        .name       = "mouse_move",
+        .args_type  = "dx_str:s,dy_str:s,dz_str:s?",
+        .params     = "dx dy [dz]",
+        .help       = "send mouse move events",
+        .mhandler.cmd = do_mouse_move,
+    },
+
+STEXI
+@item mouse_move @var{dx} @var{dy} [@var{dz}]
+@findex mouse_move
+Move the active mouse to the specified coordinates @var{dx} @var{dy}
+with optional scroll axis @var{dz}.
+ETEXI
+
+    {
+        .name       = "mouse_button",
+        .args_type  = "button_state:i",
+        .params     = "state",
+        .help       = "change mouse button state (1=L, 2=M, 4=R)",
+        .mhandler.cmd = do_mouse_button,
+    },
+
+STEXI
+@item mouse_button @var{val}
+@findex mouse_button
+Change the active mouse button state @var{val} (1=L, 2=M, 4=R).
+ETEXI
+
+    {
+        .name       = "mouse_set",
+        .args_type  = "index:i",
+        .params     = "index",
+        .help       = "set which mouse device receives events",
+        .mhandler.cmd = do_mouse_set,
+    },
+
+STEXI
+@item mouse_set @var{index}
+@findex mouse_set
+Set which mouse device receives events at given @var{index}, index
+can be obtained with
+@example
+info mice
+@end example
+ETEXI
+
+    {
+        .name       = "keyboard_set",
+        .args_type  = "index:i",
+        .params     = "index",
+        .help       = "set which keyboard device receives events",
+        .mhandler.cmd_new = do_keyboard_set,
+    },
+
+STEXI
+@item keyboard_set @var{index}
+@findex keyboard_set
+Set which keyboard device receives events at given @var{index}, index
+can be obtained with
+@example
+info keyboard
+@end example
+ETEXI
+
+#ifdef HAS_AUDIO
+    {
+        .name       = "wavcapture",
+        .args_type  = "path:F,freq:i?,bits:i?,nchannels:i?",
+        .params     = "path [frequency [bits [channels]]]",
+        .help       = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
+        .mhandler.cmd = do_wav_capture,
+    },
+#endif
+STEXI
+@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
+@findex wavcapture
+Capture audio into @var{filename}. Using sample rate @var{frequency}
+bits per sample @var{bits} and number of channels @var{channels}.
+
+Defaults:
+@itemize @minus
+@item Sample rate = 44100 Hz - CD quality
+@item Bits = 16
+@item Number of channels = 2 - Stereo
+@end itemize
+ETEXI
+
+#ifdef HAS_AUDIO
+    {
+        .name       = "stopcapture",
+        .args_type  = "n:i",
+        .params     = "capture index",
+        .help       = "stop capture",
+        .mhandler.cmd = do_stop_capture,
+    },
+#endif
+STEXI
+@item stopcapture @var{index}
+@findex stopcapture
+Stop capture with a given @var{index}, index can be obtained with
+@example
+info capture
+@end example
+ETEXI
+
+    {
         .name       = "memsave",
         .args_type  = "val:l,size:i,filename:s",
         .params     = "addr size file",
index 2692822..87967fe 100644 (file)
@@ -158,4 +158,9 @@ void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
                 const char *message);
 void icmp_reflect(struct mbuf *);
 
+#ifndef _WIN32
+#include <sys/wait.h>
+#endif
+#include <unistd.h>
+
 #endif
index 5bcd53a..71832eb 100644 (file)
@@ -43,6 +43,7 @@
 #define EXCP_EXCEPTION_EXIT  8   /* Return from v7M exception.  */
 #define EXCP_KERNEL_TRAP     9   /* Jumped to kernel code page.  */
 #define EXCP_STREX          10
+#define EXCP_SMC            11   /* secure monitor call */
 
 #define ARMV7M_EXCP_RESET   1
 #define ARMV7M_EXCP_NMI     2
@@ -82,9 +83,9 @@ typedef struct CPUARMState {
     uint32_t spsr;
 
     /* Banked registers.  */
-    uint32_t banked_spsr[6];
-    uint32_t banked_r13[6];
-    uint32_t banked_r14[6];
+    uint32_t banked_spsr[7];
+    uint32_t banked_r13[7];
+    uint32_t banked_r14[7];
 
     /* These hold r8-r12.  */
     uint32_t usr_regs[5];
@@ -99,6 +100,9 @@ typedef struct CPUARMState {
     uint32_t GE; /* cpsr[19:16] */
     uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
     uint32_t condexec_bits; /* IT bits.  cpsr[15:10,26:25].  */
+    uint32_t actual_condexec_bits; /* Real IT bits updated as execution goes.  */
+    uint32_t saved_condexec_bits; /* Saved IT bits to be restored after interrupt.  */
+    uint32_t saved_condexec_tb; /* Address of a new TB after interrupt inside of IT block.  */
 
     /* System control coprocessor (cp15) */
     struct {
@@ -112,6 +116,9 @@ typedef struct CPUARMState {
         uint32_t c1_sys; /* System control register.  */
         uint32_t c1_coproc; /* Coprocessor access register.  */
         uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
+        uint32_t c1_secfg; /* Secure configuration register. */
+        uint32_t c1_sedbg; /* Secure debug enable register. */
+        uint32_t c1_nseac; /* Non-secure access control register. */
         uint32_t c2_base0; /* MMU translation table base 0.  */
         uint32_t c2_base1; /* MMU translation table base 1.  */
         uint32_t c2_control; /* MMU translation table base control.  */
@@ -128,6 +135,8 @@ typedef struct CPUARMState {
         uint32_t c6_data;
         uint32_t c9_insn; /* Cache lockdown registers.  */
         uint32_t c9_data;
+        uint32_t c12_vbar; /* secure/nonsecure vector base address register. */
+        uint32_t c12_mvbar; /* monitor vector base address register. */
         uint32_t c13_fcse; /* FCSE PID.  */
         uint32_t c13_context; /* Context ID.  */
         uint32_t c13_tls1; /* User RW Thread register.  */
@@ -303,10 +312,12 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
     if (mask & CPSR_IT_0_1) {
         env->condexec_bits &= ~3;
         env->condexec_bits |= (val >> 25) & 3;
+        env->actual_condexec_bits = env->condexec_bits;
     }
     if (mask & CPSR_IT_2_7) {
         env->condexec_bits &= 3;
         env->condexec_bits |= (val >> 8) & 0xfc;
+        env->actual_condexec_bits = env->condexec_bits;
     }
     if (mask & 0x1ff) {
         env->v7m.exception = val & 0x1ff;
@@ -322,6 +333,7 @@ enum arm_cpu_mode {
   ARM_CPU_MODE_FIQ = 0x11,
   ARM_CPU_MODE_IRQ = 0x12,
   ARM_CPU_MODE_SVC = 0x13,
+  ARM_CPU_MODE_SMC = 0x16,
   ARM_CPU_MODE_ABT = 0x17,
   ARM_CPU_MODE_UND = 0x1b,
   ARM_CPU_MODE_SYS = 0x1f
@@ -362,7 +374,8 @@ enum arm_features {
     ARM_FEATURE_DIV,
     ARM_FEATURE_M, /* Microcontroller profile.  */
     ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
-    ARM_FEATURE_THUMB2EE
+    ARM_FEATURE_THUMB2EE,
+    ARM_FEATURE_TRUSTZONE /* TrustZone Security Extensions. */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
@@ -409,6 +422,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define ARM_CPUID_ARM1136_R2  0x4107b362
 #define ARM_CPUID_ARM11MPCORE 0x410fb022
 #define ARM_CPUID_CORTEXA8    0x410fc080
+#define ARM_CPUID_CORTEXA8_R2 0x412fc083
 #define ARM_CPUID_CORTEXA9    0x410fc090
 #define ARM_CPUID_CORTEXM3    0x410fc231
 #define ARM_CPUID_ANY         0xffffffff
index b562767..00218ac 100644 (file)
@@ -24,6 +24,9 @@ static uint32_t cortexa8_cp15_c0_c1[8] =
 static uint32_t cortexa8_cp15_c0_c2[8] =
 { 0x00101111, 0x12112111, 0x21232031, 0x11112131, 0x00111142, 0, 0, 0 };
 
+static uint32_t cortexa8r2_cp15_c0_c2[8] =
+{ 0x00101111, 0x12112111, 0x21232031, 0x11112131, 0x00011142, 0, 0, 0 };
+
 static uint32_t mpcore_cp15_c0_c1[8] =
 { 0x111, 0x1, 0, 0x2, 0x01100103, 0x10020302, 0x01222000, 0 };
 
@@ -100,6 +103,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         set_feature(env, ARM_FEATURE_VFP3);
         set_feature(env, ARM_FEATURE_NEON);
         set_feature(env, ARM_FEATURE_THUMB2EE);
+        set_feature(env, ARM_FEATURE_TRUSTZONE);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410330c0;
         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
         env->vfp.xregs[ARM_VFP_MVFR1] = 0x00011100;
@@ -112,6 +116,28 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_ccsid[2] = 0xf0000000; /* No L2 icache. */
         env->cp15.c1_sys = 0x00c50078;
         break;
+    case ARM_CPUID_CORTEXA8_R2:
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_V6K);
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_AUXCR);
+        set_feature(env, ARM_FEATURE_THUMB2);
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_VFP3);
+        set_feature(env, ARM_FEATURE_NEON);
+        set_feature(env, ARM_FEATURE_THUMB2EE);
+        set_feature(env, ARM_FEATURE_TRUSTZONE);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410330c2;
+        env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
+        env->vfp.xregs[ARM_VFP_MVFR1] = 0x00011111;
+        memcpy(env->cp15.c0_c1, cortexa8_cp15_c0_c1, 8 * sizeof(uint32_t));
+        memcpy(env->cp15.c0_c2, cortexa8r2_cp15_c0_c2, 8 * sizeof(uint32_t));
+        env->cp15.c0_cachetype = 0x82048004;
+        env->cp15.c0_clid = (1 << 27) | (2 << 24) | (4 << 3) | 3;
+        env->cp15.c0_ccsid[0] = 0xe007e01a; /* 16k L1 dcache. */
+        env->cp15.c0_ccsid[1] = 0x2007e01a; /* 16k L1 icache. */
+        env->cp15.c0_ccsid[2] = 0xf03fe03a; /* 256k L2 cache. */
+        break;
     case ARM_CPUID_CORTEXA9:
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
@@ -123,6 +149,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         set_feature(env, ARM_FEATURE_VFP_FP16);
         set_feature(env, ARM_FEATURE_NEON);
         set_feature(env, ARM_FEATURE_THUMB2EE);
+        set_feature(env, ARM_FEATURE_TRUSTZONE);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x41034000; /* Guess */
         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
         env->vfp.xregs[ARM_VFP_MVFR1] = 0x01111111;
@@ -342,6 +369,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
     { ARM_CPUID_ARM11MPCORE, "arm11mpcore"},
     { ARM_CPUID_CORTEXM3, "cortex-m3"},
     { ARM_CPUID_CORTEXA8, "cortex-a8"},
+    { ARM_CPUID_CORTEXA8_R2, "cortex-a8-r2"},
     { ARM_CPUID_CORTEXA9, "cortex-a9"},
     { ARM_CPUID_TI925T, "ti925t" },
     { ARM_CPUID_PXA250, "pxa250" },
@@ -417,10 +445,12 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
     if (mask & CPSR_IT_0_1) {
         env->condexec_bits &= ~3;
         env->condexec_bits |= (val >> 25) & 3;
+        env->actual_condexec_bits = env->condexec_bits;
     }
     if (mask & CPSR_IT_2_7) {
         env->condexec_bits &= 3;
         env->condexec_bits |= (val >> 8) & 0xfc;
+        env->actual_condexec_bits = env->condexec_bits;
     }
     if (mask & CPSR_GE) {
         env->GE = (val >> 16) & 0xf;
@@ -586,6 +616,8 @@ static inline int bank_number (int mode)
         return 4;
     case ARM_CPU_MODE_FIQ:
         return 5;
+    case ARM_CPU_MODE_SMC:
+        return 6;
     }
     cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
     return -1;
@@ -759,6 +791,13 @@ void do_interrupt(CPUARMState *env)
     int new_mode;
     uint32_t offset;
 
+#if 0
+    static uint32_t interrupt_count = 0;
+    interrupt_count++;
+    if ((interrupt_count % 4096) == 0)
+        printf("  $$$ INTERRUPT COUNT = %u $$$  \n", interrupt_count);
+#endif
+
     if (IS_M(env)) {
         do_interrupt_v7m(env);
         return;
@@ -835,13 +874,47 @@ void do_interrupt(CPUARMState *env)
         mask = CPSR_A | CPSR_I | CPSR_F;
         offset = 4;
         break;
+    case EXCP_SMC:
+        if (semihosting_enabled) {
+            cpu_abort(env, "SMC handling under semihosting not implemented\n");
+            return;
+        }
+        if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SMC) {
+            env->cp15.c1_secfg &= ~1;
+        }
+        offset = env->thumb ? 2 : 0;
+        new_mode = ARM_CPU_MODE_SMC;
+        addr = 0x08;
+        mask = CPSR_A | CPSR_I | CPSR_F;
+        break;
     default:
         cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
         return; /* Never happens.  Keep compiler happy.  */
     }
-    /* High vectors.  */
-    if (env->cp15.c1_sys & (1 << 13)) {
-        addr += 0xffff0000;
+    /* Save actual IT bits along with the address where they belong.
+     * When return from ISR happens these should be restored to preserve
+     * correct execution.  */
+    if (env->thumb && env->actual_condexec_bits) {
+        env->saved_condexec_tb = env->regs[15];
+        env->saved_condexec_bits = env->actual_condexec_bits;
+        env->actual_condexec_bits = 0;
+    }
+    if (arm_feature(env, ARM_FEATURE_TRUSTZONE)) {
+        if (new_mode == ARM_CPU_MODE_SMC ||
+            (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SMC) {
+            addr += env->cp15.c12_mvbar;
+        } else {
+            if (env->cp15.c1_sys & (1 << 13)) {
+                addr += 0xffff0000;
+            } else {
+                addr += env->cp15.c12_vbar;
+            }
+        }
+    } else {
+        /* High vectors.  */
+        if (env->cp15.c1_sys & (1 << 13)) {
+            addr += 0xffff0000;
+        }
     }
     switch_mode (env, new_mode);
     env->spsr = cpsr_read(env);
@@ -1032,7 +1105,7 @@ static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type,
     table = get_level1_table_address(env, address);
     desc = ldl_phys(table);
     type = (desc & 3);
-    if (type == 0) {
+    if (type == 0 || type == 3) {
         /* Section translation fault.  */
         code = 5;
         domain = 0;
@@ -1177,10 +1250,18 @@ static int get_phys_addr_mpu(CPUState *env, uint32_t address, int access_type,
     return 0;
 }
 
-static inline int get_phys_addr(CPUState *env, uint32_t address,
-                                int access_type, int is_user,
-                                uint32_t *phys_ptr, int *prot,
-                                target_ulong *page_size)
+#ifdef CONFIG_GLES2
+int get_phys_addr(CPUState *env, uint32_t address,
+                  int access_type, int is_user,
+                  uint32_t *phys_ptr, int *prot,
+                  target_ulong *page_size);
+#else
+static inline
+#endif
+int get_phys_addr(CPUState *env, uint32_t address,
+                  int access_type, int is_user,
+                  uint32_t *phys_ptr, int *prot,
+                  target_ulong *page_size)
 {
     /* Fast Context Switch Extension.  */
     if (address < 0x02000000)
@@ -1331,30 +1412,60 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
         }
         goto bad_reg;
     case 1: /* System configuration.  */
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            op2 = 0;
-        switch (op2) {
+        switch (crm) {
         case 0:
-            if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
-                env->cp15.c1_sys = val;
-            /* ??? Lots of these bits are not implemented.  */
-            /* This may enable/disable the MMU, so do a TLB flush.  */
-            tlb_flush(env, 1);
-            break;
-        case 1: /* Auxiliary cotrol register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE)) {
-                env->cp15.c1_xscaleauxcr = val;
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
+            switch (op2) {
+            case 0:
+                if (!arm_feature(env, ARM_FEATURE_XSCALE))
+                    env->cp15.c1_sys = val;
+                /* ??? Lots of these bits are not implemented.  */
+                /* This may enable/disable the MMU, so do a TLB flush.  */
+                tlb_flush(env, 1);
                 break;
+            case 1: /* Auxiliary cotrol register.  */
+                if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+                    env->cp15.c1_xscaleauxcr = val;
+                    break;
+                }
+                /* Not implemented.  */
+                break;
+            case 2:
+                if (arm_feature(env, ARM_FEATURE_XSCALE))
+                    goto bad_reg;
+                if (env->cp15.c1_coproc != val) {
+                    env->cp15.c1_coproc = val;
+                    /* ??? Is this safe when called from within a TB?  */
+                    tb_flush(env);
+                }
+                break;
+            default:
+                goto bad_reg;
             }
-            /* Not implemented.  */
             break;
-        case 2:
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
+        case 1:
+            if (!arm_feature(env, ARM_FEATURE_TRUSTZONE)
+                || (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
+                goto bad_reg;
+            switch (op2) {
+            case 0: /* Secure configuration register. */
+                if (env->cp15.c1_secfg & 1)
+                    goto bad_reg;
+                env->cp15.c1_secfg = val;
+                break;
+            case 1: /* Secure debug enable register. */
+                if (env->cp15.c1_secfg & 1)
+                    goto bad_reg;
+                env->cp15.c1_sedbg = val;
+                break;
+            case 2: /* Nonsecure access control register. */
+                if (env->cp15.c1_secfg & 1)
+                    goto bad_reg;
+                env->cp15.c1_nseac = val;
+                break;
+            default:
                 goto bad_reg;
-            if (env->cp15.c1_coproc != val) {
-                env->cp15.c1_coproc = val;
-                /* ??? Is this safe when called from within a TB?  */
-                tb_flush(env);
             }
             break;
         default:
@@ -1374,22 +1485,22 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
                 goto bad_reg;
             }
         } else {
-           switch (op2) {
-           case 0:
-               env->cp15.c2_base0 = val;
-               break;
-           case 1:
-               env->cp15.c2_base1 = val;
-               break;
-           case 2:
+            switch (op2) {
+            case 0:
+                env->cp15.c2_base0 = val;
+                break;
+            case 1:
+                env->cp15.c2_base1 = val;
+                break;
+            case 2:
                 val &= 7;
                 env->cp15.c2_control = val;
-               env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val);
+                env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val);
                 env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> val);
-               break;
-           default:
-               goto bad_reg;
-           }
+                break;
+            default:
+                goto bad_reg;
+            }
         }
         break;
     case 3: /* MMU Domain access control / MPU write buffer control.  */
@@ -1477,29 +1588,31 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
             break;
         switch (crm) {
         case 0: /* Cache lockdown.  */
-           switch (op1) {
-           case 0: /* L1 cache.  */
-               switch (op2) {
-               case 0:
-                   env->cp15.c9_data = val;
-                   break;
-               case 1:
-                   env->cp15.c9_insn = val;
-                   break;
-               default:
-                   goto bad_reg;
-               }
-               break;
-           case 1: /* L2 cache.  */
-               /* Ignore writes to L2 lockdown/auxiliary registers.  */
-               break;
-           default:
-               goto bad_reg;
-           }
-           break;
+            switch (op1) {
+            case 0: /* L1 cache.  */
+                switch (op2) {
+                case 0:
+                    env->cp15.c9_data = val;
+                    break;
+                case 1:
+                    env->cp15.c9_insn = val;
+                    break;
+                default:
+                    goto bad_reg;
+                }
+                break;
+            case 1: /* L2 cache.  */
+                /* Ignore writes to L2 lockdown/auxiliary registers.  */
+                break;
+            default:
+                goto bad_reg;
+            }
+            break;
         case 1: /* TCM memory region registers.  */
             /* Not implemented.  */
             goto bad_reg;
+        case 12: /* Performance counters.  */
+            break;
         default:
             goto bad_reg;
         }
@@ -1508,6 +1621,27 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
         /* ??? TLB lockdown not implemented.  */
         break;
     case 12: /* Reserved.  */
+        if (!op1 && !crm) {
+            switch (op2) {
+            case 0:
+                if (!arm_feature(env, ARM_FEATURE_TRUSTZONE)) {
+                    goto bad_reg;
+                }
+                env->cp15.c12_vbar = val & ~0x1f;
+                break;
+            case 1:
+                if (!arm_feature(env, ARM_FEATURE_TRUSTZONE)) {
+                    goto bad_reg;
+                }
+                if (!(env->cp15.c1_secfg & 1)) {
+                    env->cp15.c12_mvbar = val & ~0x1f;
+                }
+                break;
+            default:
+                goto bad_reg;
+            }
+            break;
+        }
         goto bad_reg;
     case 13: /* Process ID.  */
         switch (op2) {
@@ -1597,7 +1731,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
                 case 0: /* Device ID.  */
                     return env->cp15.c0_cpuid;
                 case 1: /* Cache Type.  */
-                   return env->cp15.c0_cachetype;
+                    return env->cp15.c0_cachetype;
                 case 2: /* TCM status.  */
                     return 0;
                 case 3: /* TLB type register.  */
@@ -1624,6 +1758,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
             default:
                 goto bad_reg;
             }
+            break;
         case 1:
             /* These registers aren't documented on arm11 cores.  However
                Linux looks at them anyway.  */
@@ -1650,39 +1785,68 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
         default:
             goto bad_reg;
         }
+        break;
     case 1: /* System configuration.  */
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            op2 = 0;
-        switch (op2) {
-        case 0: /* Control register.  */
-            return env->cp15.c1_sys;
-        case 1: /* Auxiliary control register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
-                return env->cp15.c1_xscaleauxcr;
-            if (!arm_feature(env, ARM_FEATURE_AUXCR))
-                goto bad_reg;
-            switch (ARM_CPUID(env)) {
-            case ARM_CPUID_ARM1026:
-                return 1;
-            case ARM_CPUID_ARM1136:
-            case ARM_CPUID_ARM1136_R2:
-                return 7;
-            case ARM_CPUID_ARM11MPCORE:
-                return 1;
-            case ARM_CPUID_CORTEXA8:
-                return 2;
-            case ARM_CPUID_CORTEXA9:
-                return 0;
+        switch (crm) {
+        case 0:
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
+            switch (op2) {
+            case 0: /* Control register.  */
+                return env->cp15.c1_sys;
+            case 1: /* Auxiliary control register.  */
+                if (arm_feature(env, ARM_FEATURE_XSCALE))
+                    return env->cp15.c1_xscaleauxcr;
+                if (!arm_feature(env, ARM_FEATURE_AUXCR))
+                    goto bad_reg;
+                switch (ARM_CPUID(env)) {
+                case ARM_CPUID_ARM1026:
+                    return 1;
+                case ARM_CPUID_ARM1136:
+                case ARM_CPUID_ARM1136_R2:
+                    return 7;
+                case ARM_CPUID_ARM11MPCORE:
+                    return 1;
+                case ARM_CPUID_CORTEXA8:
+                case ARM_CPUID_CORTEXA8_R2:
+                    return 2;
+                case ARM_CPUID_CORTEXA9:
+                    return 0;
+                default:
+                    goto bad_reg;
+                }
+                break;
+            case 2: /* Coprocessor access register.  */
+                if (arm_feature(env, ARM_FEATURE_XSCALE))
+                    goto bad_reg;
+                return env->cp15.c1_coproc;
             default:
                 goto bad_reg;
             }
-        case 2: /* Coprocessor access register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
+            break;
+        case 1:
+            if (!arm_feature(env, ARM_FEATURE_TRUSTZONE)
+                || (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
+                goto bad_reg;
+            switch (op2) {
+            case 0: /* Secure configuration register. */
+                if (env->cp15.c1_secfg & 1)
+                    goto bad_reg;
+                return env->cp15.c1_secfg;
+            case 1: /* Secure debug enable register. */
+                if (env->cp15.c1_secfg & 1)
+                    goto bad_reg;
+                return env->cp15.c1_sedbg;
+            case 2: /* Nonsecure access control register. */
+                return env->cp15.c1_nseac;
+            default:
                 goto bad_reg;
-            return env->cp15.c1_coproc;
+            }
+            break;
         default:
             goto bad_reg;
         }
+        break;
     case 2: /* MMU Page table control / MPU cache control.  */
         if (arm_feature(env, ARM_FEATURE_MPU)) {
             switch (op2) {
@@ -1696,17 +1860,17 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
                 goto bad_reg;
             }
         } else {
-           switch (op2) {
-           case 0:
-               return env->cp15.c2_base0;
-           case 1:
-               return env->cp15.c2_base1;
-           case 2:
+            switch (op2) {
+            case 0:
+                return env->cp15.c2_base0;
+            case 1:
+                return env->cp15.c2_base1;
+            case 2:
                 return env->cp15.c2_control;
-           default:
-               goto bad_reg;
-           }
-       }
+            default:
+                goto bad_reg;
+            }
+        }
     case 3: /* MMU Domain access control / MPU write buffer control.  */
         return env->cp15.c3;
     case 4: /* Reserved.  */
@@ -1742,41 +1906,39 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
         } else {
             if (arm_feature(env, ARM_FEATURE_OMAPCP))
                 op2 = 0;
-           switch (op2) {
-           case 0:
-               return env->cp15.c6_data;
-           case 1:
-               if (arm_feature(env, ARM_FEATURE_V6)) {
-                   /* Watchpoint Fault Adrress.  */
-                   return 0; /* Not implemented.  */
-               } else {
-                   /* Instruction Fault Adrress.  */
-                   /* Arm9 doesn't have an IFAR, but implementing it anyway
-                      shouldn't do any harm.  */
-                   return env->cp15.c6_insn;
-               }
-           case 2:
-               if (arm_feature(env, ARM_FEATURE_V6)) {
-                   /* Instruction Fault Adrress.  */
-                   return env->cp15.c6_insn;
-               } else {
-                   goto bad_reg;
-               }
-           default:
-               goto bad_reg;
-           }
+            switch (op2) {
+            case 0:
+                return env->cp15.c6_data;
+            case 1:
+                if (arm_feature(env, ARM_FEATURE_V6)) {
+                    /* Watchpoint Fault Adrress.  */
+                    return 0; /* Not implemented.  */
+                }
+                /* Instruction Fault Adrress.  */
+                /* Arm9 doesn't have an IFAR, but implementing it anyway
+                   shouldn't do any harm.  */
+                return env->cp15.c6_insn;
+            case 2:
+                if (arm_feature(env, ARM_FEATURE_V6)) {
+                    /* Instruction Fault Adrress.  */
+                    return env->cp15.c6_insn;
+                }
+                goto bad_reg;
+            default:
+                goto bad_reg;
+            }
         }
     case 7: /* Cache control.  */
-        /* FIXME: Should only clear Z flag if destination is r15.  */
-        env->ZF = 0;
+        if (((insn >> 12) & 0xf) == 0xf) /* clear ZF only if destination is r15 */
+            env->ZF = 0;
         return 0;
     case 8: /* MMU TLB control.  */
         goto bad_reg;
     case 9: /* Cache lockdown.  */
         switch (op1) {
         case 0: /* L1 cache.  */
-           if (arm_feature(env, ARM_FEATURE_OMAPCP))
-               return 0;
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                return 0;
             switch (op2) {
             case 0:
                 return env->cp15.c9_data;
@@ -1798,6 +1960,22 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
         return 0;
     case 11: /* TCM DMA control.  */
     case 12: /* Reserved.  */
+        if (!op1 && !crm) {
+            switch (op2) {
+            case 0: /* secure or nonsecure vector base address */
+                if (arm_feature(env, ARM_FEATURE_TRUSTZONE)) {
+                    return env->cp15.c12_vbar;
+                }
+                break;
+            case 1: /* monitor vector base address */
+                if (arm_feature(env, ARM_FEATURE_TRUSTZONE)) {
+                    return env->cp15.c12_mvbar;
+                }
+                break;
+            default:
+                break;
+            }
+        }
         goto bad_reg;
     case 13: /* Process ID.  */
         switch (op2) {
diff --git a/target-arm/helper_gpi_dummy.c b/target-arm/helper_gpi_dummy.c
new file mode 100644 (file)
index 0000000..26991d8
--- /dev/null
@@ -0,0 +1,9 @@
+#include "debug_ch.h"
+MULTI_DEBUG_CHANNEL(qemu, arm_dummy);
+
+int call_gpi(int pid, int call_num, char *in_args, int args_len, char *r_buffer, int r_length);
+
+int call_gpi(int pid, int call_num, char *in_args, int args_len, char *r_buffer, int r_length){
+    ERR("virtio -> call_gpi(arm_dummy) called!!!\n");
+    return 0;
+}
index 8a2564e..541ed23 100644 (file)
@@ -137,6 +137,11 @@ DEF_HELPER_2(rsqrte_f32, f32, f32, env)
 DEF_HELPER_2(recpe_u32, i32, i32, env)
 DEF_HELPER_2(rsqrte_u32, i32, i32, env)
 DEF_HELPER_4(neon_tbl, i32, i32, i32, i32, i32)
+DEF_HELPER_2(neon_add_saturate_u64, i64, i64, i64)
+DEF_HELPER_2(neon_add_saturate_s64, i64, i64, i64)
+DEF_HELPER_2(neon_sub_saturate_u64, i64, i64, i64)
+DEF_HELPER_2(neon_sub_saturate_s64, i64, i64, i64)
+
 
 DEF_HELPER_2(add_cc, i32, i32, i32)
 DEF_HELPER_2(adc_cc, i32, i32, i32)
@@ -275,6 +280,7 @@ DEF_HELPER_2(neon_sub_u16, i32, i32, i32)
 DEF_HELPER_2(neon_mul_u8, i32, i32, i32)
 DEF_HELPER_2(neon_mul_u16, i32, i32, i32)
 DEF_HELPER_2(neon_mul_p8, i32, i32, i32)
+DEF_HELPER_2(neon_mull_p8, i64, i32, i32)
 
 DEF_HELPER_2(neon_tst_u8, i32, i32, i32)
 DEF_HELPER_2(neon_tst_u16, i32, i32, i32)
@@ -299,10 +305,13 @@ DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32)
 
 DEF_HELPER_1(neon_narrow_u8, i32, i64)
 DEF_HELPER_1(neon_narrow_u16, i32, i64)
+DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64)
 DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64)
 DEF_HELPER_1(neon_narrow_high_u8, i32, i64)
@@ -455,4 +464,8 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32)
 
 DEF_HELPER_2(set_teecr, void, env, i32)
 
+DEF_HELPER_2(neon_unzip, void, env, i32)
+DEF_HELPER_2(neon_zip, void, env, i32)
+DEF_HELPER_2(neon_vldst_all, void, env, i32)
+
 #include "def-helper.h"
index 3925d3a..8595549 100644 (file)
@@ -26,6 +26,9 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, env->cp15.c1_sys);
     qemu_put_be32(f, env->cp15.c1_coproc);
     qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
+    qemu_put_be32(f, env->cp15.c1_secfg);
+    qemu_put_be32(f, env->cp15.c1_sedbg);
+    qemu_put_be32(f, env->cp15.c1_nseac);
     qemu_put_be32(f, env->cp15.c2_base0);
     qemu_put_be32(f, env->cp15.c2_base1);
     qemu_put_be32(f, env->cp15.c2_control);
@@ -133,6 +136,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     env->cp15.c1_sys = qemu_get_be32(f);
     env->cp15.c1_coproc = qemu_get_be32(f);
     env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
+    env->cp15.c1_secfg = qemu_get_be32(f);
+    env->cp15.c1_sedbg = qemu_get_be32(f);
+    env->cp15.c1_nseac = qemu_get_be32(f);
     env->cp15.c2_base0 = qemu_get_be32(f);
     env->cp15.c2_base1 = qemu_get_be32(f);
     env->cp15.c2_control = qemu_get_be32(f);
index 268af33..f906e0b 100644 (file)
@@ -29,6 +29,11 @@ static inline float32 vfp_itos(uint32_t i)
         float32 s;
     } v;
 
+    /* flush-to-zero */
+    if (!(i & (0xff << 23))) {
+        i &= 1 << 31; /* make it +-0 */
+    }
+
     v.i = i;
     return v.s;
 }
@@ -558,9 +563,28 @@ uint64_t HELPER(neon_shl_s64)(uint64_t valop, uint64_t shiftop)
     }} while (0)
 NEON_VOP(rshl_s8, neon_s8, 4)
 NEON_VOP(rshl_s16, neon_s16, 2)
-NEON_VOP(rshl_s32, neon_s32, 1)
 #undef NEON_FN
 
+uint32_t HELPER(neon_rshl_s32)(uint32_t valop, uint32_t shiftop)
+{
+    int8_t shift =(int8_t)shiftop;
+    int32_t val = valop;
+    if (shift >= 32) {
+        val = 0;
+    } else if (shift < -32) {
+        val >>= 31;
+    } else if (shift == -32) {
+        val >>= 31;
+        val++;
+        val >>= 1;
+    } else if (shift < 0) {
+        val = ((int64_t)val + (1 << (-1 - shift))) >> -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
 uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
 {
     int8_t shift = (int8_t)shiftop;
@@ -569,7 +593,7 @@ uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
         val = 0;
     } else if (shift < -64) {
         val >>= 63;
-    } else if (shift == -63) {
+    } else if (shift == -64) {
         val >>= 63;
         val++;
         val >>= 1;
@@ -588,7 +612,7 @@ uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
         tmp < -(ssize_t)sizeof(src1) * 8) { \
         dest = 0; \
     } else if (tmp == -(ssize_t)sizeof(src1) * 8) { \
-        dest = src1 >> (tmp - 1); \
+        dest = src1 >> (-tmp - 1); \
     } else if (tmp < 0) { \
         dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
     } else { \
@@ -596,20 +620,39 @@ uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
     }} while (0)
 NEON_VOP(rshl_u8, neon_u8, 4)
 NEON_VOP(rshl_u16, neon_u16, 2)
-NEON_VOP(rshl_u32, neon_u32, 1)
 #undef NEON_FN
 
+uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    if (shift >= 32 || shift < -32) {
+        val = 0;
+    } else if (shift == -32) {
+        val >>= 31;
+    } else if (shift < 0) {
+        val = ((uint64_t)val + (1 << (-1 - shift))) >> -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
 uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop)
 {
     int8_t shift = (uint8_t)shiftop;
-    if (shift >= 64 || shift < 64) {
+    if (shift >= 64 || shift < -64) {
         val = 0;
     } else if (shift == -64) {
         /* Rounding a 1-bit result just preserves that bit.  */
         val >>= 63;
     } if (shift < 0) {
-        val = (val + ((uint64_t)1 << (-1 - shift))) >> -shift;
-        val >>= -shift;
+        uint64_t r = ((uint64_t)1 << (-1 - shift));
+        uint64_t lo = val + r;
+        if (lo < val || lo < r) {
+            val = (lo >> -shift) | ((1ull << 63) >> (-shift - 1));
+        } else {
+            val = lo >> -shift;
+        }
     } else {
         val <<= shift;
     }
@@ -784,9 +827,24 @@ uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
     }} while (0)
 NEON_VOP_ENV(qrshl_u8, neon_u8, 4)
 NEON_VOP_ENV(qrshl_u16, neon_u16, 2)
-NEON_VOP_ENV(qrshl_u32, neon_u32, 1)
 #undef NEON_FN
 
+uint32_t HELPER(neon_qrshl_u32)(CPUState *env, uint32_t val, uint32_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    if (shift < 0) {
+        val = ((uint64_t)val + (1 << (-1 - shift))) >> -shift;
+    } else {
+        uint32_t tmp = val;
+        val <<= shift;
+        if ((val >> shift) != tmp) {
+            SET_QC();
+            val = ~0;
+        }
+    }
+    return val;
+}
+
 uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
 {
     int8_t shift = (int8_t)shiftop;
@@ -812,7 +870,7 @@ uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
         dest = src1 << tmp; \
         if ((dest >> tmp) != src1) { \
             SET_QC(); \
-            dest = src1 >> 31; \
+            dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)) - (src1 > 0 ? 1 : 0); \
         } \
     }} while (0)
 NEON_VOP_ENV(qrshl_s8, neon_s8, 4)
@@ -828,7 +886,7 @@ uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
     if (shift < 0) {
         val = (val + (1 << (-1 - shift))) >> -shift;
     } else {
-        int64_t tmp = val;;
+        int64_t tmp = val;
         val <<= shift;
         if ((val >> shift) != tmp) {
             SET_QC();
@@ -895,6 +953,30 @@ uint32_t HELPER(neon_mul_p8)(uint32_t op1, uint32_t op2)
     return result;
 }
 
+uint64_t HELPER(neon_mull_p8)(uint32_t op1, uint32_t op2)
+{
+    int i;
+    uint64_t result = 0;
+    uint8_t e1;
+    uint16_t e2, r;
+#define MULP8(n) \
+    e1 = (op1 >> n) & 0xff; \
+    e2 = (op2 >> n) & 0xff; \
+    for (i = 0, r = 0; e1; i++, e1 >>= 1) { \
+        if (e1 & 1) { \
+            r ^= e2 << i; \
+        } \
+    } \
+    result |= (uint64_t)r << (n * 2);
+
+    MULP8(0);
+    MULP8(8);
+    MULP8(16);
+    MULP8(24);
+#undef MULP8
+    return result;
+}
+
 #define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0
 NEON_VOP(tst_u8, neon_u8, 4)
 NEON_VOP(tst_u16, neon_u16, 2)
@@ -1053,6 +1135,33 @@ uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x)
     return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
 }
 
+uint32_t HELPER(neon_unarrow_sat8)(CPUState *env, uint64_t x)
+{
+    uint16_t s;
+    uint8_t d;
+    uint32_t res = 0;
+#define SAT8(n) \
+    s = x >> n; \
+    if (s & 0x8000) { \
+        SET_QC(); \
+    } else { \
+        if (s > 0xff) { \
+            d = 0xff; \
+            SET_QC(); \
+        } else  { \
+            d = s; \
+        } \
+        res |= (uint32_t)d << (n / 2); \
+    }
+    
+    SAT8(0);
+    SAT8(16);
+    SAT8(32);
+    SAT8(48);
+#undef SAT8
+    return res;
+}
+
 uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x)
 {
     uint16_t s;
@@ -1099,6 +1208,29 @@ uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x)
     return res;
 }
 
+uint32_t HELPER(neon_unarrow_sat16)(CPUState *env, uint64_t x)
+{
+    uint32_t high;
+    uint32_t low;
+    low = x;
+    if (low & 0x80000000) {
+        low = 0;
+        SET_QC();
+    } else if (low > 0xffff) {
+        low = 0xffff;
+        SET_QC();
+    }
+    high = x >> 32;
+    if (high & 0x80000000) {
+        high = 0;
+        SET_QC();
+    } else if (high > 0xffff) {
+        high = 0xffff;
+        SET_QC();
+    }
+    return low | (high << 16);
+}
+
 uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x)
 {
     uint32_t high;
@@ -1133,6 +1265,19 @@ uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x)
     return (uint16_t)low | (high << 16);
 }
 
+uint32_t HELPER(neon_unarrow_sat32)(CPUState *env, uint64_t x)
+{
+    if (x & 0x8000000000000000ull) {
+        SET_QC();
+        return 0;
+    }
+    if (x > 0xffffffffu) {
+        SET_QC();
+        return 0xffffffffu;
+    }
+    return x;
+}
+
 uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
 {
     if (x > 0xffffffffu) {
@@ -1144,9 +1289,13 @@ uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
 
 uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x)
 {
-    if ((int64_t)x != (int32_t)x) {
+    if ((int64_t)x < -2147483648ll) {
+        SET_QC();
+        return 0x80000000;
+    }
+    if ((int64_t)x > 2147483647ll) {
         SET_QC();
-        return (x >> 63) ^ 0x7fffffff;
+        return 0x7fffffff;
     }
     return x;
 }
@@ -1421,7 +1570,6 @@ uint64_t HELPER(neon_negl_u16)(uint64_t x)
     return result;
 }
 
-#include <stdio.h>
 uint64_t HELPER(neon_negl_u32)(uint64_t x)
 {
     uint32_t low = -x;
@@ -1601,3 +1749,169 @@ uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
     float32 f1 = float32_abs(vfp_itos(b));
     return (float32_compare_quiet(f0, f1, NFS) > 0) ? ~0 : 0;
 }
+
+#define ELEM(V, N, SIZE) (uint64_t)(((uint64_t)(V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1))
+
+void HELPER(neon_unzip)(CPUState *env, uint32_t insn)
+{
+    int rd = ((insn >> 18) & 0x10) | ((insn >> 12) & 0x0f);
+    int rm = ((insn >> 1) & 0x10) | (insn & 0x0f);
+    int size = (insn >> 18) & 3;
+    if (insn & 0x40) { /* Q */
+        uint64_t zm0 = float64_val(env->vfp.regs[rm]);
+        uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
+        uint64_t zd0 = float64_val(env->vfp.regs[rd]);
+        uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]);
+        uint64_t m0 = 0, m1 = 0, d0 = 0, d1 = 0;
+        switch (size) {
+            case 0:
+                d0 = ELEM(zd0, 0, 8) | (ELEM(zd0, 2, 8) << 8)
+                     | (ELEM(zd0, 4, 8) << 16) | (ELEM(zd0, 6, 8) << 24)
+                     | (ELEM(zd1, 0, 8) << 32) | (ELEM(zd1, 2, 8) << 40)
+                     | (ELEM(zd1, 4, 8) << 48) | (ELEM(zd1, 6, 8) << 56);
+                d1 = ELEM(zm0, 0, 8) | (ELEM(zm0, 2, 8) << 8)
+                     | (ELEM(zm0, 4, 8) << 16) | (ELEM(zm0, 6, 8) << 24)
+                     | (ELEM(zm1, 0, 8) << 32) | (ELEM(zm1, 2, 8) << 40)
+                     | (ELEM(zm1, 4, 8) << 48) | (ELEM(zm1, 6, 8) << 56);
+                m0 = ELEM(zd0, 1, 8) | (ELEM(zd0, 3, 8) << 8)
+                     | (ELEM(zd0, 5, 8) << 16) | (ELEM(zd0, 7, 8) << 24)
+                     | (ELEM(zd1, 1, 8) << 32) | (ELEM(zd1, 3, 8) << 40)
+                     | (ELEM(zd1, 5, 8) << 48) | (ELEM(zd1, 7, 8) << 56);
+                m1 = ELEM(zm0, 1, 8) | (ELEM(zm0, 3, 8) << 8)
+                     | (ELEM(zm0, 5, 8) << 16) | (ELEM(zm0, 7, 8) << 24)
+                     | (ELEM(zm1, 1, 8) << 32) | (ELEM(zm1, 3, 8) << 40)
+                     | (ELEM(zm1, 5, 8) << 48) | (ELEM(zm1, 7, 8) << 56);
+                break;
+            case 1:
+                d0 = ELEM(zd0, 0, 16) | (ELEM(zd0, 2, 16) << 16)
+                     | (ELEM(zd1, 0, 16) << 32) | (ELEM(zd1, 2, 16) << 48);
+                d1 = ELEM(zm0, 0, 16) | (ELEM(zm0, 2, 16) << 16)
+                     | (ELEM(zm1, 0, 16) << 32) | (ELEM(zm1, 2, 16) << 48);
+                m0 = ELEM(zd0, 1, 16) | (ELEM(zd0, 3, 16) << 16)
+                     | (ELEM(zd1, 1, 16) << 32) | (ELEM(zd1, 3, 16) << 48);
+                m1 = ELEM(zm0, 1, 16) | (ELEM(zm0, 3, 16) << 16)
+                     | (ELEM(zm1, 1, 16) << 32) | (ELEM(zm1, 3, 16) << 48);
+                break;
+            case 2:
+                d0 = ELEM(zd0, 0, 32) | (ELEM(zd1, 0, 32) << 32);
+                d1 = ELEM(zm0, 0, 32) | (ELEM(zm1, 0, 32) << 32);
+                m0 = ELEM(zd0, 1, 32) | (ELEM(zd1, 1, 32) << 32);
+                m1 = ELEM(zm0, 1, 32) | (ELEM(zm1, 1, 32) << 32);
+                break;
+            default:
+                break;
+        }
+        env->vfp.regs[rm] = make_float64(m0);
+        env->vfp.regs[rm + 1] = make_float64(m1);
+        env->vfp.regs[rd] = make_float64(d0);
+        env->vfp.regs[rd + 1] = make_float64(d1);
+    } else {
+        uint64_t zm = float64_val(env->vfp.regs[rm]);
+        uint64_t zd = float64_val(env->vfp.regs[rd]);
+        uint64_t m = 0, d = 0;
+        switch (size) {
+            case 0:
+                d = ELEM(zd, 0, 8) | (ELEM(zd, 2, 8) << 8)
+                    | (ELEM(zd, 4, 8) << 16) | (ELEM(zd, 6, 8) << 24)
+                    | (ELEM(zm, 0, 8) << 32) | (ELEM(zm, 2, 8) << 40)
+                    | (ELEM(zm, 4, 8) << 48) | (ELEM(zm, 6, 8) << 56);
+                m = ELEM(zd, 1, 8) | (ELEM(zd, 3, 8) << 8)
+                    | (ELEM(zd, 5, 8) << 16) | (ELEM(zd, 7, 8) << 24)
+                    | (ELEM(zm, 1, 8) << 32) | (ELEM(zm, 3, 8) << 40)
+                    | (ELEM(zm, 5, 8) << 48) | (ELEM(zm, 7, 8) << 56);
+                break;
+            case 1:
+                d = ELEM(zd, 0, 16) | (ELEM(zd, 2, 16) << 16)
+                    | (ELEM(zm, 0, 16) << 32) | (ELEM(zm, 2, 16) << 48);
+                m = ELEM(zd, 1, 16) | (ELEM(zd, 3, 16) << 16)
+                    | (ELEM(zm, 1, 16) << 32) | (ELEM(zm, 3, 16) << 48);
+                break;
+            default:
+                /* size == 2 is a no-op for doubleword vectors */
+                break;
+        }
+        env->vfp.regs[rm] = make_float64(m);
+        env->vfp.regs[rd] = make_float64(d);
+    }
+}
+
+void HELPER(neon_zip)(CPUState *env, uint32_t insn)
+{
+    int rd = ((insn >> 18) & 0x10) | ((insn >> 12) & 0x0f);
+    int rm = ((insn >> 1) & 0x10) | (insn & 0x0f);
+    int size = (insn >> 18) & 3;
+    if (insn & 0x40) { /* Q */
+        uint64_t zm0 = float64_val(env->vfp.regs[rm]);
+        uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
+        uint64_t zd0 = float64_val(env->vfp.regs[rd]);
+        uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]);
+        uint64_t m0 = 0, m1 = 0, d0 = 0, d1 = 0;
+        switch (size) {
+            case 0:
+                d0 = ELEM(zd0, 0, 8) | (ELEM(zm0, 0, 8) << 8)
+                     | (ELEM(zd0, 1, 8) << 16) | (ELEM(zm0, 1, 8) << 24)
+                     | (ELEM(zd0, 2, 8) << 32) | (ELEM(zm0, 2, 8) << 40)
+                     | (ELEM(zd0, 3, 8) << 48) | (ELEM(zm0, 3, 8) << 56);
+                d1 = ELEM(zd0, 4, 8) | (ELEM(zm0, 4, 8) << 8)
+                     | (ELEM(zd0, 5, 8) << 16) | (ELEM(zm0, 5, 8) << 24)
+                     | (ELEM(zd0, 6, 8) << 32) | (ELEM(zm0, 6, 8) << 40)
+                     | (ELEM(zd0, 7, 8) << 48) | (ELEM(zm0, 7, 8) << 56);
+                m0 = ELEM(zd1, 0, 8) | (ELEM(zm1, 0, 8) << 8)
+                     | (ELEM(zd1, 1, 8) << 16) | (ELEM(zm1, 1, 8) << 24)
+                     | (ELEM(zd1, 2, 8) << 32) | (ELEM(zm1, 2, 8) << 40)
+                     | (ELEM(zd1, 3, 8) << 48) | (ELEM(zm1, 3, 8) << 56);
+                m1 = ELEM(zd1, 4, 8) | (ELEM(zm1, 4, 8) << 8)
+                     | (ELEM(zd1, 5, 8) << 16) | (ELEM(zm1, 5, 8) << 24)
+                     | (ELEM(zd1, 6, 8) << 32) | (ELEM(zm1, 6, 8) << 40)
+                     | (ELEM(zd1, 7, 8) << 48) | (ELEM(zm1, 7, 8) << 56);
+                break;
+            case 1:
+                d0 = ELEM(zd0, 0, 16) | (ELEM(zm0, 0, 16) << 16)
+                     | (ELEM(zd0, 1, 16) << 32) | (ELEM(zm0, 1, 16) << 48);
+                d1 = ELEM(zd0, 2, 16) | (ELEM(zm0, 2, 16) << 16)
+                     | (ELEM(zd0, 3, 16) << 32) | (ELEM(zm0, 3, 16) << 48);
+                m0 = ELEM(zd1, 0, 16) | (ELEM(zm1, 0, 16) << 16)
+                     | (ELEM(zd1, 1, 16) << 32) | (ELEM(zm1, 1, 16) << 48);
+                m1 = ELEM(zd1, 2, 16) | (ELEM(zm1, 2, 16) << 16)
+                     | (ELEM(zd1, 3, 16) << 32) | (ELEM(zm1, 3, 16) << 48);
+                break;
+            case 2:
+                d0 = ELEM(zd0, 0, 32) | (ELEM(zm0, 0, 32) << 32);
+                d1 = ELEM(zd0, 1, 32) | (ELEM(zm0, 1, 32) << 32);
+                m0 = ELEM(zd1, 0, 32) | (ELEM(zm1, 0, 32) << 32);
+                m1 = ELEM(zd1, 1, 32) | (ELEM(zm1, 1, 32) << 32);
+                break;
+        }
+        env->vfp.regs[rm] = make_float64(m0);
+        env->vfp.regs[rm + 1] = make_float64(m1);
+        env->vfp.regs[rd] = make_float64(d0);
+        env->vfp.regs[rd + 1] = make_float64(d1);
+    } else {
+        uint64_t zm = float64_val(env->vfp.regs[rm]);
+        uint64_t zd = float64_val(env->vfp.regs[rd]);
+        uint64_t m = 0, d = 0;
+        switch (size) {
+            case 0:
+                d = ELEM(zd, 0, 8) | (ELEM(zm, 0, 8) << 8)
+                    | (ELEM(zd, 1, 8) << 16) | (ELEM(zm, 1, 8) << 24)
+                    | (ELEM(zd, 2, 8) << 32) | (ELEM(zm, 2, 8) << 40)
+                    | (ELEM(zd, 3, 8) << 48) | (ELEM(zm, 3, 8) << 56);
+                m = ELEM(zd, 4, 8) | (ELEM(zm, 4, 8) << 8)
+                    | (ELEM(zd, 5, 8) << 16) | (ELEM(zm, 5, 8) << 24)
+                    | (ELEM(zd, 6, 8) << 32) | (ELEM(zm, 6, 8) << 40)
+                    | (ELEM(zd, 7, 8) << 48) | (ELEM(zm, 7, 8) << 56);
+                break;
+            case 1:
+                d = ELEM(zd, 0, 16) | (ELEM(zm, 0, 16) << 16)
+                    | (ELEM(zd, 1, 16) << 32) | (ELEM(zm, 1, 16) << 48);
+                m = ELEM(zd, 2, 16) | (ELEM(zm, 2, 16) << 16)
+                    | (ELEM(zd, 3, 16) << 32) | (ELEM(zm, 3, 16) << 48);
+                break;
+            default:
+                /* size == 2 is a no-op for doubleword vectors */
+                break;
+        }
+        env->vfp.regs[rm] = make_float64(m);
+        env->vfp.regs[rd] = make_float64(d);
+    }
+}
index 3de2610..954fdc3 100644 (file)
@@ -424,3 +424,176 @@ uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
         return ((uint32_t)x >> shift) | (x << (32 - shift));
     }
 }
+
+uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (res < src1) {
+        env->QF = 1;
+        res = ~(uint64_t)0;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 - src2;
+    if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    if (src1 < src2) {
+        env->QF = 1;
+        res = 0;
+    } else {
+        res = src1 - src2;
+    }
+    return res;
+}
+
+void HELPER(neon_vldst_all)(CPUState *env, uint32_t insn)
+{
+#if defined(CONFIG_USER_ONLY)
+#define LDB(addr) ldub(addr)
+#define LDW(addr) lduw(addr)
+#define LDL(addr) ldl(addr)
+#define LDQ(addr) ldq(addr)
+#define STB(addr, val) stb(addr, val)
+#define STW(addr, val) stw(addr, val)
+#define STL(addr, val) stl(addr, val)
+#define STQ(addr, val) stq(addr, val)
+#else
+    int user = cpu_mmu_index(env);
+#define LDB(addr) slow_ldb_mmu(addr, user, GETPC())
+#define LDW(addr) slow_ldw_mmu(addr, user, GETPC())
+#define LDL(addr) slow_ldl_mmu(addr, user, GETPC())
+#define LDQ(addr) slow_ldq_mmu(addr, user, GETPC())
+#define STB(addr, val) slow_stb_mmu(addr, val, user, GETPC())
+#define STW(addr, val) slow_stw_mmu(addr, val, user, GETPC())
+#define STL(addr, val) slow_stl_mmu(addr, val, user, GETPC())
+#define STQ(addr, val) slow_stq_mmu(addr, val, user, GETPC())
+#endif
+    static const struct {
+        int nregs;
+        int interleave;
+        int spacing;
+    } neon_ls_element_type[11] = {
+        {4, 4, 1},
+        {4, 4, 2},
+        {4, 1, 1},
+        {4, 2, 1},
+        {3, 3, 1},
+        {3, 3, 2},
+        {3, 1, 1},
+        {1, 1, 1},
+        {2, 2, 1},
+        {2, 2, 2},
+        {2, 1, 1}
+    };
+    
+    const int op = (insn >> 8) & 0xf;
+    const int size = (insn >> 6) & 3;
+    int rd = ((insn >> 12) & 0x0f) | ((insn >> 18) & 0x10);
+    const int rn = (insn >> 16) & 0xf;
+    const int load = (insn & (1 << 21)) != 0;
+    const int nregs = neon_ls_element_type[op].nregs;
+    const int interleave = neon_ls_element_type[op].interleave;
+    const int spacing = neon_ls_element_type[op].spacing;
+    uint32_t addr = env->regs[rn];
+    const int stride = (1 << size) * interleave;
+    int i, reg;
+    uint64_t tmp64;
+    
+    for (reg = 0; reg < nregs; reg++) {
+        if (interleave > 2 || (interleave == 2 && nregs == 2)) {
+            addr = env->regs[rn] + (1 << size) * reg;
+        } else if (interleave == 2 && nregs == 4 && reg == 2) {
+            addr = env->regs[rn] + (1 << size);
+        }
+        switch (size) {
+            case 3:
+                if (load) {
+                    env->vfp.regs[rd] = make_float64(LDQ(addr));
+                } else {
+                    STQ(addr, float64_val(env->vfp.regs[rd]));
+                }
+                addr += stride;
+                break;
+            case 2:
+                if (load) {
+                    tmp64 = (uint32_t)LDL(addr);
+                    addr += stride;
+                    tmp64 |= (uint64_t)LDL(addr) << 32;
+                    addr += stride;
+                    env->vfp.regs[rd] = make_float64(tmp64);
+                } else {
+                    tmp64 = float64_val(env->vfp.regs[rd]);
+                    STL(addr, tmp64);
+                    addr += stride;
+                    STL(addr, tmp64 >> 32);
+                    addr += stride;
+                }
+                break;
+            case 1:
+                if (load) {
+                    tmp64 = 0ull;
+                    for (i = 0; i < 4; i++, addr += stride) {
+                        tmp64 |= (uint64_t)LDW(addr) << (i * 16);
+                    }
+                    env->vfp.regs[rd] = make_float64(tmp64);
+                } else {
+                    tmp64 = float64_val(env->vfp.regs[rd]);
+                    for (i = 0; i < 4; i++, addr += stride, tmp64 >>= 16) {
+                        STW(addr, tmp64);
+                    }
+                }
+                break;
+            case 0:
+                if (load) {
+                    tmp64 = 0ull;
+                    for (i = 0; i < 8; i++, addr += stride) {
+                        tmp64 |= (uint64_t)LDB(addr) << (i * 8);
+                    }
+                    env->vfp.regs[rd] = make_float64(tmp64);
+                } else {
+                    tmp64 = float64_val(env->vfp.regs[rd]);
+                    for (i = 0; i < 8; i++, addr += stride, tmp64 >>= 8) {
+                        STB(addr, tmp64);
+                    }
+                }
+                break;
+        }
+        rd += spacing;
+    }
+#undef LDB
+#undef LDW
+#undef LDL
+#undef LDQ
+#undef STB
+#undef STW
+#undef STL
+#undef STQ
+}
diff --git a/target-arm/opengl_dummy.c b/target-arm/opengl_dummy.c
new file mode 100644 (file)
index 0000000..57e4336
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "debug_ch.h"
+
+MULTI_DEBUG_CHANNEL(qemu, arm_dummy);
+
+void *init_opengl_server(void *arg);
+
+void *init_opengl_server(void *arg){
+    ERR("init_open_gl_server(arm_dummy) called!!!\n");
+    return 0;
+}
+
+#ifndef _WIN32
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+void opengl_exec_set_parent_window(Display* _dpy, Window _parent_window);
+void opengl_exec_set_parent_window(Display* _dpy, Window _parent_window)
+{
+    ERR("opengl_exec_set_parent_window(arm_dummy) called!!!\n");
+}
+#endif
index 5149543..fa20663 100644 (file)
@@ -34,6 +34,8 @@
 #define GEN_HELPER 1
 #include "helpers.h"
 
+//#define RESOURCE_LEAK_DEBUG
+
 #define ENABLE_ARCH_5J    0
 #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
 #define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
@@ -76,6 +78,7 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
    conditional executions state has been updated.  */
 #define DISAS_WFI 4
 #define DISAS_SWI 5
+#define DISAS_SMC 6
 
 static TCGv_ptr cpu_env;
 /* We reuse the same 64-bit temporaries for efficiency.  */
@@ -128,22 +131,97 @@ void arm_translate_init(void)
 #include "helpers.h"
 }
 
+#ifdef RESOURCE_LEAK_DEBUG
 static int num_temps;
 
 /* Allocate a temporary variable.  */
-static TCGv_i32 new_tmp(void)
+static inline TCGv_i32 new_tmp(void)
 {
     num_temps++;
     return tcg_temp_new_i32();
 }
 
+static inline TCGv_i32 new_tmplocal32(void)
+{
+    num_temps++;
+    return tcg_temp_local_new_i32();
+}
+
+static inline TCGv new_tmplocal(void)
+{
+    num_temps++;
+    return tcg_temp_local_new();
+}
+
+static inline TCGv_i64 new_tmp64(void)
+{
+    num_temps++;
+    return tcg_temp_new_i64();
+}
+
+static inline TCGv_ptr new_tmpptr(void)
+{
+    num_temps++;
+    return tcg_temp_new_ptr();
+}
+
+static inline TCGv_i32 new_const(uint32_t value)
+{
+    num_temps++;
+    return tcg_const_i32(value);
+}
+
+static inline TCGv_i64 new_const64(uint64_t value)
+{
+    num_temps++;
+    return tcg_const_i64(value);
+}
+
 /* Release a temporary variable.  */
-static void dead_tmp(TCGv tmp)
+static inline void dead_tmp(TCGv tmp)
+{
+    tcg_temp_free_i32(tmp);
+    num_temps--;
+}
+
+static inline void dead_tmp64(TCGv_i64 tmp)
+{
+    tcg_temp_free_i64(tmp);
+    num_temps--;
+}
+
+static inline void dead_tmp_(TCGv tmp)
 {
     tcg_temp_free(tmp);
     num_temps--;
 }
 
+static inline void dead_tmpptr(TCGv_ptr tmp)
+{
+    tcg_temp_free_ptr(tmp);
+    num_temps--;
+}
+
+#undef tcg_temp_local_new
+#undef tcg_temp_new_ptr
+#undef tcg_temp_free
+#undef tcg_temp_free_ptr
+#define tcg_temp_new_i32() new_tmp()
+#define tcg_temp_new_i64() new_tmp64()
+#define tcg_temp_local_new() new_tmplocal()
+#define tcg_temp_local_new_i32() new_tmplocal32()
+#define tcg_temp_new_ptr() new_tmpptr()
+#define tcg_const_i32(x) new_const(x)
+#define tcg_const_i64(x) new_const64(x)
+#define tcg_temp_free(x) dead_tmp_(x)
+#define tcg_temp_free_i32(x) dead_tmp(x)
+#define tcg_temp_free_i64(x) dead_tmp64(x)
+#define tcg_temp_free_ptr(x) dead_tmpptr(x)
+#else // RESOURCE_LEAK_DEBUG
+#define new_tmp() tcg_temp_new_i32()
+#define dead_tmp(x) tcg_temp_free_i32(x)
+#endif // RESOOURCE_LEAK_DEBUG
+
 static inline TCGv load_cpu_offset(int offset)
 {
     TCGv tmp = new_tmp();
@@ -766,6 +844,12 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
     }
 }
 
+static inline void gen_smc(CPUState *env, DisasContext *s)
+{
+    tcg_gen_movi_i32(cpu_R[15], s->pc);
+    s->is_jmp = DISAS_SMC;
+}
+
 static inline TCGv gen_ld8s(TCGv addr, int index)
 {
     TCGv tmp = new_tmp();
@@ -1699,8 +1783,8 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
             TCGV_UNUSED(tmp3);
         }
         gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3);
-        tcg_temp_free(tmp3);
-        tcg_temp_free(tmp2);
+        tcg_temp_free_i32(tmp3);
+        tcg_temp_free_i32(tmp2);
         dead_tmp(tmp);
         gen_op_iwmmxt_movq_wRn_M0(wrd);
         gen_op_iwmmxt_set_mup();
@@ -2159,7 +2243,7 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
         tmp = tcg_const_i32((insn >> 20) & 3);
         iwmmxt_load_reg(cpu_V1, rd1);
         gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp);
-        tcg_temp_free(tmp);
+        tcg_temp_free_i32(tmp);
         gen_op_iwmmxt_movq_wRn_M0(wrd);
         gen_op_iwmmxt_set_mup();
         break;
@@ -2215,7 +2299,7 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
         gen_op_iwmmxt_movq_M0_wRn(rd0);
         tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f));
         gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp);
-        tcg_temp_free(tmp);
+        tcg_temp_free_i32(tmp);
         gen_op_iwmmxt_movq_wRn_M0(wrd);
         gen_op_iwmmxt_set_mup();
         gen_op_iwmmxt_set_cup();
@@ -2428,7 +2512,7 @@ static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
         tmp = new_tmp();
         tmp2 = tcg_const_i32(insn);
         gen_helper_get_cp(tmp, cpu_env, tmp2);
-        tcg_temp_free(tmp2);
+        tcg_temp_free_i32(tmp2);
         store_reg(s, rd, tmp);
     } else {
         if (!env->cp[cp].cp_write)
@@ -2437,7 +2521,7 @@ static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
         tmp = load_reg(s, rd);
         tmp2 = tcg_const_i32(insn);
         gen_helper_set_cp(cpu_env, tmp2, tmp);
-        tcg_temp_free(tmp2);
+        tcg_temp_free_i32(tmp2);
         dead_tmp(tmp);
     }
     return 0;
@@ -3318,6 +3402,7 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     offset = 8;
                 else
                     offset = 4;
+                tmp = tcg_const_i32(offset);
                 for (i = 0; i < n; i++) {
                     if (insn & ARM_CP_RW_BIT) {
                         /* load */
@@ -3328,8 +3413,9 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
                         gen_mov_F0_vreg(dp, rd + i);
                         gen_vfp_st(s, dp, addr);
                     }
-                    tcg_gen_addi_i32(addr, addr, offset);
+                    tcg_gen_add_i32(addr, addr, tmp);
                 }
+                tcg_temp_free_i32(tmp);
                 if (insn & (1 << 21)) {
                     /* writeback */
                     if (insn & (1 << 24))
@@ -3495,6 +3581,15 @@ static void gen_exception_insn(DisasContext *s, int offset, int excp)
     s->is_jmp = DISAS_JUMP;
 }
 
+static inline void
+gen_save_condexec (DisasContext *s)
+{
+    uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1);
+    TCGv tmp = new_tmp();
+    tcg_gen_movi_i32(tmp, val);
+    store_cpu_field(tmp, actual_condexec_bits);
+}
+
 static void gen_nop_hint(DisasContext *s, int val)
 {
     switch (val) {
@@ -3614,116 +3709,6 @@ static inline TCGv neon_get_scalar(int size, int reg)
     return tmp;
 }
 
-static void gen_neon_unzip_u8(TCGv t0, TCGv t1)
-{
-    TCGv rd, rm, tmp;
-
-    rd = new_tmp();
-    rm = new_tmp();
-    tmp = new_tmp();
-
-    tcg_gen_andi_i32(rd, t0, 0xff);
-    tcg_gen_shri_i32(tmp, t0, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff00);
-    tcg_gen_or_i32(rd, rd, tmp);
-    tcg_gen_shli_i32(tmp, t1, 16);
-    tcg_gen_andi_i32(tmp, tmp, 0xff0000);
-    tcg_gen_or_i32(rd, rd, tmp);
-    tcg_gen_shli_i32(tmp, t1, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff000000);
-    tcg_gen_or_i32(rd, rd, tmp);
-
-    tcg_gen_shri_i32(rm, t0, 8);
-    tcg_gen_andi_i32(rm, rm, 0xff);
-    tcg_gen_shri_i32(tmp, t0, 16);
-    tcg_gen_andi_i32(tmp, tmp, 0xff00);
-    tcg_gen_or_i32(rm, rm, tmp);
-    tcg_gen_shli_i32(tmp, t1, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff0000);
-    tcg_gen_or_i32(rm, rm, tmp);
-    tcg_gen_andi_i32(tmp, t1, 0xff000000);
-    tcg_gen_or_i32(t1, rm, tmp);
-    tcg_gen_mov_i32(t0, rd);
-
-    dead_tmp(tmp);
-    dead_tmp(rm);
-    dead_tmp(rd);
-}
-
-static void gen_neon_zip_u8(TCGv t0, TCGv t1)
-{
-    TCGv rd, rm, tmp;
-
-    rd = new_tmp();
-    rm = new_tmp();
-    tmp = new_tmp();
-
-    tcg_gen_andi_i32(rd, t0, 0xff);
-    tcg_gen_shli_i32(tmp, t1, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff00);
-    tcg_gen_or_i32(rd, rd, tmp);
-    tcg_gen_shli_i32(tmp, t0, 16);
-    tcg_gen_andi_i32(tmp, tmp, 0xff0000);
-    tcg_gen_or_i32(rd, rd, tmp);
-    tcg_gen_shli_i32(tmp, t1, 24);
-    tcg_gen_andi_i32(tmp, tmp, 0xff000000);
-    tcg_gen_or_i32(rd, rd, tmp);
-
-    tcg_gen_andi_i32(rm, t1, 0xff000000);
-    tcg_gen_shri_i32(tmp, t0, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff0000);
-    tcg_gen_or_i32(rm, rm, tmp);
-    tcg_gen_shri_i32(tmp, t1, 8);
-    tcg_gen_andi_i32(tmp, tmp, 0xff00);
-    tcg_gen_or_i32(rm, rm, tmp);
-    tcg_gen_shri_i32(tmp, t0, 16);
-    tcg_gen_andi_i32(tmp, tmp, 0xff);
-    tcg_gen_or_i32(t1, rm, tmp);
-    tcg_gen_mov_i32(t0, rd);
-
-    dead_tmp(tmp);
-    dead_tmp(rm);
-    dead_tmp(rd);
-}
-
-static void gen_neon_zip_u16(TCGv t0, TCGv t1)
-{
-    TCGv tmp, tmp2;
-
-    tmp = new_tmp();
-    tmp2 = new_tmp();
-
-    tcg_gen_andi_i32(tmp, t0, 0xffff);
-    tcg_gen_shli_i32(tmp2, t1, 16);
-    tcg_gen_or_i32(tmp, tmp, tmp2);
-    tcg_gen_andi_i32(t1, t1, 0xffff0000);
-    tcg_gen_shri_i32(tmp2, t0, 16);
-    tcg_gen_or_i32(t1, t1, tmp2);
-    tcg_gen_mov_i32(t0, tmp);
-
-    dead_tmp(tmp2);
-    dead_tmp(tmp);
-}
-
-static void gen_neon_unzip(int reg, int q, int tmp, int size)
-{
-    int n;
-    TCGv t0, t1;
-
-    for (n = 0; n < q + 1; n += 2) {
-        t0 = neon_load_reg(reg, n);
-        t1 = neon_load_reg(reg, n + 1);
-        switch (size) {
-        case 0: gen_neon_unzip_u8(t0, t1); break;
-        case 1: gen_neon_zip_u16(t0, t1); break; /* zip and unzip are the same.  */
-        case 2: /* no-op */; break;
-        default: abort();
-        }
-        neon_store_scratch(tmp + n, t0);
-        neon_store_scratch(tmp + n + 1, t1);
-    }
-}
-
 static void gen_neon_trn_u8(TCGv t0, TCGv t1)
 {
     TCGv rd, tmp;
@@ -3765,7 +3750,6 @@ static void gen_neon_trn_u16(TCGv t0, TCGv t1)
     dead_tmp(rd);
 }
 
-
 static struct {
     int nregs;
     int interleave;
@@ -3799,11 +3783,9 @@ static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
     int pass;
     int load;
     int shift;
-    int n;
     TCGv addr;
     TCGv tmp;
     TCGv tmp2;
-    TCGv_i64 tmp64;
 
     if (!s->vfp_enabled)
       return 1;
@@ -3816,108 +3798,35 @@ static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
         /* Load store all elements.  */
         op = (insn >> 8) & 0xf;
         size = (insn >> 6) & 3;
-        if (op > 10)
+        if (op > 10) {
+            dead_tmp(addr);
             return 1;
+        }
         nregs = neon_ls_element_type[op].nregs;
         interleave = neon_ls_element_type[op].interleave;
         spacing = neon_ls_element_type[op].spacing;
-        if (size == 3 && (interleave | spacing) != 1)
+        if (size == 3 && (interleave | spacing) != 1) {
+            dead_tmp(addr);
             return 1;
-        load_reg_var(s, addr, rn);
-        stride = (1 << size) * interleave;
-        for (reg = 0; reg < nregs; reg++) {
-            if (interleave > 2 || (interleave == 2 && nregs == 2)) {
-                load_reg_var(s, addr, rn);
-                tcg_gen_addi_i32(addr, addr, (1 << size) * reg);
-            } else if (interleave == 2 && nregs == 4 && reg == 2) {
-                load_reg_var(s, addr, rn);
-                tcg_gen_addi_i32(addr, addr, 1 << size);
-            }
-            if (size == 3) {
-                if (load) {
-                    tmp64 = gen_ld64(addr, IS_USER(s));
-                    neon_store_reg64(tmp64, rd);
-                    tcg_temp_free_i64(tmp64);
-                } else {
-                    tmp64 = tcg_temp_new_i64();
-                    neon_load_reg64(tmp64, rd);
-                    gen_st64(tmp64, addr, IS_USER(s));
-                }
-                tcg_gen_addi_i32(addr, addr, stride);
-            } else {
-                for (pass = 0; pass < 2; pass++) {
-                    if (size == 2) {
-                        if (load) {
-                            tmp = gen_ld32(addr, IS_USER(s));
-                            neon_store_reg(rd, pass, tmp);
-                        } else {
-                            tmp = neon_load_reg(rd, pass);
-                            gen_st32(tmp, addr, IS_USER(s));
-                        }
-                        tcg_gen_addi_i32(addr, addr, stride);
-                    } else if (size == 1) {
-                        if (load) {
-                            tmp = gen_ld16u(addr, IS_USER(s));
-                            tcg_gen_addi_i32(addr, addr, stride);
-                            tmp2 = gen_ld16u(addr, IS_USER(s));
-                            tcg_gen_addi_i32(addr, addr, stride);
-                            tcg_gen_shli_i32(tmp2, tmp2, 16);
-                            tcg_gen_or_i32(tmp, tmp, tmp2);
-                            dead_tmp(tmp2);
-                            neon_store_reg(rd, pass, tmp);
-                        } else {
-                            tmp = neon_load_reg(rd, pass);
-                            tmp2 = new_tmp();
-                            tcg_gen_shri_i32(tmp2, tmp, 16);
-                            gen_st16(tmp, addr, IS_USER(s));
-                            tcg_gen_addi_i32(addr, addr, stride);
-                            gen_st16(tmp2, addr, IS_USER(s));
-                            tcg_gen_addi_i32(addr, addr, stride);
-                        }
-                    } else /* size == 0 */ {
-                        if (load) {
-                            TCGV_UNUSED(tmp2);
-                            for (n = 0; n < 4; n++) {
-                                tmp = gen_ld8u(addr, IS_USER(s));
-                                tcg_gen_addi_i32(addr, addr, stride);
-                                if (n == 0) {
-                                    tmp2 = tmp;
-                                } else {
-                                    tcg_gen_shli_i32(tmp, tmp, n * 8);
-                                    tcg_gen_or_i32(tmp2, tmp2, tmp);
-                                    dead_tmp(tmp);
-                                }
-                            }
-                            neon_store_reg(rd, pass, tmp2);
-                        } else {
-                            tmp2 = neon_load_reg(rd, pass);
-                            for (n = 0; n < 4; n++) {
-                                tmp = new_tmp();
-                                if (n == 0) {
-                                    tcg_gen_mov_i32(tmp, tmp2);
-                                } else {
-                                    tcg_gen_shri_i32(tmp, tmp2, n * 8);
-                                }
-                                gen_st8(tmp, addr, IS_USER(s));
-                                tcg_gen_addi_i32(addr, addr, stride);
-                            }
-                            dead_tmp(tmp2);
-                        }
-                    }
-                }
-            }
-            rd += spacing;
         }
+        tcg_gen_movi_i32(addr, insn);
+        gen_helper_neon_vldst_all(cpu_env, addr);
         stride = nregs * 8;
     } else {
         size = (insn >> 10) & 3;
         if (size == 3) {
             /* Load single element to all lanes.  */
-            if (!load)
+            if (!load) {
+                dead_tmp(addr);
                 return 1;
+            }
             size = (insn >> 6) & 3;
             nregs = ((insn >> 8) & 3) + 1;
-            stride = (insn & (1 << 5)) ? 2 : 1;
+            if (nregs == 1) {
+                stride = 0;
+            } else {
+                stride = (insn & (1 << 5)) ? 2 : 1;
+            }
             load_reg_var(s, addr, rn);
             for (reg = 0; reg < nregs; reg++) {
                 switch (size) {
@@ -3933,11 +3842,17 @@ static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     tmp = gen_ld32(addr, IS_USER(s));
                     break;
                 case 3:
+                    dead_tmp(addr);
                     return 1;
                 default: /* Avoid compiler warnings.  */
                     abort();
                 }
-                tcg_gen_addi_i32(addr, addr, 1 << size);
+                if (stride && reg < nregs - 1) {
+                    tcg_gen_addi_i32(addr, addr, 1 << size);
+                } else if (!stride) {
+                    tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 0));
+                    tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 1));
+                }
                 tmp2 = new_tmp();
                 tcg_gen_mov_i32(tmp2, tmp);
                 neon_store_reg(rd, 0, tmp2);
@@ -4065,6 +3980,16 @@ static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
     }
 }
 
+static inline void gen_neon_unarrow_sats(int size, TCGv dest, TCGv_i64 src)
+{
+    switch(size) {
+    case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break;
+    case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break;
+    case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break;
+    default: abort();
+    }
+}
+
 static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift,
                                          int q, int u)
 {
@@ -4085,8 +4010,8 @@ static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift,
     } else {
         if (u) {
             switch (size) {
-            case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
-            case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
+            case 1: gen_helper_neon_shl_u16(var, var, shift); break;
+            case 2: gen_helper_neon_shl_u32(var, var, shift); break;
             default: abort();
             }
         } else {
@@ -4207,8 +4132,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
     TCGv tmp, tmp2, tmp3, tmp4, tmp5;
     TCGv_i64 tmp64;
 
-    if (!s->vfp_enabled)
-      return 1;
+    if (!s->vfp_enabled) {
+        return 1;
+    }
     q = (insn & (1 << 6)) != 0;
     u = (insn >> 24) & 1;
     VFP_DREG_D(rd, insn);
@@ -4281,14 +4207,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     } else {
                         tcg_gen_add_i64(CPU_V001);
                     }
-                    break;
-                default:
-                    abort();
+                    neon_store_reg64(cpu_V0, rd + pass);
                 }
-                neon_store_reg64(cpu_V0, rd + pass);
+                return 0;
+            }
+            if (op != 3) {
+                return 1;
             }
-            return 0;
         }
+        pairwise = 0;
         switch (op) {
         case 8: /* VSHL */
         case 9: /* VQSHL */
@@ -4300,290 +4227,338 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 rtmp = rn;
                 rn = rm;
                 rm = rtmp;
-                pairwise = 0;
             }
             break;
+        case 19: /* VMUL */
+            if (u && size) {
+                return 1;
+            }
+            break;
+        case 23: /* VPADD */
+            if (u) {
+                return 1;
+            }
+            /* fall through */
         case 20: /* VPMAX */
         case 21: /* VPMIN */
-        case 23: /* VPADD */
             pairwise = 1;
             break;
-        case 26: /* VPADD (float) */
+        case 22: /* VQDMULH/VQRDMULH */
+            if (!size) {
+                return 1;
+            }
+            break;
+        case 26: /* VADD/VSUB/VPADD/VABD (float) */
             pairwise = (u && size < 2);
+            /* fall through */
+        case 27: /* VMLA/VMLS/VMUL (float) */
+            if (size & 1) {
+                return 1;
+            }
+            break;
+        case 28: /* VCEQ/VCGE/VCGT (float) */
+            if ((!u && size) || (size & 1)) {
+                return 1;
+            }
+            break;
+        case 29: /* VACGE/VACGT (float) */
+            if (!u || (size & 1)) {
+                return 1;
+            }
             break;
         case 30: /* VPMIN/VPMAX (float) */
             pairwise = u;
+            if (size & 1) {
+                return 1;
+            }
+            break;
+        case 31: /* VRECPS/VRSQRTS */
+            if (u || (size & 1)) {
+                return 1;
+            }
             break;
         default:
-            pairwise = 0;
             break;
         }
+        if (pairwise && q) {
+            return 1;
+        }
 
         for (pass = 0; pass < (q ? 4 : 2); pass++) {
-
-        if (pairwise) {
-            /* Pairwise.  */
-            if (q)
-                n = (pass & 1) * 2;
-            else
-                n = 0;
-            if (pass < q + 1) {
-                tmp = neon_load_reg(rn, n);
-                tmp2 = neon_load_reg(rn, n + 1);
+            if (pairwise) {
+                /* Pairwise.  */
+                if (!pass) {
+                    tmp = neon_load_reg(rn, 0);
+                    tmp2 = neon_load_reg(rn, 1);
+                } else {
+                    tmp = neon_load_reg(rm, 0);
+                    tmp2 = neon_load_reg(rm, 1);
+                }
             } else {
-                tmp = neon_load_reg(rm, n);
-                tmp2 = neon_load_reg(rm, n + 1);
+                /* Elementwise.  */
+                tmp = neon_load_reg(rn, pass);
+                if (rn == rm) {
+                    tmp2 = new_tmp();
+                    tcg_gen_mov_i32(tmp2, tmp);
+                } else {
+                    tmp2 = neon_load_reg(rm, pass);
+                }
             }
-        } else {
-            /* Elementwise.  */
-            tmp = neon_load_reg(rn, pass);
-            tmp2 = neon_load_reg(rm, pass);
-        }
-        switch (op) {
-        case 0: /* VHADD */
-            GEN_NEON_INTEGER_OP(hadd);
-            break;
-        case 1: /* VQADD */
-            GEN_NEON_INTEGER_OP_ENV(qadd);
-            break;
-        case 2: /* VRHADD */
-            GEN_NEON_INTEGER_OP(rhadd);
-            break;
-        case 3: /* Logic ops.  */
-            switch ((u << 2) | size) {
-            case 0: /* VAND */
-                tcg_gen_and_i32(tmp, tmp, tmp2);
+            switch (op) {
+            case 0: /* VHADD */
+                GEN_NEON_INTEGER_OP(hadd);
                 break;
-            case 1: /* BIC */
-                tcg_gen_andc_i32(tmp, tmp, tmp2);
+            case 1: /* VQADD */
+                GEN_NEON_INTEGER_OP_ENV(qadd);
                 break;
-            case 2: /* VORR */
-                tcg_gen_or_i32(tmp, tmp, tmp2);
+            case 2: /* VRHADD */
+                GEN_NEON_INTEGER_OP(rhadd);
                 break;
-            case 3: /* VORN */
-                tcg_gen_orc_i32(tmp, tmp, tmp2);
+            case 3: /* Logic ops.  */
+                switch ((u << 2) | size) {
+                case 0: /* VAND */
+                    tcg_gen_and_i32(tmp, tmp, tmp2);
+                    break;
+                case 1: /* VBIC */
+                    tcg_gen_andc_i32(tmp, tmp, tmp2);
+                    break;
+                case 2: /* VORR, VMOV */
+                    tcg_gen_or_i32(tmp, tmp, tmp2);
+                    break;
+                case 3: /* VORN */
+                    tcg_gen_orc_i32(tmp, tmp, tmp2);
+                    break;
+                case 4: /* VEOR */
+                    tcg_gen_xor_i32(tmp, tmp, tmp2);
+                    break;
+                case 5: /* VBSL */
+                    tmp3 = neon_load_reg(rd, pass);
+                    gen_neon_bsl(tmp, tmp, tmp2, tmp3);
+                    dead_tmp(tmp3);
+                    break;
+                case 6: /* VBIT */
+                    tmp3 = neon_load_reg(rd, pass);
+                    gen_neon_bsl(tmp, tmp, tmp3, tmp2);
+                    dead_tmp(tmp3);
+                    break;
+                case 7: /* VBIF */
+                    tmp3 = neon_load_reg(rd, pass);
+                    gen_neon_bsl(tmp, tmp3, tmp, tmp2);
+                    dead_tmp(tmp3);
+                    break;
+                }
                 break;
-            case 4: /* VEOR */
-                tcg_gen_xor_i32(tmp, tmp, tmp2);
+            case 4: /* VHSUB */
+                GEN_NEON_INTEGER_OP(hsub);
                 break;
-            case 5: /* VBSL */
-                tmp3 = neon_load_reg(rd, pass);
-                gen_neon_bsl(tmp, tmp, tmp2, tmp3);
-                dead_tmp(tmp3);
+            case 5: /* VQSUB */
+                GEN_NEON_INTEGER_OP_ENV(qsub);
                 break;
-            case 6: /* VBIT */
-                tmp3 = neon_load_reg(rd, pass);
-                gen_neon_bsl(tmp, tmp, tmp3, tmp2);
-                dead_tmp(tmp3);
+            case 6: /* VCGT */
+                GEN_NEON_INTEGER_OP(cgt);
                 break;
-            case 7: /* VBIF */
-                tmp3 = neon_load_reg(rd, pass);
-                gen_neon_bsl(tmp, tmp3, tmp, tmp2);
-                dead_tmp(tmp3);
+            case 7: /* VCGE */
+                GEN_NEON_INTEGER_OP(cge);
                 break;
-            }
-            break;
-        case 4: /* VHSUB */
-            GEN_NEON_INTEGER_OP(hsub);
-            break;
-        case 5: /* VQSUB */
-            GEN_NEON_INTEGER_OP_ENV(qsub);
-            break;
-        case 6: /* VCGT */
-            GEN_NEON_INTEGER_OP(cgt);
-            break;
-        case 7: /* VCGE */
-            GEN_NEON_INTEGER_OP(cge);
-            break;
-        case 8: /* VSHL */
-            GEN_NEON_INTEGER_OP(shl);
-            break;
-        case 9: /* VQSHL */
-            GEN_NEON_INTEGER_OP_ENV(qshl);
-            break;
-        case 10: /* VRSHL */
-            GEN_NEON_INTEGER_OP(rshl);
-            break;
-        case 11: /* VQRSHL */
-            GEN_NEON_INTEGER_OP_ENV(qrshl);
-            break;
-        case 12: /* VMAX */
-            GEN_NEON_INTEGER_OP(max);
-            break;
-        case 13: /* VMIN */
-            GEN_NEON_INTEGER_OP(min);
-            break;
-        case 14: /* VABD */
-            GEN_NEON_INTEGER_OP(abd);
-            break;
-        case 15: /* VABA */
-            GEN_NEON_INTEGER_OP(abd);
-            dead_tmp(tmp2);
-            tmp2 = neon_load_reg(rd, pass);
-            gen_neon_add(size, tmp, tmp2);
-            break;
-        case 16:
-            if (!u) { /* VADD */
-                if (gen_neon_add(size, tmp, tmp2))
-                    return 1;
-            } else { /* VSUB */
-                switch (size) {
-                case 0: gen_helper_neon_sub_u8(tmp, tmp, tmp2); break;
-                case 1: gen_helper_neon_sub_u16(tmp, tmp, tmp2); break;
-                case 2: tcg_gen_sub_i32(tmp, tmp, tmp2); break;
-                default: return 1;
-                }
-            }
-            break;
-        case 17:
-            if (!u) { /* VTST */
-                switch (size) {
-                case 0: gen_helper_neon_tst_u8(tmp, tmp, tmp2); break;
-                case 1: gen_helper_neon_tst_u16(tmp, tmp, tmp2); break;
-                case 2: gen_helper_neon_tst_u32(tmp, tmp, tmp2); break;
-                default: return 1;
+            case 8: /* VSHL */
+                GEN_NEON_INTEGER_OP(shl);
+                break;
+            case 9: /* VQSHL */
+                GEN_NEON_INTEGER_OP_ENV(qshl);
+                break;
+            case 10: /* VRSHL */
+                GEN_NEON_INTEGER_OP(rshl);
+                break;
+            case 11: /* VQRSHL */
+                GEN_NEON_INTEGER_OP_ENV(qrshl);
+                break;
+            case 12: /* VMAX */
+                GEN_NEON_INTEGER_OP(max);
+                break;
+            case 13: /* VMIN */
+                GEN_NEON_INTEGER_OP(min);
+                break;
+            case 14: /* VABD */
+                GEN_NEON_INTEGER_OP(abd);
+                break;
+            case 15: /* VABA */
+                GEN_NEON_INTEGER_OP(abd);
+                dead_tmp(tmp2);
+                tmp2 = neon_load_reg(rd, pass);
+                gen_neon_add(size, tmp, tmp2);
+                break;
+            case 16:
+                if (!u) { /* VADD */
+                    if (gen_neon_add(size, tmp, tmp2)) {
+                        abort(); /* size == 3 is handled earlier */
+                    }
+                } else { /* VSUB */
+                    switch (size) {
+                    case 0: gen_helper_neon_sub_u8(tmp, tmp, tmp2); break;
+                    case 1: gen_helper_neon_sub_u16(tmp, tmp, tmp2); break;
+                    case 2: tcg_gen_sub_i32(tmp, tmp, tmp2); break;
+                    default: abort(); /* size == 3 is handled earlier */
+                    }
                 }
-            } else { /* VCEQ */
-                switch (size) {
-                case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
-                case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
-                case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
-                default: return 1;
+                break;
+            case 17:
+                if (!u) { /* VTST */
+                    switch (size) {
+                    case 0: gen_helper_neon_tst_u8(tmp, tmp, tmp2); break;
+                    case 1: gen_helper_neon_tst_u16(tmp, tmp, tmp2); break;
+                    case 2: gen_helper_neon_tst_u32(tmp, tmp, tmp2); break;
+                    default: abort(); /* size == 3 is handled earlier */
+                    }
+                } else { /* VCEQ */
+                    switch (size) {
+                    case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
+                    case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
+                    case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
+                    default: abort(); /* size == 3 is handled earlier */
+                    }
                 }
-            }
-            break;
-        case 18: /* Multiply.  */
-            switch (size) {
-            case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
-            case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
-            case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
-            default: return 1;
-            }
-            dead_tmp(tmp2);
-            tmp2 = neon_load_reg(rd, pass);
-            if (u) { /* VMLS */
-                gen_neon_rsb(size, tmp, tmp2);
-            } else { /* VMLA */
-                gen_neon_add(size, tmp, tmp2);
-            }
-            break;
-        case 19: /* VMUL */
-            if (u) { /* polynomial */
-                gen_helper_neon_mul_p8(tmp, tmp, tmp2);
-            } else { /* Integer */
+                break;
+            case 18: /* Multiply.  */
                 switch (size) {
                 case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
                 case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
                 case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
-                default: return 1;
+                default: abort(); /* size == 3 is handled earlier */
                 }
-            }
-            break;
-        case 20: /* VPMAX */
-            GEN_NEON_INTEGER_OP(pmax);
-            break;
-        case 21: /* VPMIN */
-            GEN_NEON_INTEGER_OP(pmin);
-            break;
-        case 22: /* Hultiply high.  */
-            if (!u) { /* VQDMULH */
-                switch (size) {
-                case 1: gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); break;
-                case 2: gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); break;
-                default: return 1;
+                dead_tmp(tmp2);
+                tmp2 = neon_load_reg(rd, pass);
+                if (u) { /* VMLS */
+                    gen_neon_rsb(size, tmp, tmp2);
+                } else { /* VMLA */
+                    gen_neon_add(size, tmp, tmp2);
+                }
+                break;
+            case 19: /* VMUL */
+                if (u) { /* polynomial */
+                    gen_helper_neon_mul_p8(tmp, tmp, tmp2);
+                } else { /* Integer */
+                    switch (size) {
+                    case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
+                    case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
+                    case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
+                    default: abort(); /* size == 3 is handled earlier */
+                    }
                 }
-            } else { /* VQRDHMUL */
+                break;
+            case 20: /* VPMAX */
+                GEN_NEON_INTEGER_OP(pmax);
+                break;
+            case 21: /* VPMIN */
+                GEN_NEON_INTEGER_OP(pmin);
+                break;
+            case 22: /* Multiply high.  */
+                if (!u) { /* VQDMULH */
+                    switch (size) {
+                    case 1:
+                        gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
+                        break;
+                    case 2:
+                        gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
+                        break;
+                    default:
+                        abort(); /* size == 0,3 is handled earlier */
+                    }
+                } else { /* VQRDHMUL */
+                    switch (size) {
+                    case 1:
+                        gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
+                        break;
+                    case 2:
+                        gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
+                        break;
+                    default:
+                        abort(); /* size == 0,3 is handled earlier */
+                    }
+                }
+                break;
+            case 23: /* VPADD */
                 switch (size) {
-                case 1: gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); break;
-                case 2: gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); break;
-                default: return 1;
+                case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break;
+                case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break;
+                case 2: tcg_gen_add_i32(tmp, tmp, tmp2); break;
+                default: abort(); /* size == 3 is handled earlier */
+                }
+                break;
+            case 26: /* Floating point arithmetic.  */
+                switch ((u << 2) | size) {
+                case 0: /* VADD */
+                    gen_helper_neon_add_f32(tmp, tmp, tmp2);
+                    break;
+                case 2: /* VSUB */
+                    gen_helper_neon_sub_f32(tmp, tmp, tmp2);
+                    break;
+                case 4: /* VPADD */
+                    gen_helper_neon_add_f32(tmp, tmp, tmp2);
+                    break;
+                case 6: /* VABD */
+                    gen_helper_neon_abd_f32(tmp, tmp, tmp2);
+                    break;
+                default:
+                    abort(); /* other values are handled earlier */
                 }
-            }
-            break;
-        case 23: /* VPADD */
-            if (u)
-                return 1;
-            switch (size) {
-            case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break;
-            case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break;
-            case 2: tcg_gen_add_i32(tmp, tmp, tmp2); break;
-            default: return 1;
-            }
-            break;
-        case 26: /* Floating point arithnetic.  */
-            switch ((u << 2) | size) {
-            case 0: /* VADD */
-                gen_helper_neon_add_f32(tmp, tmp, tmp2);
                 break;
-            case 2: /* VSUB */
-                gen_helper_neon_sub_f32(tmp, tmp, tmp2);
+            case 27: /* Float multiply.  */
+                gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+                if (!u) {
+                    dead_tmp(tmp2);
+                    tmp2 = neon_load_reg(rd, pass);
+                    if (size == 0) {
+                        gen_helper_neon_add_f32(tmp, tmp, tmp2);
+                    } else {
+                        gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+                    }
+                }
                 break;
-            case 4: /* VPADD */
-                gen_helper_neon_add_f32(tmp, tmp, tmp2);
+            case 28: /* Float compare.  */
+                if (!u) {
+                    gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+                } else {
+                    if (size == 0) {
+                        gen_helper_neon_cge_f32(tmp, tmp, tmp2);
+                    } else {
+                        gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+                    }
+                }
                 break;
-            case 6: /* VABD */
-                gen_helper_neon_abd_f32(tmp, tmp, tmp2);
+            case 29: /* Float compare absolute.  */
+                if (size == 0) {
+                    gen_helper_neon_acge_f32(tmp, tmp, tmp2);
+                } else {
+                    gen_helper_neon_acgt_f32(tmp, tmp, tmp2);
+                }
                 break;
-            default:
-                return 1;
-            }
-            break;
-        case 27: /* Float multiply.  */
-            gen_helper_neon_mul_f32(tmp, tmp, tmp2);
-            if (!u) {
-                dead_tmp(tmp2);
-                tmp2 = neon_load_reg(rd, pass);
+            case 30: /* Float min/max.  */
                 if (size == 0) {
-                    gen_helper_neon_add_f32(tmp, tmp, tmp2);
+                    gen_helper_neon_max_f32(tmp, tmp, tmp2);
+                } else {
+                    gen_helper_neon_min_f32(tmp, tmp, tmp2);
+                }
+                break;
+            case 31:
+                if (size == 0) {
+                    gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env);
                 } else {
-                    gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+                    gen_helper_rsqrts_f32(tmp, tmp, tmp2, cpu_env);
                 }
+                break;
+            default:
+                abort();
             }
-            break;
-        case 28: /* Float compare.  */
-            if (!u) {
-                gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+            dead_tmp(tmp2);
+
+            /* Save the result.  For elementwise operations we can put it
+               straight into the destination register.  For pairwise operations
+               we have to be careful to avoid clobbering the source operands.*/
+            if (pairwise && rd == rm) {
+                neon_store_scratch(pass, tmp);
             } else {
-                if (size == 0)
-                    gen_helper_neon_cge_f32(tmp, tmp, tmp2);
-                else
-                    gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+                neon_store_reg(rd, pass, tmp);
             }
-            break;
-        case 29: /* Float compare absolute.  */
-            if (!u)
-                return 1;
-            if (size == 0)
-                gen_helper_neon_acge_f32(tmp, tmp, tmp2);
-            else
-                gen_helper_neon_acgt_f32(tmp, tmp, tmp2);
-            break;
-        case 30: /* Float min/max.  */
-            if (size == 0)
-                gen_helper_neon_max_f32(tmp, tmp, tmp2);
-            else
-                gen_helper_neon_min_f32(tmp, tmp, tmp2);
-            break;
-        case 31:
-            if (size == 0)
-                gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env);
-            else
-                gen_helper_rsqrts_f32(tmp, tmp, tmp2, cpu_env);
-            break;
-        default:
-            abort();
-        }
-        dead_tmp(tmp2);
-
-        /* Save the result.  For elementwise operations we can put it
-           straight into the destination register.  For pairwise operations
-           we have to be careful to avoid clobbering the source operands.  */
-        if (pairwise && rd == rm) {
-            neon_store_scratch(pass, tmp);
-        } else {
-            neon_store_reg(rd, pass, tmp);
-        }
-
         } /* for pass */
         if (pairwise && rd == rm) {
             for (pass = 0; pass < (q ? 4 : 2); pass++) {
@@ -4597,23 +4572,32 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
             /* Two registers and shift.  */
             op = (insn >> 8) & 0xf;
             if (insn & (1 << 7)) {
+                if (op & 8) {
+                    return 1;
+                }
                 /* 64-bit shift.   */
                 size = 3;
             } else {
                 size = 2;
-                while ((insn & (1 << (size + 19))) == 0)
+                while ((insn & (1 << (size + 19))) == 0) {
                     size--;
+                }
             }
             shift = (insn >> 16) & ((1 << (3 + size)) - 1);
             /* To avoid excessive dumplication of ops we implement shift
                by immediate using the variable shift operations.  */
             if (op < 8) {
                 /* Shift by immediate:
-                   VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU.  */
+                   VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VSLI, VQSHL, VQSHLU */
                 /* Right shifts are encoded as N - shift, where N is the
                    element size in bits.  */
-                if (op <= 4)
+                if ((q && ((rd | rm) & 1))
+                    || (!u && (op == 4 || op == 6))) {
+                    return 1;
+                }
+                if (op <= 4) {
                     shift = shift - (1 << (size + 3));
+                }
                 if (size == 3) {
                     count = q + 1;
                 } else {
@@ -4644,21 +4628,25 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                         switch (op) {
                         case 0:  /* VSHR */
                         case 1:  /* VSRA */
-                            if (u)
-                                gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
-                            else
-                                gen_helper_neon_shl_s64(cpu_V0, cpu_V0, cpu_V1);
+                            if (u) {
+                                gen_helper_neon_shl_u64(cpu_V0, cpu_V0,
+                                                        cpu_V1);
+                            } else {
+                                gen_helper_neon_shl_s64(cpu_V0, cpu_V0,
+                                                        cpu_V1);
+                            }
                             break;
                         case 2: /* VRSHR */
                         case 3: /* VRSRA */
-                            if (u)
-                                gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, cpu_V1);
-                            else
-                                gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1);
+                            if (u) {
+                                gen_helper_neon_rshl_u64(cpu_V0, cpu_V0,
+                                                         cpu_V1);
+                            } else {
+                                gen_helper_neon_rshl_s64(cpu_V0, cpu_V0,
+                                                         cpu_V1);
+                            }
                             break;
                         case 4: /* VSRI */
-                            if (!u)
-                                return 1;
                             gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
                             break;
                         case 5: /* VSHL, VSLI */
@@ -4688,14 +4676,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1);
                         } else if (op == 4 || (op == 5 && u)) {
                             /* Insert */
-                            cpu_abort(env, "VS[LR]I.64 not implemented");
+                            neon_load_reg64(cpu_V1, rd + pass);
+                            uint64_t mask;
+                            if (op == 4) {
+                                mask = 0xffffffffffffffffull >> -shift;
+                            } else {
+                                mask = 0xffffffffffffffffull << shift;
+                            }
+                            tcg_gen_andi_i64(cpu_V0, cpu_V0, mask);
+                            tcg_gen_andi_i64(cpu_V1, cpu_V1, ~mask);
+                            tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
                         }
                         neon_store_reg64(cpu_V0, rd + pass);
                     } else { /* size < 3 */
                         /* Operands in T0 and T1.  */
                         tmp = neon_load_reg(rm, pass);
-                        tmp2 = new_tmp();
-                        tcg_gen_movi_i32(tmp2, imm);
+                        tmp2 = tcg_const_i32(imm);
                         switch (op) {
                         case 0:  /* VSHR */
                         case 1:  /* VSRA */
@@ -4706,16 +4702,19 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             GEN_NEON_INTEGER_OP(rshl);
                             break;
                         case 4: /* VSRI */
-                            if (!u)
-                                return 1;
-                            GEN_NEON_INTEGER_OP(shl);
-                            break;
                         case 5: /* VSHL, VSLI */
                             switch (size) {
-                            case 0: gen_helper_neon_shl_u8(tmp, tmp, tmp2); break;
-                            case 1: gen_helper_neon_shl_u16(tmp, tmp, tmp2); break;
-                            case 2: gen_helper_neon_shl_u32(tmp, tmp, tmp2); break;
-                            default: return 1;
+                            case 0:
+                                gen_helper_neon_shl_u8(tmp, tmp, tmp2);
+                                break;
+                            case 1:
+                                gen_helper_neon_shl_u16(tmp, tmp, tmp2);
+                                break;
+                            case 2:
+                                gen_helper_neon_shl_u32(tmp, tmp, tmp2);
+                                break;
+                            default:
+                                abort(); /* size == 3 is handled earlier */
                             }
                             break;
                         case 6: /* VQSHLU */
@@ -4743,7 +4742,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             GEN_NEON_INTEGER_OP_ENV(qshl);
                             break;
                         }
-                        dead_tmp(tmp2);
+                        tcg_temp_free_i32(tmp2);
 
                         if (op == 1 || op == 3) {
                             /* Accumulate.  */
@@ -4754,32 +4753,35 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             /* Insert */
                             switch (size) {
                             case 0:
-                                if (op == 4)
+                                if (op == 4) {
                                     mask = 0xff >> -shift;
-                                else
+                                } else {
                                     mask = (uint8_t)(0xff << shift);
+                                }
                                 mask |= mask << 8;
                                 mask |= mask << 16;
                                 break;
                             case 1:
-                                if (op == 4)
+                                if (op == 4) {
                                     mask = 0xffff >> -shift;
-                                else
+                                } else {
                                     mask = (uint16_t)(0xffff << shift);
+                                }
                                 mask |= mask << 16;
                                 break;
                             case 2:
                                 if (shift < -31 || shift > 31) {
                                     mask = 0;
                                 } else {
-                                    if (op == 4)
+                                    if (op == 4) {
                                         mask = 0xffffffffu >> -shift;
-                                    else
+                                    } else {
                                         mask = 0xffffffffu << shift;
+                                    }
                                 }
                                 break;
                             default:
-                                abort();
+                                abort(); /* size == 3 is handled earlier */
                             }
                             tmp2 = neon_load_reg(rd, pass);
                             tcg_gen_andi_i32(tmp, tmp, mask);
@@ -4792,7 +4794,10 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 } /* for pass */
             } else if (op < 10) {
                 /* Shift by immediate and narrow:
-                   VSHRN, VRSHRN, VQSHRN, VQRSHRN.  */
+                   VSHRN, VRSHRN, VQSHRN, VQSHRUN, VQRSHRN, VQRSHRUN */
+                if (rm & 1) {
+                    return 1;
+                }
                 shift = shift - (1 << (size + 3));
                 size++;
                 switch (size) {
@@ -4815,40 +4820,58 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     abort();
                 }
 
+                TCGV_UNUSED(tmp5);
                 for (pass = 0; pass < 2; pass++) {
                     if (size == 3) {
                         neon_load_reg64(cpu_V0, rm + pass);
                         if (q) {
-                          if (u)
-                            gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, tmp64);
-                          else
-                            gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, tmp64);
+                            if ((op == 8 && !u) || (op == 9 && u)) {
+                                gen_helper_neon_rshl_u64(cpu_V0, cpu_V0,
+                                                         tmp64);
+                            } else {
+                                gen_helper_neon_rshl_s64(cpu_V0, cpu_V0,
+                                                         tmp64);
+                            }
                         } else {
-                          if (u)
-                            gen_helper_neon_shl_u64(cpu_V0, cpu_V0, tmp64);
-                          else
-                            gen_helper_neon_shl_s64(cpu_V0, cpu_V0, tmp64);
+                            if ((op == 8 && !u) || (op == 9 && u)) {
+                                gen_helper_neon_shl_u64(cpu_V0, cpu_V0,
+                                                        tmp64);
+                            } else {
+                                gen_helper_neon_shl_s64(cpu_V0, cpu_V0,
+                                                        tmp64);
+                            }
                         }
                     } else {
                         tmp = neon_load_reg(rm + pass, 0);
-                        gen_neon_shift_narrow(size, tmp, tmp2, q, u);
+                        gen_neon_shift_narrow(size, tmp, tmp2, q,
+                                              (op == 8) ? !u : u);
                         tmp3 = neon_load_reg(rm + pass, 1);
-                        gen_neon_shift_narrow(size, tmp3, tmp2, q, u);
+                        gen_neon_shift_narrow(size, tmp3, tmp2, q,
+                                              (op == 8) ? !u : u);
                         tcg_gen_concat_i32_i64(cpu_V0, tmp, tmp3);
                         dead_tmp(tmp);
                         dead_tmp(tmp3);
                     }
-                    tmp = new_tmp();
-                    if (op == 8 && !u) {
-                        gen_neon_narrow(size - 1, tmp, cpu_V0);
-                    } else {
-                        if (op == 8)
-                            gen_neon_narrow_sats(size - 1, tmp, cpu_V0);
-                        else
-                            gen_neon_narrow_satu(size - 1, tmp, cpu_V0);
+                    tmp4 = new_tmp();
+                    if (op == 8) {
+                        if (u) { /* VQSHRUN / VQRSHRUN */
+                            gen_neon_unarrow_sats(size - 1, tmp4, cpu_V0);
+                        } else { /* VSHRN / VRSHRN */
+                            gen_neon_narrow(size - 1, tmp4, cpu_V0);
+                        }
+                    } else { /* VQSHRN / VQRSHRN */
+                        if (u) {
+                            gen_neon_narrow_satu(size - 1, tmp4, cpu_V0);
+                        } else {
+                            gen_neon_narrow_sats(size - 1, tmp4, cpu_V0);
+                        }
+                    }
+                    if (!pass) {
+                        tmp5 = tmp4;
                     }
-                    neon_store_reg(rd, pass, tmp);
                 } /* for pass */
+                neon_store_reg(rd, 0, tmp5);
+                neon_store_reg(rd, 1, tmp4);
                 if (size == 3) {
                     tcg_temp_free_i64(tmp64);
                 } else {
@@ -4856,21 +4879,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 }
             } else if (op == 10) {
                 /* VSHLL */
-                if (q || size == 3)
+                if (q) {
                     return 1;
+                }
                 tmp = neon_load_reg(rm, 0);
                 tmp2 = neon_load_reg(rm, 1);
                 for (pass = 0; pass < 2; pass++) {
-                    if (pass == 1)
+                    if (pass == 1) {
                         tmp = tmp2;
-
+                    }
                     gen_neon_widen(cpu_V0, tmp, size, u);
 
                     if (shift != 0) {
                         /* The shift is less than the width of the source
                            type, so we can just shift the whole register.  */
                         tcg_gen_shli_i64(cpu_V0, cpu_V0, shift);
-                        if (size < 2 || !u) {
+                        if (size < 2) {
                             uint64_t imm64;
                             if (size == 0) {
                                 imm = (0xffu >> (8 - shift));
@@ -4879,7 +4903,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                                 imm = 0xffff >> (16 - shift);
                             }
                             imm64 = imm | (((uint64_t)imm) << 32);
-                            tcg_gen_andi_i64(cpu_V0, cpu_V0, imm64);
+                            tcg_gen_andi_i64(cpu_V0, cpu_V0, ~imm64);
                         }
                     }
                     neon_store_reg64(cpu_V0, rd + pass);
@@ -4893,17 +4917,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 for (pass = 0; pass < (q ? 4 : 2); pass++) {
                     tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass));
                     if (!(op & 1)) {
-                        if (u)
+                        if (u){
+                                                       gen_vfp_toul(0, shift);
+                        } else {
+                            gen_vfp_tosl(0, shift);
+                        }
+                    } else {
+                        if (u) {
                             gen_vfp_ulto(0, shift);
-                        else
+                        } else {
                             gen_vfp_slto(0, shift);
-                    } else {
-                        if (u)
-                            gen_vfp_toul(0, shift);
-                        else
-                            gen_vfp_tosl(0, shift);
+                        }
                     }
-                    tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass));
+                    tcg_gen_st_f32(cpu_F0s, cpu_env,
+                                   neon_reg_offset(rd, pass));
                 }
             } else {
                 return 1;
@@ -4942,10 +4969,14 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 break;
             case 14:
                 imm |= (imm << 8) | (imm << 16) | (imm << 24);
-                if (invert)
+                if (invert) {
                     imm = ~imm;
+                }
                 break;
             case 15:
+                if (invert) {
+                    return 1;
+                }
                 imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19)
                       | ((imm & 0x40) ? (0x1f << 25) : (1 << 30));
                 break;
@@ -4984,36 +5015,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
     } else { /* (insn & 0x00800010 == 0x00800000) */
         if (size != 3) {
             op = (insn >> 8) & 0xf;
-            if ((insn & (1 << 6)) == 0) {
+            if (!q) {
                 /* Three registers of different lengths.  */
-                int src1_wide;
-                int src2_wide;
-                int prewiden;
-                /* prewiden, src1_wide, src2_wide */
-                static const int neon_3reg_wide[16][3] = {
-                    {1, 0, 0}, /* VADDL */
-                    {1, 1, 0}, /* VADDW */
-                    {1, 0, 0}, /* VSUBL */
-                    {1, 1, 0}, /* VSUBW */
-                    {0, 1, 1}, /* VADDHN */
-                    {0, 0, 0}, /* VABAL */
-                    {0, 1, 1}, /* VSUBHN */
-                    {0, 0, 0}, /* VABDL */
-                    {0, 0, 0}, /* VMLAL */
-                    {0, 0, 0}, /* VQDMLAL */
-                    {0, 0, 0}, /* VMLSL */
-                    {0, 0, 0}, /* VQDMLSL */
-                    {0, 0, 0}, /* Integer VMULL */
-                    {0, 0, 0}, /* VQDMULL */
-                    {0, 0, 0}  /* Polynomial VMULL */
-                };
-
-                prewiden = neon_3reg_wide[op][0];
-                src1_wide = neon_3reg_wide[op][1];
-                src2_wide = neon_3reg_wide[op][2];
-
-                if (size == 0 && (op == 9 || op == 11 || op == 13))
+
+                if (op == 15
+                    || (op < 4 && ((rd & 1) || ((op & 1) && (rn & 1))))
+                    || ((op == 4 || op == 6) && ((rn | rm) & 1))
+                    || ((op == 5 || op >= 7) && (rd & 1))
+                    || ((op == 9 || op == 11) && (u || size == 0))
+                    || (op == 13 && size == 0)
+                    || (op == 14 && (u || size))) {
                     return 1;
+                }
+
+                int src1_wide = (op == 1 || op == 3 || op == 4 || op == 6);
+                int src2_wide = (op == 4 || op == 6);
+                int prewiden = (op < 4);
 
                 /* Avoid overlapping operands.  Wide source operands are
                    always aligned so will never overlap with wide
@@ -5080,7 +5097,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                         case 5:
                             gen_helper_neon_abdl_u64(cpu_V0, tmp, tmp2);
                             break;
-                        default: abort();
+                        default: abort(); /* size == 3 is handled earlier */
                         }
                         dead_tmp(tmp2);
                         dead_tmp(tmp);
@@ -5090,14 +5107,16 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                         gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
                         break;
                     case 14: /* Polynomial VMULL */
-                        cpu_abort(env, "Polynomial VMULL not implemented");
-
+                        gen_helper_neon_mull_p8(cpu_V0, tmp, tmp2);
+                        dead_tmp(tmp2);
+                        dead_tmp(tmp);
+                        break;
                     default: /* 15 is RESERVED.  */
-                        return 1;
+                        abort(); /* op == 15 is handled earlier */
                     }
                     if (op == 5 || op == 13 || (op >= 8 && op <= 11)) {
                         /* Accumulate.  */
-                        if (op == 10 || op == 11) {
+                        if (op == 10) {
                             gen_neon_negl(cpu_V0, size);
                         }
 
@@ -5111,9 +5130,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             break;
                         case 9: case 11: /* VQDMLAL, VQDMLSL */
                             gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            if (op == 11) {
+                                gen_neon_negl(cpu_V0, size);
+                            }
                             gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
                             break;
-                            /* Fall through.  */
                         case 13: /* VQDMULL */
                             gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
                             break;
@@ -5136,7 +5157,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                                 tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
                                 tcg_gen_trunc_i64_i32(tmp, cpu_V0);
                                 break;
-                            default: abort();
+                            default: abort(); /* size == 3 is handled earlier */
                             }
                         } else {
                             switch (size) {
@@ -5151,7 +5172,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                                 tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
                                 tcg_gen_trunc_i64_i32(tmp, cpu_V0);
                                 break;
-                            default: abort();
+                            default: abort(); /* size == 3 is handled earlier */
                             }
                         }
                         if (pass == 0) {
@@ -5174,8 +5195,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 case 5: /* Floating point VMLS scalar */
                 case 8: /* Integer VMUL scalar */
                 case 9: /* Floating point VMUL scalar */
+                    if (size <= (op & 1)) {
+                        return 1;
+                    }
+                    /* fall through */
                 case 12: /* VQDMULH scalar */
                 case 13: /* VQRDMULH scalar */
+                    if (u && ((rd | rn) & 1)) {
+                        return 1;
+                    }
                     tmp = neon_get_scalar(size, rm);
                     neon_store_scratch(0, tmp);
                     for (pass = 0; pass < (u ? 4 : 2); pass++) {
@@ -5183,24 +5211,35 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                         tmp2 = neon_load_reg(rn, pass);
                         if (op == 12) {
                             if (size == 1) {
-                                gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
-                            } else {
-                                gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
+                                gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp,
+                                                           tmp2);
+                            } else { /* TODO: what happens when size == 0? */
+                                gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp,
+                                                           tmp2);
                             }
                         } else if (op == 13) {
                             if (size == 1) {
-                                gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
-                            } else {
-                                gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
+                                gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp,
+                                                            tmp2);
+                            } else { /* TODO: what happens when size == 0? */
+                                gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp,
+                                                            tmp2);
                             }
                         } else if (op & 1) {
                             gen_helper_neon_mul_f32(tmp, tmp, tmp2);
                         } else {
                             switch (size) {
-                            case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
-                            case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
-                            case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
-                            default: return 1;
+                            case 0:
+                                gen_helper_neon_mul_u8(tmp, tmp, tmp2);
+                                break;
+                            case 1:
+                                gen_helper_neon_mul_u16(tmp, tmp, tmp2);
+                                break;
+                            case 2:
+                                tcg_gen_mul_i32(tmp, tmp, tmp2);
+                                break;
+                            default:
+                                abort(); /* size == 3 is handled earlier */
                             }
                         }
                         dead_tmp(tmp2);
@@ -5221,21 +5260,26 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                                 gen_helper_neon_sub_f32(tmp, tmp2, tmp);
                                 break;
                             default:
-                                abort();
+                                abort(); /* size == 3 is handled earlier */
                             }
                             dead_tmp(tmp2);
                         }
                         neon_store_reg(rd, pass, tmp);
                     }
                     break;
-                case 2: /* VMLAL sclar */
                 case 3: /* VQDMLAL scalar */
-                case 6: /* VMLSL scalar */
                 case 7: /* VQDMLSL scalar */
-                case 10: /* VMULL scalar */
                 case 11: /* VQDMULL scalar */
-                    if (size == 0 && (op == 3 || op == 7 || op == 11))
+                    if (u) {
+                        return 1;
+                    }
+                    /* fall through */
+                case 2: /* VMLAL sclar */
+                case 6: /* VMLSL scalar */
+                case 10: /* VMULL scalar */
+                    if (size == 0 || (rd & 1)) {
                         return 1;
+                    }
 
                     tmp2 = neon_get_scalar(size, rm);
                     /* We need a copy of tmp2 because gen_neon_mull
@@ -5264,6 +5308,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             break;
                         case 3: case 7:
                             gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            if (op == 7) {
+                                gen_neon_negl(cpu_V0, size);
+                            }
                             gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
                             break;
                         case 10:
@@ -5289,8 +5336,10 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 /* Extract.  */
                 imm = (insn >> 8) & 0xf;
 
-                if (imm > 7 && !q)
+                if ((imm > 7 && !q)
+                    || (q && ((rd | rn | rm) & 1))) {
                     return 1;
+                }
 
                 if (imm == 0) {
                     neon_load_reg64(cpu_V0, rn);
@@ -5340,10 +5389,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 /* Two register misc.  */
                 op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
                 size = (insn >> 18) & 3;
+                if ((q && (op < 36 || op > 46) && ((rd | rm) & 1))
+                    || (op >= 56 && size != 2)) {
+                    return 1;
+                }
                 switch (op) {
                 case 0: /* VREV64 */
-                    if (size == 3)
+                    if (size == 3) {
                         return 1;
+                    }
                     for (pass = 0; pass < (q ? 2 : 1); pass++) {
                         tmp = neon_load_reg(rm, pass * 2);
                         tmp2 = neon_load_reg(rm, pass * 2 + 1);
@@ -5368,8 +5422,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     break;
                 case 4: case 5: /* VPADDL */
                 case 12: case 13: /* VPADAL */
-                    if (size == 3)
+                    if (size == 3) {
                         return 1;
+                    }
                     for (pass = 0; pass < q + 1; pass++) {
                         tmp = neon_load_reg(rm, pass * 2);
                         gen_neon_widen(cpu_V0, tmp, size, op & 1);
@@ -5390,83 +5445,57 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     }
                     break;
                 case 33: /* VTRN */
-                    if (size == 2) {
+                    switch (size) {
+                    case 0: case 1:
+                        goto elementwise;
+                    case 2:
                         for (n = 0; n < (q ? 4 : 2); n += 2) {
                             tmp = neon_load_reg(rm, n);
                             tmp2 = neon_load_reg(rd, n + 1);
                             neon_store_reg(rm, n, tmp2);
                             neon_store_reg(rd, n + 1, tmp);
                         }
-                    } else {
-                        goto elementwise;
+                        break;
+                    default:
+                        return 1;
                     }
                     break;
                 case 34: /* VUZP */
-                    /* Reg  Before       After
-                       Rd   A3 A2 A1 A0  B2 B0 A2 A0
-                       Rm   B3 B2 B1 B0  B3 B1 A3 A1
-                     */
-                    if (size == 3)
+                    if (size == 3 || (!q && size == 2)) {
                         return 1;
-                    gen_neon_unzip(rd, q, 0, size);
-                    gen_neon_unzip(rm, q, 4, size);
-                    if (q) {
-                        static int unzip_order_q[8] =
-                            {0, 2, 4, 6, 1, 3, 5, 7};
-                        for (n = 0; n < 8; n++) {
-                            int reg = (n < 4) ? rd : rm;
-                            tmp = neon_load_scratch(unzip_order_q[n]);
-                            neon_store_reg(reg, n % 4, tmp);
-                        }
-                    } else {
-                        static int unzip_order[4] =
-                            {0, 4, 1, 5};
-                        for (n = 0; n < 4; n++) {
-                            int reg = (n < 2) ? rd : rm;
-                            tmp = neon_load_scratch(unzip_order[n]);
-                            neon_store_reg(reg, n % 2, tmp);
-                        }
                     }
+                    tmp = tcg_const_i32(insn);
+                    gen_helper_neon_unzip(cpu_env, tmp);
+                    tcg_temp_free_i32(tmp);
                     break;
                 case 35: /* VZIP */
-                    /* Reg  Before       After
-                       Rd   A3 A2 A1 A0  B1 A1 B0 A0
-                       Rm   B3 B2 B1 B0  B3 A3 B2 A2
-                     */
-                    if (size == 3)
+                    if (size == 3 || (!q && size == 2)) {
                         return 1;
-                    count = (q ? 4 : 2);
-                    for (n = 0; n < count; n++) {
-                        tmp = neon_load_reg(rd, n);
-                        tmp2 = neon_load_reg(rd, n);
-                        switch (size) {
-                        case 0: gen_neon_zip_u8(tmp, tmp2); break;
-                        case 1: gen_neon_zip_u16(tmp, tmp2); break;
-                        case 2: /* no-op */; break;
-                        default: abort();
-                        }
-                        neon_store_scratch(n * 2, tmp);
-                        neon_store_scratch(n * 2 + 1, tmp2);
-                    }
-                    for (n = 0; n < count * 2; n++) {
-                        int reg = (n < count) ? rd : rm;
-                        tmp = neon_load_scratch(n);
-                        neon_store_reg(reg, n % count, tmp);
                     }
+                    tmp = tcg_const_i32(insn);
+                    gen_helper_neon_zip(cpu_env, tmp);
+                    tcg_temp_free_i32(tmp);
                     break;
                 case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */
-                    if (size == 3)
+                    if (size == 3 || (rm & 1)) {
                         return 1;
+                    }
                     TCGV_UNUSED(tmp2);
                     for (pass = 0; pass < 2; pass++) {
                         neon_load_reg64(cpu_V0, rm + pass);
                         tmp = new_tmp();
-                        if (op == 36 && q == 0) {
-                            gen_neon_narrow(size, tmp, cpu_V0);
-                        } else if (q) {
-                            gen_neon_narrow_satu(size, tmp, cpu_V0);
-                        } else {
-                            gen_neon_narrow_sats(size, tmp, cpu_V0);
+                        if (op == 36) {
+                            if (q) { /* VQMOVUN */
+                                gen_neon_unarrow_sats(size, tmp, cpu_V0);
+                            } else { /* VMOVN */
+                                gen_neon_narrow(size, tmp, cpu_V0);
+                            }
+                        } else { /* VQMOVN */
+                            if (q) {
+                                gen_neon_narrow_satu(size, tmp, cpu_V0);
+                            } else {
+                                gen_neon_narrow_sats(size, tmp, cpu_V0);
+                            }
                         }
                         if (pass == 0) {
                             tmp2 = tmp;
@@ -5477,21 +5506,25 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     }
                     break;
                 case 38: /* VSHLL */
-                    if (q || size == 3)
+                    if (q || size == 3 || (rd & 1)) {
                         return 1;
+                    }
                     tmp = neon_load_reg(rm, 0);
                     tmp2 = neon_load_reg(rm, 1);
                     for (pass = 0; pass < 2; pass++) {
-                        if (pass == 1)
+                        if (pass == 1) {
                             tmp = tmp2;
+                        }
                         gen_neon_widen(cpu_V0, tmp, size, 1);
                         tcg_gen_shli_i64(cpu_V0, cpu_V0, 8 << size);
                         neon_store_reg64(cpu_V0, rd + pass);
                     }
                     break;
                 case 44: /* VCVT.F16.F32 */
-                    if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
-                      return 1;
+                    if (!arm_feature(env, ARM_FEATURE_VFP_FP16)
+                        || q || size != 1 || (rm & 1)) {
+                        return 1;
+                    }
                     tmp = new_tmp();
                     tmp2 = new_tmp();
                     tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 0));
@@ -5512,8 +5545,10 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                     dead_tmp(tmp);
                     break;
                 case 46: /* VCVT.F32.F16 */
-                    if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
-                      return 1;
+                    if (!arm_feature(env, ARM_FEATURE_VFP_FP16)
+                        || q || size != 1 || (rd & 1)) {
+                        return 1;
+                    }
                     tmp3 = new_tmp();
                     tmp = neon_load_reg(rm, 0);
                     tmp2 = neon_load_reg(rm, 1);
@@ -5548,38 +5583,44 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             switch (size) {
                             case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
                             case 1: gen_swap_half(tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
                         case 2: /* VREV16 */
-                            if (size != 0)
+                            if (size != 0) {
+                                dead_tmp(tmp);
                                 return 1;
+                            }
                             gen_rev16(tmp);
                             break;
-                        case 8: /* CLS */
+                        case 8: /* VCLS */
                             switch (size) {
                             case 0: gen_helper_neon_cls_s8(tmp, tmp); break;
                             case 1: gen_helper_neon_cls_s16(tmp, tmp); break;
                             case 2: gen_helper_neon_cls_s32(tmp, tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
-                        case 9: /* CLZ */
+                        case 9: /* VCLZ */
                             switch (size) {
                             case 0: gen_helper_neon_clz_u8(tmp, tmp); break;
                             case 1: gen_helper_neon_clz_u16(tmp, tmp); break;
                             case 2: gen_helper_clz(tmp, tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
-                        case 10: /* CNT */
-                            if (size != 0)
+                        case 10: /* VCNT */
+                            if (size != 0) {
+                                dead_tmp(tmp);
                                 return 1;
+                            }
                             gen_helper_neon_cnt_u8(tmp, tmp);
                             break;
-                        case 11: /* VNOT */
-                            if (size != 0)
+                        case 11: /* VMVN */
+                            if (size != 0) {
+                                dead_tmp(tmp);
                                 return 1;
+                            }
                             tcg_gen_not_i32(tmp, tmp);
                             break;
                         case 14: /* VQABS */
@@ -5587,7 +5628,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             case 0: gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); break;
                             case 1: gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); break;
                             case 2: gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
                         case 15: /* VQNEG */
@@ -5595,84 +5636,112 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             case 0: gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); break;
                             case 1: gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); break;
                             case 2: gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
                         case 16: case 19: /* VCGT #0, VCLE #0 */
                             tmp2 = tcg_const_i32(0);
-                            switch(size) {
+                            switch (size) {
                             case 0: gen_helper_neon_cgt_s8(tmp, tmp, tmp2); break;
                             case 1: gen_helper_neon_cgt_s16(tmp, tmp, tmp2); break;
                             case 2: gen_helper_neon_cgt_s32(tmp, tmp, tmp2); break;
-                            default: return 1;
+                            default: tcg_temp_free_i32(tmp2); dead_tmp(tmp); return 1;
                             }
-                            tcg_temp_free(tmp2);
-                            if (op == 19)
+                            tcg_temp_free_i32(tmp2);
+                            if (op == 19) {
                                 tcg_gen_not_i32(tmp, tmp);
+                            }
                             break;
                         case 17: case 20: /* VCGE #0, VCLT #0 */
                             tmp2 = tcg_const_i32(0);
-                            switch(size) {
+                            switch (size) {
                             case 0: gen_helper_neon_cge_s8(tmp, tmp, tmp2); break;
                             case 1: gen_helper_neon_cge_s16(tmp, tmp, tmp2); break;
                             case 2: gen_helper_neon_cge_s32(tmp, tmp, tmp2); break;
-                            default: return 1;
+                            default: tcg_temp_free_i32(tmp2); dead_tmp(tmp); return 1;
                             }
-                            tcg_temp_free(tmp2);
-                            if (op == 20)
+                            tcg_temp_free_i32(tmp2);
+                            if (op == 20) {
                                 tcg_gen_not_i32(tmp, tmp);
+                            }
                             break;
                         case 18: /* VCEQ #0 */
                             tmp2 = tcg_const_i32(0);
-                            switch(size) {
+                            switch (size) {
                             case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
                             case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
                             case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
-                            default: return 1;
+                            default: tcg_temp_free_i32(tmp2); dead_tmp(tmp); return 1;
                             }
-                            tcg_temp_free(tmp2);
+                            tcg_temp_free_i32(tmp2);
                             break;
                         case 22: /* VABS */
-                            switch(size) {
+                            switch (size) {
                             case 0: gen_helper_neon_abs_s8(tmp, tmp); break;
                             case 1: gen_helper_neon_abs_s16(tmp, tmp); break;
                             case 2: tcg_gen_abs_i32(tmp, tmp); break;
-                            default: return 1;
+                            default: dead_tmp(tmp); return 1;
                             }
                             break;
                         case 23: /* VNEG */
-                            if (size == 3)
+                            if (size == 3) {
+                                dead_tmp(tmp);
                                 return 1;
+                            }
                             tmp2 = tcg_const_i32(0);
                             gen_neon_rsb(size, tmp, tmp2);
-                            tcg_temp_free(tmp2);
+                            tcg_temp_free_i32(tmp2);
                             break;
                         case 24: case 27: /* Float VCGT #0, Float VCLE #0 */
+                            if (size != 2) {
+                                dead_tmp(tmp);
+                                return 1;
+                            }
                             tmp2 = tcg_const_i32(0);
                             gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
-                            tcg_temp_free(tmp2);
-                            if (op == 27)
+                            tcg_temp_free_i32(tmp2);
+                            if (op == 27) {
                                 tcg_gen_not_i32(tmp, tmp);
+                            }
                             break;
                         case 25: case 28: /* Float VCGE #0, Float VCLT #0 */
+                            if (size != 2) {
+                                dead_tmp(tmp);
+                                return 1;
+                            }
                             tmp2 = tcg_const_i32(0);
                             gen_helper_neon_cge_f32(tmp, tmp, tmp2);
-                            tcg_temp_free(tmp2);
-                            if (op == 28)
+                            tcg_temp_free_i32(tmp2);
+                            if (op == 28) {
                                 tcg_gen_not_i32(tmp, tmp);
+                            }
                             break;
                         case 26: /* Float VCEQ #0 */
+                            if (size != 2) {
+                                dead_tmp(tmp);
+                                return 1;
+                            }
                             tmp2 = tcg_const_i32(0);
                             gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
-                            tcg_temp_free(tmp2);
+                            tcg_temp_free_i32(tmp2);
                             break;
                         case 30: /* Float VABS */
+                            if (size != 2) {
+                                return 1;
+                            }
                             gen_vfp_abs(0);
                             break;
                         case 31: /* Float VNEG */
+                            if (size != 2) {
+                                return 1;
+                            }
                             gen_vfp_neg(0);
                             break;
                         case 32: /* VSWP */
+                            if (size != 0) {
+                                dead_tmp(tmp);
+                                return 1;
+                            }
                             tmp2 = neon_load_reg(rd, pass);
                             neon_store_reg(rm, pass, tmp2);
                             break;
@@ -5681,8 +5750,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             switch (size) {
                             case 0: gen_neon_trn_u8(tmp, tmp2); break;
                             case 1: gen_neon_trn_u16(tmp, tmp2); break;
-                            case 2: abort();
-                            default: return 1;
+                            default: abort(); /* size == 2,3 is handled earlier */
                             }
                             neon_store_reg(rm, pass, tmp2);
                             break;
@@ -5711,7 +5779,8 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                             gen_vfp_touiz(0);
                             break;
                         default:
-                            /* Reserved: 21, 29, 39-56 */
+                            /* Reserved: 3, 6, 7, 21, 29, 39-43, 45, 47-55 */
+                            dead_tmp(tmp);
                             return 1;
                         }
                         if (op == 30 || op == 31 || op >= 58) {
@@ -5726,7 +5795,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
             } else if ((insn & (1 << 10)) == 0) {
                 /* VTBL, VTBX.  */
                 n = ((insn >> 5) & 0x18) + 8;
-                if (insn & (1 << 6)) {
+                if (q) {
                     tmp = neon_load_reg(rd, 0);
                 } else {
                     tmp = new_tmp();
@@ -5737,7 +5806,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 tmp5 = tcg_const_i32(n);
                 gen_helper_neon_tbl(tmp2, tmp2, tmp, tmp4, tmp5);
                 dead_tmp(tmp);
-                if (insn & (1 << 6)) {
+                if (q) {
                     tmp = neon_load_reg(rd, 1);
                 } else {
                     tmp = new_tmp();
@@ -5752,6 +5821,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 dead_tmp(tmp);
             } else if ((insn & 0x380) == 0) {
                 /* VDUP */
+                if ((insn & (7 << 16)) == 0 || (q && (rd & 1))) {
+                    return 1;
+                }
                 if (insn & (1 << 19)) {
                     tmp = neon_load_reg(rm, 1);
                 } else {
@@ -5760,10 +5832,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
                 if (insn & (1 << 16)) {
                     gen_neon_dup_u8(tmp, ((insn >> 17) & 3) * 8);
                 } else if (insn & (1 << 17)) {
-                    if ((insn >> 18) & 1)
+                    if ((insn >> 18) & 1) {
                         gen_neon_dup_high16(tmp);
-                    else
+                    } else {
                         gen_neon_dup_low16(tmp);
+                    }
                 }
                 for (pass = 0; pass < (q ? 4 : 2); pass++) {
                     tmp2 = new_tmp();
@@ -6102,6 +6175,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         }
         if ((insn & 0x0d70f000) == 0x0550f000)
             return; /* PLD */
+        if ((insn & 0x0d70f000) == 0x0450f000) {
+            ARCH(7);
+            return; /* PLI */
+        }
         else if ((insn & 0x0ffffdff) == 0x01010000) {
             ARCH(6);
             /* setend */
@@ -6234,8 +6311,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
             }
         } else if ((insn & 0x0fe00000) == 0x0c400000) {
             /* Coprocessor double register transfer.  */
+            cpu_abort(env, "unsupported coprocessor double register transfer\n");
         } else if ((insn & 0x0f000010) == 0x0e000010) {
             /* Additional coprocessor register transfer.  */
+            if (!disas_coproc_insn(env, s, insn)) {
+                return;
+            }
         } else if ((insn & 0x0ff10020) == 0x01000000) {
             uint32_t mask;
             uint32_t val;
@@ -6270,6 +6351,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
         s->condlabel = gen_new_label();
         gen_test_cc(cond ^ 1, s->condlabel);
         s->condjmp = 1;
+        gen_save_condexec(s);
     }
     if ((insn & 0x0f900000) == 0x03000000) {
         if ((insn & (1 << 21)) == 0) {
@@ -6937,7 +7019,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                         case 4: gen_uxtb16(tmp);  break;
                         case 6: gen_uxtb(tmp);    break;
                         case 7: gen_uxth(tmp);    break;
-                        default: goto illegal_op;
+                        default: dead_tmp(tmp); goto illegal_op;
                         }
                         if (rn != 15) {
                             tmp2 = load_reg(s, rn);
@@ -7149,7 +7231,8 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                 }
                 rn = (insn >> 16) & 0xf;
                 addr = load_reg(s, rn);
-
+                tmp3 = tcg_const_i32(4);
+                
                 /* compute total size */
                 loaded_base = 0;
                 TCGV_UNUSED(loaded_var);
@@ -7162,7 +7245,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                 if (insn & (1 << 23)) {
                     if (insn & (1 << 24)) {
                         /* pre increment */
-                        tcg_gen_addi_i32(addr, addr, 4);
+                        tcg_gen_add_i32(addr, addr, tmp3);
                     } else {
                         /* post increment */
                     }
@@ -7215,7 +7298,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                         j++;
                         /* no need to add after the last transfer */
                         if (j != n)
-                            tcg_gen_addi_i32(addr, addr, 4);
+                            tcg_gen_add_i32(addr, addr, tmp3);
                     }
                 }
                 if (insn & (1 << 21)) {
@@ -7225,7 +7308,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                             /* pre increment */
                         } else {
                             /* post increment */
-                            tcg_gen_addi_i32(addr, addr, 4);
+                            tcg_gen_add_i32(addr, addr, tmp3);
                         }
                     } else {
                         if (insn & (1 << 24)) {
@@ -7241,6 +7324,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
                 } else {
                     dead_tmp(addr);
                 }
+                tcg_temp_free_i32(tmp3);
                 if (loaded_base) {
                     store_reg(s, rn, loaded_var);
                 }
@@ -7606,6 +7690,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     tcg_gen_addi_i32(addr, addr, -offset);
                 }
 
+                tmp2 = tcg_const_i32(4);
                 for (i = 0; i < 16; i++) {
                     if ((insn & (1 << i)) == 0)
                         continue;
@@ -7622,8 +7707,9 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                         tmp = load_reg(s, i);
                         gen_st32(tmp, addr, IS_USER(s));
                     }
-                    tcg_gen_addi_i32(addr, addr, 4);
+                    tcg_gen_add_i32(addr, addr, tmp2);
                 }
+                tcg_temp_free_i32(tmp2);
                 if (insn & (1 << 21)) {
                     /* Base register writeback.  */
                     if (insn & (1 << 24)) {
@@ -7949,7 +8035,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
         /* Coprocessor.  */
         if (((insn >> 24) & 3) == 3) {
             /* Translate into the equivalent ARM encoding.  */
-            insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4);
+            insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28);
             if (disas_neon_data_insn(env, s, insn))
                 goto illegal_op;
         } else {
@@ -7994,8 +8080,11 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     goto illegal_op;
 
                 if (insn & (1 << 26)) {
-                    /* Secure monitor call (v6Z) */
-                    goto illegal_op; /* not implemented.  */
+                    /* Secure monitor call / smc (v6Z) */
+                    if (!(env->cp15.c0_c2[4] & 0xf000) || IS_USER(s)) {
+                        goto illegal_op;
+                    }
+                    gen_smc(env, s);
                 } else {
                     op = (insn >> 20) & 7;
                     switch (op) {
@@ -8863,6 +8952,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
             if ((insn & (1 << 11)) == 0) {
                 tcg_gen_addi_i32(addr, addr, -offset);
             }
+            tmp2 = tcg_const_i32(4);
             for (i = 0; i < 8; i++) {
                 if (insn & (1 << i)) {
                     if (insn & (1 << 11)) {
@@ -8875,7 +8965,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
                         gen_st32(tmp, addr, IS_USER(s));
                     }
                     /* advance to the next address.  */
-                    tcg_gen_addi_i32(addr, addr, 4);
+                    tcg_gen_add_i32(addr, addr, tmp2);
                 }
             }
             TCGV_UNUSED(tmp);
@@ -8890,8 +8980,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
                     tmp = load_reg(s, 14);
                     gen_st32(tmp, addr, IS_USER(s));
                 }
-                tcg_gen_addi_i32(addr, addr, 4);
+                tcg_gen_add_i32(addr, addr, tmp2);
             }
+            tcg_temp_free_i32(tmp2);
             if ((insn & (1 << 11)) == 0) {
                 tcg_gen_addi_i32(addr, addr, -offset);
             }
@@ -9074,9 +9165,13 @@ static inline void gen_intermediate_code_internal(CPUState *env,
     uint32_t next_page_start;
     int num_insns;
     int max_insns;
+    int need_to_save_condexec = 0;
+#ifdef RESOURCE_LEAK_DEBUG
+    int force_asmdump = 0;
 
     /* generate intermediate code */
     num_temps = 0;
+#endif
 
     pc_start = tb->pc;
 
@@ -9091,6 +9186,16 @@ static inline void gen_intermediate_code_internal(CPUState *env,
     dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
     dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
     dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
+    dc->thumb = env->thumb;
+    /* If interrupt happens inside of an IT block new TB begins at the
+     * instruction that caused it after return from ISR.  To preserve
+     * correct execution restore IT bits saved right before the interrupt.  */
+    if (env->thumb && env->saved_condexec_tb == env->regs[15]) {
+        env->condexec_bits = env->saved_condexec_bits;
+        env->saved_condexec_tb = 0;
+    }
+    dc->condexec_mask = (env->condexec_bits & 0xf) << 1;
+    dc->condexec_cond = env->condexec_bits >> 4;
 #if !defined(CONFIG_USER_ONLY)
     dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
 #endif
@@ -9105,6 +9210,9 @@ static inline void gen_intermediate_code_internal(CPUState *env,
     cpu_V1 = cpu_F1d;
     /* FIXME: cpu_M0 can probably be the same as cpu_V0.  */
     cpu_M0 = tcg_temp_new_i64();
+#ifdef RESOURCE_LEAK_DEBUG
+    num_temps = 0; /* to ignore above global temporaries from the count */
+#endif
     next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
     lj = -1;
     num_insns = 0;
@@ -9149,10 +9257,15 @@ static inline void gen_intermediate_code_internal(CPUState *env,
        complications trying to do it at the end of the block.  */
     if (dc->condexec_mask || dc->condexec_cond)
       {
-        TCGv tmp = new_tmp();
-        tcg_gen_movi_i32(tmp, 0);
-        store_cpu_field(tmp, condexec_bits);
-      }
+                 if (env->condexec_bits) {
+                         TCGv tmp = new_tmp();
+                         tcg_gen_movi_i32(tmp, 0);
+                         store_cpu_field(tmp, condexec_bits);
+                         tmp = new_tmp();
+                         tcg_gen_movi_i32(tmp, env->condexec_bits);
+                         store_cpu_field(tmp, actual_condexec_bits);
+                 }
+         }
     do {
 #ifdef CONFIG_USER_ONLY
         /* Intercept jump to the magic kernel page.  */
@@ -9213,19 +9326,29 @@ static inline void gen_intermediate_code_internal(CPUState *env,
                 dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f;
                 if (dc->condexec_mask == 0) {
                     dc->condexec_cond = 0;
+                    /* Clear saved condexec.
+                     * Code to clear is inserted after the last label.  */
+                    need_to_save_condexec = 1;
                 }
             }
         } else {
             disas_arm_insn(env, dc);
         }
+#ifdef RESOURCE_LEAK_DEBUG
         if (num_temps) {
             fprintf(stderr, "Internal resource leak before %08x\n", dc->pc);
+            force_asmdump = 1;
             num_temps = 0;
         }
+#endif
 
         if (dc->condjmp && !dc->is_jmp) {
             gen_set_label(dc->condlabel);
             dc->condjmp = 0;
+            if (need_to_save_condexec) {
+                gen_save_condexec(dc);
+                need_to_save_condexec = 0;
+            }
         }
         /* Translation stops when a conditional branch is encountered.
          * Otherwise the subsequent code could get translated several times.
@@ -9256,6 +9379,8 @@ static inline void gen_intermediate_code_internal(CPUState *env,
             gen_set_condexec(dc);
             if (dc->is_jmp == DISAS_SWI) {
                 gen_exception(EXCP_SWI);
+            } else if (dc->is_jmp == DISAS_SMC) {
+                gen_exception(EXCP_SMC);
             } else {
                 gen_exception(EXCP_DEBUG);
             }
@@ -9268,6 +9393,8 @@ static inline void gen_intermediate_code_internal(CPUState *env,
         gen_set_condexec(dc);
         if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
             gen_exception(EXCP_SWI);
+        } else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
+            gen_exception(EXCP_SMC);
         } else {
             /* FIXME: Single stepping a WFI insn will not halt
                the CPU.  */
@@ -9302,10 +9429,14 @@ static inline void gen_intermediate_code_internal(CPUState *env,
         case DISAS_SWI:
             gen_exception(EXCP_SWI);
             break;
+        case DISAS_SMC:
+            gen_exception(EXCP_SMC);
+            break;
         }
         if (dc->condjmp) {
             gen_set_label(dc->condlabel);
             gen_set_condexec(dc);
+            gen_save_condexec(dc);
             gen_goto_tb(dc, 1, dc->pc);
             dc->condjmp = 0;
         }
@@ -9315,6 +9446,14 @@ done_generating:
     gen_icount_end(tb, num_insns);
     *gen_opc_ptr = INDEX_op_end;
 
+#ifdef RESOURCE_LEAK_DEBUG
+    if (force_asmdump) {
+        fprintf(stderr, "----------------\n");
+        fprintf(stderr, "IN: %s\n", lookup_symbol(pc_start));
+        target_disas(stderr, pc_start, dc->pc - pc_start, env->thumb);
+        fprintf(stderr, "\n");
+    }
+#endif
 #ifdef DEBUG_DISAS
     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
         qemu_log("----------------\n");
index 389a118..54485b5 100644 (file)
@@ -1,6 +1,6 @@
 include ./../../config-host.mak
 include ./../../i386-softmmu/config-target.mak
-#include ./../../arm-softmmu/config-target.mak
+include ./../../arm-softmmu/config-target.mak
 
 # Trick for invalid QEMU Makefile targets coming from the first include file
 subdir-libhw32: all
@@ -174,8 +174,7 @@ endif
        @echo "  CC    $@"
        $(Q)$(CC) $(CFLAGS) -o $@ -c $<
 
-#all: $(TARGET_X86) $(TARGET_ARM)
-all: $(TARGET_X86) $(VTM)
+all: $(TARGET_X86) $(TARGET_ARM) $(VTM)
 
 -include $(DEPFILES)
 
@@ -231,8 +230,8 @@ endif
        mkdir -p $(PHONE_DIR)/x86/data/kernel-img
        mkdir -p $(PHONE_DIR)/x86/VMs/default
        mkdir -p $(PHONE_DIR)/x86/VMs/default/logs
-       install emulator-x86 $(PHONE_DIR)/bin/emulator-x86
-#      install emulator-arm $(PHONE_DIR)/bin/emulator-arm
+       install emulator-x86 $(PHONE_DIR)/emulator-x86
+       install emulator-arm $(PHONE_DIR)/emulator-arm
 #      install savevm.glade $(PHONE_DIR)
        cp -a $(VTM) $(PHONE_DIR)/bin
        cp -a vtm.glade $(PHONE_DIR)/etc
index 6324775..d624136 100644 (file)
 MULTI_DEBUG_CHANNEL(tizen, arch_arm);
 
 extern int sensor_update(uint16_t x, uint16_t y, uint16_t z);
-
+/*
 // fake sensor_update
 int sensor_update(uint16_t x, uint16_t y, uint16_t z)
 {
        TRACE( "x(%d), y(%d), z(%d) \n", x, y, z);
        return 0;
 }
-
+*/
 int qemu_arch_is_arm(void)
 {
        return 1;
index 6bec463..6335c99 100755 (executable)
@@ -991,7 +991,7 @@ gint motion_notify_event_handler(GtkWidget *widget, GdkEventButton *event, gpoin
 
                                if (kbd_mouse_is_absolute()) {
                                        TRACE( "press parsing keycode = %d, result = %d\n", keycode, keycode & 0x7f);
-                                       ps2kbd_put_keycode(keycode & 0x7f);                             
+                                       //ps2kbd_put_keycode(keycode & 0x7f);                           
                                }
                        }
                        else {
@@ -1033,7 +1033,7 @@ gint motion_notify_event_handler(GtkWidget *widget, GdkEventButton *event, gpoin
 
                                if (kbd_mouse_is_absolute()) {
                                        TRACE( "release parsing keycode = %d, result = %d\n", keycode, keycode| 0x80);
-                                       ps2kbd_put_keycode(keycode | 0x80);     
+                                       //ps2kbd_put_keycode(keycode | 0x80);   
                                }
                        }
                }
index f9b982e..35935fe 100644 (file)
@@ -44,6 +44,8 @@
 #include "vnc-enc-tight.h"
 #include "vnc-palette.h"
 
+typedef unsigned int uint;
+
 /* Compression level stuff. The following array contains various
    encoder parameters for each of 10 compression levels (0..9).
    Last three parameters correspond to JPEG quality levels (0..9). */
index ccf7073..81a30f1 100644 (file)
@@ -187,6 +187,8 @@ typedef struct AsyncURB
 
     USBPacket     *packet;
     USBHostDevice *hdev;
+
+    int more;     /* packet required multiple URBs */
 } AsyncURB;
 
 static AsyncURB *async_alloc(void)
@@ -252,7 +254,7 @@ static void async_complete(void *opaque)
         if (p) {
             switch (aurb->urb.status) {
             case 0:
-                p->len = aurb->urb.actual_length;
+                p->len += aurb->urb.actual_length;
                 if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
                     async_complete_ctrl(s, p);
                 }
@@ -268,7 +270,10 @@ static void async_complete(void *opaque)
                 break;
             }
 
-            usb_packet_complete(p);
+            if (!aurb->more) {
+                DPRINTF("invoking packet_complete. plen = %d\n", p->len);
+                usb_packet_complete(p);
+            }
         }
 
         async_free(aurb);
@@ -294,7 +299,7 @@ static void async_cancel(USBPacket *unused, void *opaque)
 static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
 {
     int dev_descr_len, config_descr_len;
-    int interface, nb_interfaces;
+    int interface, nb_interfaces, nb_configurations;
     int ret, i;
 
     if (configuration == 0) /* address state - ignore */
@@ -307,12 +312,12 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
     if (dev_descr_len > dev->descr_len) {
         goto fail;
     }
+    nb_configurations = dev->descr[17];
 
     i += dev_descr_len;
     while (i < dev->descr_len) {
         DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
-                i, dev->descr_len,
-               dev->descr[i], dev->descr[i+1]);
+                i, dev->descr_len, dev->descr[i], dev->descr[i+1]);
 
         if (dev->descr[i+1] != USB_DT_CONFIG) {
             i += dev->descr[i];
@@ -414,69 +419,89 @@ static void usb_host_handle_destroy(USBDevice *dev)
 
 static int usb_linux_update_endp_table(USBHostDevice *s);
 
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
 static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
 {
     struct usbdevfs_urb *urb;
     AsyncURB *aurb;
-    int ret;
+    int ret, len;
+    int rem = p->len;
+    uint8_t *pbuf = p->data;
 
-    aurb = async_alloc();
-    aurb->hdev   = s;
-    aurb->packet = p;
+    p->len = 0;
+    while (rem) {
+        aurb = async_alloc();
+        aurb->hdev   = s;
+        aurb->packet = p;
 
-    urb = &aurb->urb;
+        urb = &aurb->urb;
 
-    if (p->pid == USB_TOKEN_IN) {
-        urb->endpoint = p->devep | 0x80;
-    } else {
-        urb->endpoint = p->devep;
-    }
+        if (p->pid == USB_TOKEN_IN) {
+            urb->endpoint = p->devep | 0x80;
+        } else {
+            urb->endpoint = p->devep;
+        }
 
-    if (is_halted(s, p->devep)) {
-        ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
-        if (ret < 0) {
-            DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n",
-                   urb->endpoint, errno);
-            return USB_RET_NAK;
+        if (is_halted(s, p->devep)) {
+            ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
+            if (ret < 0) {
+                DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n",
+                        urb->endpoint, errno);
+                return USB_RET_NAK;
+            }
+            clear_halt(s, p->devep);
         }
-        clear_halt(s, p->devep);
-    }
 
-    urb->buffer        = p->data;
-    urb->buffer_length = p->len;
+        if (is_isoc(s, p->devep)) {
+            /* Setup ISOC transfer */
+            urb->type     = USBDEVFS_URB_TYPE_ISO;
+            urb->flags    = USBDEVFS_URB_ISO_ASAP;
+            urb->number_of_packets = 1;
+            urb->iso_frame_desc[0].length = p->len;
+        } else {
+            /* Setup bulk transfer */
+            urb->type     = USBDEVFS_URB_TYPE_BULK;
+        }
 
-    if (is_isoc(s, p->devep)) {
-        /* Setup ISOC transfer */
-        urb->type     = USBDEVFS_URB_TYPE_ISO;
-        urb->flags    = USBDEVFS_URB_ISO_ASAP;
-        urb->number_of_packets = 1;
-        urb->iso_frame_desc[0].length = p->len;
-    } else {
-        /* Setup bulk transfer */
-        urb->type     = USBDEVFS_URB_TYPE_BULK;
-    }
+        urb->usercontext = s;
 
-    urb->usercontext = s;
+        /* USBFS limits max request size to 16k */
+        if (rem > MAX_USBFS_BUFFER_SIZE) {
+            len = MAX_USBFS_BUFFER_SIZE;
+            aurb->more = 1;
+        } else {
+            len = rem;
+            aurb->more = 0;
+        }
+        urb->buffer_length = len;
+        urb->buffer = pbuf;
 
-    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
 
-    DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
-            urb->endpoint, p->len, aurb);
+        DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
+                urb->endpoint, len, aurb);
 
-    if (ret < 0) {
-        DPRINTF("husb: submit failed. errno %d\n", errno);
-        async_free(aurb);
+        if (ret < 0) {
+            DPRINTF("husb: submit failed. errno %d\n", errno);
+                async_free(aurb);
 
-        switch(errno) {
-        case ETIMEDOUT:
-            return USB_RET_NAK;
-        case EPIPE:
-        default:
-            return USB_RET_STALL;
+            switch(errno) {
+            case ETIMEDOUT:
+                return USB_RET_NAK;
+            case EPIPE:
+            default:
+                return USB_RET_STALL;
+            }
         }
+
+        usb_defer_packet(p, async_cancel, aurb);
+
+        pbuf += len;
+        rem -= len;
     }
 
-    usb_defer_packet(p, async_cancel, aurb);
     return USB_RET_ASYNC;
 }
 
diff --git a/vl.c b/vl.c
index ee7c386..ca327a0 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -277,6 +277,10 @@ int kvm_allowed = 0;
 uint32_t xen_domid;
 enum xen_mode xen_mode = XEN_EMULATE;
 
+#ifdef CONFIG_GLES2
+int gles2_quality = 100;
+#endif
+
 static int default_serial = 1;
 static int default_parallel = 1;
 static int default_virtcon = 1;
@@ -2583,6 +2587,13 @@ int qemu_main(int argc, char **argv, char **envp)
             case QEMU_OPTION_smbios:
                 do_smbios_option(optarg);
                 break;
+#ifdef CONFIG_GLES2
+            case QEMU_OPTION_gles2_quality:
+                {
+                    gles2_quality = strtoul(optarg, NULL, 10);
+                }
+                break;
+#endif
             case QEMU_OPTION_enable_kvm:
                 kvm_allowed = 1;
                 break;