From 13aebd03979378e12d8f07ac10050b42e4cea56b Mon Sep 17 00:00:00 2001 From: "jinseok86.kim" Date: Mon, 12 Dec 2011 15:55:02 +0900 Subject: [PATCH] [Title] arm_emulator branch merged into master [Type] Support [Module] [Priority] [CQ#] [Redmine#] [Problem] [Cause] [Solution] [TestCase] Skin HW Button is disabled due to multiple keyboard support. (Needs re-implementation) --- Makefile.objs | 4 +- Makefile.target | 64 +- block.c | 4 + block/raw-posix.c | 3 + block/raw.c | 130 ++ block/vvfat.c | 8 +- block_int.h | 1 + configure | 63 +- console.h | 25 +- default-configs/arm-softmmu.mak | 2 + hw/adb.c | 5 +- hw/ak8973.c | 277 ++++ hw/ak8973.h | 10 + hw/arm-misc.h | 1 + hw/arm_boot.c | 6 + hw/bitbang_i2c.c | 3 +- hw/boards.h | 1 + hw/eepro100.c | 6 + hw/escc.c | 28 +- hw/flash.h | 12 +- hw/gles1_calls.c | 1396 +++++++++++++++++++ hw/gles2.c | 754 +++++++++++ hw/gles2.h | 357 +++++ hw/gles2_calls.c | 2796 +++++++++++++++++++++++++++++++++++++++ hw/gles2_calls.h | 278 ++++ hw/gles2_types.h | 92 ++ hw/i2c-addressable.c | 86 ++ hw/i2c-addressable.h | 61 + hw/i2c.h | 7 + hw/ide/mmio.c | 6 +- hw/iommu.c | 386 ++++++ hw/mcs5000_tk.c | 247 ++++ hw/musicpal.c | 7 +- hw/nseries.c | 17 +- hw/onenand.c | 174 ++- hw/palm.c | 5 +- hw/pci_host_template.h | 109 ++ hw/pci_ids.h | 1 + hw/pl192.c | 588 ++++++++ hw/pl330.c | 1571 ++++++++++++++++++++++ hw/primecell.h | 10 + hw/ps2.c | 5 +- hw/pxa2xx_keypad.c | 13 +- hw/qdev.c | 19 + hw/qdev.h | 2 + hw/qt602240_ts.c | 454 +++++++ hw/s5pc1xx.c | 710 ++++++++++ hw/s5pc1xx.h | 107 ++ hw/s5pc1xx_ac97.c | 630 +++++++++ hw/s5pc1xx_clk.c | 827 ++++++++++++ hw/s5pc1xx_debug.c | 582 ++++++++ hw/s5pc1xx_gpio.c | 621 +++++++++ hw/s5pc1xx_gpio_regs.h | 228 ++++ hw/s5pc1xx_hsmmc_regs.h | 258 ++++ hw/s5pc1xx_i2c.c | 263 ++++ hw/s5pc1xx_i2c_gpio.c | 147 ++ hw/s5pc1xx_i2s.c | 546 ++++++++ hw/s5pc1xx_jpeg.c | 1034 +++++++++++++++ hw/s5pc1xx_keyif.c | 547 ++++++++ hw/s5pc1xx_lcd.c | 1505 +++++++++++++++++++++ hw/s5pc1xx_mmc.c | 725 ++++++++++ hw/s5pc1xx_nand.c | 273 ++++ hw/s5pc1xx_onedram.c | 1112 ++++++++++++++++ hw/s5pc1xx_onedram.h | 397 ++++++ hw/s5pc1xx_onenand.c | 382 ++++++ hw/s5pc1xx_pcm.c | 717 ++++++++++ hw/s5pc1xx_pmu.c | 1295 ++++++++++++++++++ hw/s5pc1xx_pwm.c | 458 +++++++ hw/s5pc1xx_rtc.c | 488 +++++++ hw/s5pc1xx_spdif.c | 807 +++++++++++ hw/s5pc1xx_spi.c | 215 +++ hw/s5pc1xx_srom.c | 162 +++ hw/s5pc1xx_st.c | 408 ++++++ hw/s5pc1xx_tsadc.c | 467 +++++++ hw/s5pc1xx_uart.c | 485 +++++++ hw/s5pc1xx_usb_otg.c | 812 ++++++++++++ hw/s5pc1xx_wdt.c | 208 +++ hw/smb380.c | 326 +++++ hw/smbus_smb380.c | 350 +++++ hw/spitz.c | 3 + hw/stellaris_input.c | 7 +- hw/syborg_keyboard.c | 7 +- hw/usb-ehci.c | 2143 ++++++++++++++++++++++++++++++ hw/usb-ehci.h | 8 + hw/usb-hid.c | 34 +- hw/usb-ohci.c | 41 +- hw/wm8994.c | 308 +++++ hw/wm8994_reg.h | 232 ++++ hw/xenfb.c | 8 +- input.c | 189 ++- linux-user/elfload32.c | 30 + monitor.c | 9 + qemu-options.hx | 12 + qemu_configure.sh | 2 +- qmp-commands.hx | 106 ++ slirp/ip_icmp.h | 5 + target-arm/cpu.h | 22 +- target-arm/helper.c | 424 ++++-- target-arm/helper_gpi_dummy.c | 9 + target-arm/helpers.h | 13 + target-arm/machine.c | 6 + target-arm/neon_helper.c | 340 ++++- target-arm/op_helper.c | 173 +++ target-arm/opengl_dummy.c | 24 + target-arm/translate.c | 1525 +++++++++++---------- tizen/src/Makefile.in | 9 +- tizen/src/arch_arm.c | 4 +- tizen/src/event_handler.c | 4 +- ui/vnc-enc-tight.c | 2 + usb-linux.c | 123 +- vl.c | 11 + 111 files changed, 32945 insertions(+), 1074 deletions(-) create mode 100644 hw/ak8973.c create mode 100644 hw/ak8973.h create mode 100644 hw/gles1_calls.c create mode 100644 hw/gles2.c create mode 100644 hw/gles2.h create mode 100644 hw/gles2_calls.c create mode 100644 hw/gles2_calls.h create mode 100644 hw/gles2_types.h create mode 100644 hw/i2c-addressable.c create mode 100644 hw/i2c-addressable.h create mode 100644 hw/iommu.c create mode 100644 hw/mcs5000_tk.c create mode 100644 hw/pci_host_template.h create mode 100644 hw/pl192.c create mode 100644 hw/pl330.c create mode 100644 hw/qt602240_ts.c create mode 100644 hw/s5pc1xx.c create mode 100644 hw/s5pc1xx.h create mode 100644 hw/s5pc1xx_ac97.c create mode 100644 hw/s5pc1xx_clk.c create mode 100644 hw/s5pc1xx_debug.c create mode 100644 hw/s5pc1xx_gpio.c create mode 100644 hw/s5pc1xx_gpio_regs.h create mode 100644 hw/s5pc1xx_hsmmc_regs.h create mode 100644 hw/s5pc1xx_i2c.c create mode 100644 hw/s5pc1xx_i2c_gpio.c create mode 100644 hw/s5pc1xx_i2s.c create mode 100644 hw/s5pc1xx_jpeg.c create mode 100644 hw/s5pc1xx_keyif.c create mode 100644 hw/s5pc1xx_lcd.c create mode 100644 hw/s5pc1xx_mmc.c create mode 100644 hw/s5pc1xx_nand.c create mode 100644 hw/s5pc1xx_onedram.c create mode 100644 hw/s5pc1xx_onedram.h create mode 100644 hw/s5pc1xx_onenand.c create mode 100644 hw/s5pc1xx_pcm.c create mode 100644 hw/s5pc1xx_pmu.c create mode 100644 hw/s5pc1xx_pwm.c create mode 100644 hw/s5pc1xx_rtc.c create mode 100644 hw/s5pc1xx_spdif.c create mode 100644 hw/s5pc1xx_spi.c create mode 100644 hw/s5pc1xx_srom.c create mode 100644 hw/s5pc1xx_st.c create mode 100644 hw/s5pc1xx_tsadc.c create mode 100644 hw/s5pc1xx_uart.c create mode 100644 hw/s5pc1xx_usb_otg.c create mode 100644 hw/s5pc1xx_wdt.c create mode 100644 hw/smb380.c create mode 100644 hw/smbus_smb380.c create mode 100644 hw/usb-ehci.c create mode 100644 hw/usb-ehci.h create mode 100644 hw/wm8994.c create mode 100644 hw/wm8994_reg.h create mode 100644 linux-user/elfload32.c create mode 100644 target-arm/helper_gpi_dummy.c create mode 100644 target-arm/opengl_dummy.c diff --git a/Makefile.objs b/Makefile.objs index 3b52e33..2215e94 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 diff --git a/Makefile.target b/Makefile.target index 9fd41e9..a899f8e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -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 --- 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]; diff --git a/block/raw-posix.c b/block/raw-posix.c index 6b72470..4b4ce6f 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -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; diff --git a/block/raw.c b/block/raw.c index b0f72d6..70c2fb9 100644 --- a/block/raw.c +++ b/block/raw.c @@ -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); } diff --git a/block/vvfat.c b/block/vvfat.c index fe568fe..2d28c48 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -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) diff --git a/block_int.h b/block_int.h index 545ad11..b9755f0 100644 --- a/block_int.h +++ b/block_int.h @@ -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; diff --git a/configure b/configure index fdbe3d9..f315a1b 100755 --- 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 < #include 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 diff --git a/console.h b/console.h index ed88432..9830bd7 100644 --- 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) diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 8d1174f..ea78f4e 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -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 diff --git a/hw/adb.c b/hw/adb.c index 99b30f6..ab39591 100644 --- 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 index 0000000..bed951a --- /dev/null +++ b/hw/ak8973.c @@ -0,0 +1,277 @@ +/* + * AK8973 Compass Emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Junsik.Park + */ + +#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 index 0000000..366b2a2 --- /dev/null +++ b/hw/ak8973.h @@ -0,0 +1,10 @@ +/* + * AK8973 Compass Emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Junsik.Park + * Dmitry Zhurikhin + */ + +void ak8973_update(DeviceState *dev, + uint8_t x, uint8_t y, uint8_t z, uint8_t temp); diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 010acb4..e2d8397 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -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; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 620550b..b6ec911 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -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); diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 4ee99a1..7ca2b39 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -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); diff --git a/hw/boards.h b/hw/boards.h index 6f0f0d7..5d6edb1 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -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; diff --git a/hw/eepro100.c b/hw/eepro100.c index edf48f6..166526b 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -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); diff --git a/hw/escc.c b/hw/escc.c index f6fd919..682409d 100644 --- 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; diff --git a/hw/flash.h b/hw/flash.h index d7d103e..ecac39d 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -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 index 0000000..7bd2cd3 --- /dev/null +++ b/hw/gles1_calls.c @@ -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 . + */ + +#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 index 0000000..ab4f822 --- /dev/null +++ b/hw/gles2.c @@ -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 . + */ + +#include "gles2.h" +#include + +// 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[] = +{ + { "<>", 0 }, +#include "gles2_calls.h" +}; + +#undef CALL_ENTRY + diff --git a/hw/gles2.h b/hw/gles2.h new file mode 100644 index 0000000..21b2cbe --- /dev/null +++ b/hw/gles2.h @@ -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 . + */ + +#ifndef GLES2_H__ +#define GLES2_H__ + +#include "qemu-common.h" +#include "cpu.h" +#include + +#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 index 0000000..1cdec75 --- /dev/null +++ b/hw/gles2_calls.c @@ -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 . + */ + +#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, ¶m); + 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, ¶m); + 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 index 0000000..446858f --- /dev/null +++ b/hw/gles2_calls.h @@ -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 . + */ + +/*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 index 0000000..5506509 --- /dev/null +++ b/hw/gles2_types.h @@ -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 . + */ + +#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 index 0000000..eeb35a3 --- /dev/null +++ b/hw/i2c-addressable.c @@ -0,0 +1,86 @@ +/* + * Interface to support I2C devices with control registers accessed by I2C + * + * Contributed by Alexey Merkulov + * Dmitry Zhurikhin + * Vladimir Monakhov + */ + +#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 index 0000000..4a2a2f1 --- /dev/null +++ b/hw/i2c-addressable.h @@ -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 + * Dmitry Zhurikhin + * Vladimir Monakhov + * + * 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 diff --git a/hw/i2c.h b/hw/i2c.h index 83fd917..71ee662 100644 --- 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 diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 82b24b6..3d7da82 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -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 index 0000000..4151022 --- /dev/null +++ b/hw/iommu.c @@ -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 index 0000000..c24f1a2 --- /dev/null +++ b/hw/mcs5000_tk.c @@ -0,0 +1,247 @@ +/* + * Melfas MCS-5000 Touchkey + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Dmitry Zhurikhin + * Vladimir Monakhov + * + * 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) diff --git a/hw/musicpal.c b/hw/musicpal.c index d98aa8d..aef8ba5 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -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; } diff --git a/hw/nseries.c b/hw/nseries.c index 2f6f473..ddada06 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -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. */ diff --git a/hw/onenand.c b/hw/onenand.c index 71c1ab4..dd6d96c 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -24,12 +24,6 @@ #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); diff --git a/hw/palm.c b/hw/palm.c index f22d777..d8e2857 100644 --- 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 index 0000000..11e6c88 --- /dev/null +++ b/hw/pci_host_template.h @@ -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; +} diff --git a/hw/pci_ids.h b/hw/pci_ids.h index 68561fa..8b4cfb8 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -107,6 +107,7 @@ #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 index 0000000..8a3c51d --- /dev/null +++ b/hw/pl192.c @@ -0,0 +1,588 @@ +/* + * ARM PrimeCell PL192 Vector Interrupt Controller + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +#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 index 0000000..b72d2c4 --- /dev/null +++ b/hw/pl330.c @@ -0,0 +1,1571 @@ +/* + * ARM PrimeCell PL330 DMA Controller + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +#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) diff --git a/hw/primecell.h b/hw/primecell.h index fb456ad..5e3c565 100644 --- a/hw/primecell.h +++ b/hw/primecell.h @@ -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); diff --git a/hw/ps2.c b/hw/ps2.c index 9ccf8cb..edc24dd 100644 --- 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; } diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c index 4c99917..9d9eea0 100644 --- a/hw/pxa2xx_keypad.c +++ b/hw/pxa2xx_keypad.c @@ -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"); } diff --git a/hw/qdev.c b/hw/qdev.c index c7fec44..dbdb596 100644 --- 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) { diff --git a/hw/qdev.h b/hw/qdev.h index 9808f85..b57991c 100644 --- 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 index 0000000..8530d21 --- /dev/null +++ b/hw/qt602240_ts.c @@ -0,0 +1,454 @@ +/* + * AT42QT602240 Touchscreen + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Alexey Merkulov + * Dmitry Zhurikhin + * Vladimir Monakhov + * + * 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 index 0000000..9c1a5a5 --- /dev/null +++ b/hw/s5pc1xx.c @@ -0,0 +1,710 @@ +/* + * Samsung S5PC1XX-based board emulation. + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + * Vladimir Monakhov + * Alexey Merkulov + * Dmitry Zhurikhin + */ + + +/* 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 index 0000000..5ec95d5 --- /dev/null +++ b/hw/s5pc1xx.h @@ -0,0 +1,107 @@ +/* + * Samsung S5PC1XX-based board emulation. + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + * Vladimir Monakhov + * Alexey Merkulov + * Dmitry Zhurikhin + */ + +#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 index 0000000..fc72188 --- /dev/null +++ b/hw/s5pc1xx_ac97.c @@ -0,0 +1,630 @@ +/* + * AC97 controller for Samsung S5PC110-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..c04975c --- /dev/null +++ b/hw/s5pc1xx_clk.c @@ -0,0 +1,827 @@ +/* + * Clock controller for Samsung S5PC1XX-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + * + * 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 */ + short div_shift; /* MANDATORY FIELD - Shift for divisor value in CLK_DIV */ + + 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 index 0000000..334f1fb --- /dev/null +++ b/hw/s5pc1xx_debug.c @@ -0,0 +1,582 @@ +/* + * S5PC110 Test & Debug + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +/* 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 *)®, 4); + if (! (reg & 2)) { + outcome = 1; + } + cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)®, 4); + if (reg != ((unsigned)1 << 31)) { + outcome = 1; + } + cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)®, 4); + if (reg != PROG_ADDR + 20) { + outcome = 1; + } + dma_kill_dbg(); + cpu_physical_memory_read(FSC_ADDR, (uint8_t *)®, 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 *)®, 4); + if (! (reg & 2)) { + outcome = 1; + } + cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)®, 4); + if (reg != ((unsigned)1 << 31)) { + outcome = 1; + } + cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)®, 4); + if (reg != PROG_ADDR + 20) { + outcome = 1; + } + dma_kill_dbg(); + cpu_physical_memory_read(FSC_ADDR, (uint8_t *)®, 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 *)®, 4); + if (! (reg & 2)) { + outcome = 1; + } + cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)®, 4); + if (reg != ((unsigned)1 << 31)) { + outcome = 1; + } + cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)®, 4); + if (reg != PROG_ADDR + 18) { + outcome = 1; + } + dma_kill_dbg(); + cpu_physical_memory_read(FSC_ADDR, (uint8_t *)®, 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 *)®, 4); + if (! (reg & 2)) { + outcome = 1; + } + cpu_physical_memory_read(FTC1_ADDR, (uint8_t *)®, 4); + if (reg != ((unsigned)1 << 31)) { + outcome = 1; + } + cpu_physical_memory_read(CPC1_ADDR, (uint8_t *)®, 4); + if (reg != PROG_ADDR + 18) { + outcome = 1; + } + dma_kill_dbg(); + cpu_physical_memory_read(FSC_ADDR, (uint8_t *)®, 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 index 0000000..19806b8 --- /dev/null +++ b/hw/s5pc1xx_gpio.c @@ -0,0 +1,621 @@ +/* + * GPIO controller for Samsung S5PC110-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..93b43fb --- /dev/null +++ b/hw/s5pc1xx_gpio_regs.h @@ -0,0 +1,228 @@ +/* + * S5C GPIO Controller Constants + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + + +/* 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 index 0000000..85c3a5e --- /dev/null +++ b/hw/s5pc1xx_hsmmc_regs.h @@ -0,0 +1,258 @@ +/* + * S5C HS-MMC Controller Constants + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + * + * 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 index 0000000..efe2899 --- /dev/null +++ b/hw/s5pc1xx_i2c.c @@ -0,0 +1,263 @@ +/* + * I2C controller for Samsung S5PC1XX-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + * Alexey Merkulov + * + * 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 index 0000000..36e8529 --- /dev/null +++ b/hw/s5pc1xx_i2c_gpio.c @@ -0,0 +1,147 @@ +/* + * I2C bus through GPIO pins + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Alexey Merkulov + */ + +#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 index 0000000..e178b67 --- /dev/null +++ b/hw/s5pc1xx_i2s.c @@ -0,0 +1,546 @@ +/* + * IIS Multi Audio Interface for Samsung S5PC1XX-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Alexey Merkulov + * + * 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 index 0000000..7cdcdc7 --- /dev/null +++ b/hw/s5pc1xx_jpeg.c @@ -0,0 +1,1034 @@ +/* + * JPEG codec for Samsung S5PC110-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#include "qemu-common.h" +#include "sysbus.h" + +#ifdef CONFIG_JPEG +#include +#include +#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 index 0000000..9c21905 --- /dev/null +++ b/hw/s5pc1xx_keyif.c @@ -0,0 +1,547 @@ +/* + * S5PC1XX Keypad Interface + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Alexey Merkulov + */ + +#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 index 0000000..3428901 --- /dev/null +++ b/hw/s5pc1xx_lcd.c @@ -0,0 +1,1505 @@ +/* + * S5PC1XX LCD Controller + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +/* + * 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 index 0000000..e1de090 --- /dev/null +++ b/hw/s5pc1xx_mmc.c @@ -0,0 +1,725 @@ +/* + * MMC controller for Samsung S5PC1XX-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Alexey Merkulov + * Vladimir Monakhov + * + * 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 index 0000000..242c2bd --- /dev/null +++ b/hw/s5pc1xx_nand.c @@ -0,0 +1,273 @@ +/* + * S5PC1XX NAND controller. + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +#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 index 0000000..2ca0faf --- /dev/null +++ b/hw/s5pc1xx_onedram.c @@ -0,0 +1,1112 @@ +/* + * S5PC1XX OneDRAM controller. + * + * Contributed by Kefeng Li + */ + +#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 index 0000000..d3d3d8b --- /dev/null +++ b/hw/s5pc1xx_onedram.h @@ -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 index 0000000..d98ba57 --- /dev/null +++ b/hw/s5pc1xx_onenand.c @@ -0,0 +1,382 @@ +/* + * S5PC1XX OneNAND controller. + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Alexey Merkulov + */ + +#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 index 0000000..1710859 --- /dev/null +++ b/hw/s5pc1xx_pcm.c @@ -0,0 +1,717 @@ +/* + * PCM audio interface for Samsung S5PC110-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..d6428fe --- /dev/null +++ b/hw/s5pc1xx_pmu.c @@ -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 + * Vladimir Monakhov + */ + +#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 index 0000000..3be4d2d --- /dev/null +++ b/hw/s5pc1xx_pwm.c @@ -0,0 +1,458 @@ +/* + * PWM Timer for Samsung S5PC110-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..83c621a --- /dev/null +++ b/hw/s5pc1xx_rtc.c @@ -0,0 +1,488 @@ +/* + * Real Time Clock for Samsung S5C110-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..d34dbf2 --- /dev/null +++ b/hw/s5pc1xx_spdif.c @@ -0,0 +1,807 @@ +/* + * SPDIF transmitter for Samsung S5PC110-based board emulation + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..13dfa9e --- /dev/null +++ b/hw/s5pc1xx_spi.c @@ -0,0 +1,215 @@ +/* + * S5PC1XX SPI Emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Dmitry Zhurikhin + */ + +#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 index 0000000..058b996 --- /dev/null +++ b/hw/s5pc1xx_srom.c @@ -0,0 +1,162 @@ +/* + * S5PC1XX SROM controller. + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Alexey Merkulov + */ + +#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 index 0000000..ea0ed43 --- /dev/null +++ b/hw/s5pc1xx_st.c @@ -0,0 +1,408 @@ +/* + * System Timer for Samsung S5C110-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + * Dmitry Zhurikhin + */ + +#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 index 0000000..737bf02 --- /dev/null +++ b/hw/s5pc1xx_tsadc.c @@ -0,0 +1,467 @@ +/* + * S5PC1XX ADC & TOUCH SCREEN INTERFACE + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Alexey Merkulov + * Dmitry Zhurikhin + */ + +#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 index 0000000..b36cf5c --- /dev/null +++ b/hw/s5pc1xx_uart.c @@ -0,0 +1,485 @@ +/* + * S5PC1XX UART Emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +#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 index 0000000..d699933 --- /dev/null +++ b/hw/s5pc1xx_usb_otg.c @@ -0,0 +1,812 @@ +/* + * S5PC1XX UART Emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Kirill Batuzov + */ + +#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 index 0000000..f248d44 --- /dev/null +++ b/hw/s5pc1xx_wdt.c @@ -0,0 +1,208 @@ +/* + * Watchdog Timer for Samsung S5C110-based board emulation + * + * Copyright (c) 2009 Samsung Electronics. + * Contributed by Vladimir Monakhov + */ + +#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 index 0000000..067f335 --- /dev/null +++ b/hw/smb380.c @@ -0,0 +1,326 @@ +/* + * SMB380 Sensor Emulation + * + * Contributed by Junsik.Park + */ + +#ifndef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#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 index 0000000..b73b3f5 --- /dev/null +++ b/hw/smbus_smb380.c @@ -0,0 +1,350 @@ +/* + * SMB380 Sensor Emulation + * + * Contributed by Junsik.Park + */ + +#ifndef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#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) diff --git a/hw/spitz.c b/hw/spitz.c index 5b1e42d..3bc004e 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -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); diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c index 16aae96..ec69965 100644 --- a/hw/stellaris_input.c +++ b/hw/stellaris_input.c @@ -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); } diff --git a/hw/syborg_keyboard.c b/hw/syborg_keyboard.c index d295e99..d2f6b5a 100644 --- a/hw/syborg_keyboard.c +++ b/hw/syborg_keyboard.c @@ -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 index 0000000..a58ba0d --- /dev/null +++ b/hw/usb-ehci.c @@ -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 index 0000000..d3ade36 --- /dev/null +++ b/hw/usb-ehci.h @@ -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 diff --git a/hw/usb-hid.c b/hw/usb-hid.c index b559c50..100b3bb 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -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); diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 09ea0b6..9ec5f3c 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -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 index 0000000..d56a502 --- /dev/null +++ b/hw/wm8994.c @@ -0,0 +1,308 @@ +/* + * WM8994 Audio Codec + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Alexey Merkulov + * Dmitry Zhurikhin + * Vladimir Monakhov + */ + +#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 samples 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 index 0000000..daa109c --- /dev/null +++ b/hw/wm8994_reg.h @@ -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 */ diff --git a/hw/xenfb.c b/hw/xenfb.c index da5297b..d4523c5 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -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 --- a/input.c +++ b/input.c @@ -28,41 +28,14 @@ #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 index 0000000..4b4648c --- /dev/null +++ b/linux-user/elfload32.c @@ -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; +} diff --git a/monitor.c b/monitor.c index 7fc311d..f6b8622 100644 --- 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 = "", diff --git a/qemu-options.hx b/qemu-options.hx index 945edf3..1bf4ef4 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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 diff --git a/qemu_configure.sh b/qemu_configure.sh index 47957eb..a7fa241 100755 --- a/qemu_configure.sh +++ b/qemu_configure.sh @@ -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 \ diff --git a/qmp-commands.hx b/qmp-commands.hx index df40a3d..355a020 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -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", diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h index 2692822..87967fe 100644 --- a/slirp/ip_icmp.h +++ b/slirp/ip_icmp.h @@ -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 +#endif +#include + #endif diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 5bcd53a..71832eb 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -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 diff --git a/target-arm/helper.c b/target-arm/helper.c index b562767..00218ac 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -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 index 0000000..26991d8 --- /dev/null +++ b/target-arm/helper_gpi_dummy.c @@ -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; +} diff --git a/target-arm/helpers.h b/target-arm/helpers.h index 8a2564e..541ed23 100644 --- a/target-arm/helpers.h +++ b/target-arm/helpers.h @@ -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" diff --git a/target-arm/machine.c b/target-arm/machine.c index 3925d3a..8595549 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -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); diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index 268af33..f906e0b 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -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 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); + } +} diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 3de2610..954fdc3 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -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 index 0000000..57e4336 --- /dev/null +++ b/target-arm/opengl_dummy.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#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 +#include +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 diff --git a/target-arm/translate.c b/target-arm/translate.c index 5149543..fa20663 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -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"); diff --git a/tizen/src/Makefile.in b/tizen/src/Makefile.in index 389a118..54485b5 100644 --- a/tizen/src/Makefile.in +++ b/tizen/src/Makefile.in @@ -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 diff --git a/tizen/src/arch_arm.c b/tizen/src/arch_arm.c index 6324775..d624136 100644 --- a/tizen/src/arch_arm.c +++ b/tizen/src/arch_arm.c @@ -41,14 +41,14 @@ 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; diff --git a/tizen/src/event_handler.c b/tizen/src/event_handler.c index 6bec463..6335c99 100755 --- a/tizen/src/event_handler.c +++ b/tizen/src/event_handler.c @@ -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); } } } diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index f9b982e..35935fe 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -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). */ diff --git a/usb-linux.c b/usb-linux.c index ccf7073..81a30f1 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -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 --- 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; -- 2.7.4