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
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
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 \
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
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)
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
##########################################################
BlockDriver *drv)
{
int ret;
+ int probed = 0;
if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1;
/* Find the right image format driver */
if (!drv) {
ret = find_image_format(filename, &drv);
+ probed = 1;
}
if (!drv) {
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];
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;
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);
}
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);
}
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)
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;
vde=""
vnc_tls=""
vnc_sasl=""
+jpeg="yes"
vnc_jpeg=""
vnc_png=""
vnc_thread="no"
pkgversion=""
check_utests="no"
user_pie="no"
+gles2="no"
+gles2_static="no"
zero_malloc=""
trace_backend="nop"
trace_file="trace"
;;
--enable-jpeg) jpeg="yes"
;;
-
--disable-vnc-tls) vnc_tls="no"
;;
--enable-vnc-tls) vnc_tls="yes"
;;
--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"
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]"
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"
fi
##########################################
-# VNC JPEG detection
-if test "$vnc_jpeg" != "no" ; then
+# JPEG detection
+if test "$jpeg" != "no"; then
cat > $TMPC <<EOF
#include <stdio.h>
#include <jpeglib.h>
int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
EOF
- vnc_jpeg_cflags=""
- vnc_jpeg_libs="-ljpeg"
- if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
- vnc_jpeg=yes
- libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+ jpeg_cflags=""
+ jpeg_libs="-ljpeg"
+ if compile_prog "$jpeg_cflags" "$jpeg_libs" ; then
+ jpeg=yes
+ if test "$vnc_jpeg" != "no"; then
+ vnc_jpeg=yes
+ fi
+ libs_softmmu="$jpeg_libs $libs_softmmu"
else
- if test "$vnc_jpeg" = "yes" ; then
- feature_not_found "vnc-jpeg"
+ if test "$jpeg" = "yes" ; then
+ feature_not_found "jpeg"
fi
+ jpeg=no
vnc_jpeg=no
fi
+elif test "$vnc_jpeg" != "no"; then
+ echo
+ echo "Error: VNC JPEG was requested but JPEG was disabled"
+ echo "Please either enable JPEG with '--enable-jpeg' or disable VNC JPEG with '--disable-vnc-jpeg'."
+ echo
+ exit 1
fi
##########################################
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"
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"
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
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
/* 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);
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);
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)
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
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;
s->wptr = 0;
s->count++;
}
+ return 0;
}
static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
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);
}
--- /dev/null
+/*
+ * AK8973 Compass Emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#include "i2c-addressable.h"
+#include "ak8973.h"
+
+//#define DEBUG
+
+#define AK8973_ST 0xC0
+#define AK8973_TMPS 0xC1
+#define AK8973_H1X 0xC2
+#define AK8973_H1Y 0xC3
+#define AK8973_H1Z 0xC4
+
+#define AK8973_MS1 0xE0
+#define AK8973_HXDA 0xE1
+#define AK8973_HYDA 0xE2
+#define AK8973_HZDA 0xE3
+#define AK8973_HXGA 0xE4
+#define AK8973_HYGA 0xE5
+#define AK8973_HZGA 0xE6
+
+#define AK8973_ETS 0x62
+#define AK8973_EVIR 0x63
+#define AK8973_EIHE 0x64
+#define AK8973_ETST 0x65
+#define AK8973_EHXGA 0x66
+#define AK8973_EHYGA 0x67
+#define AK8973_EHZGA 0x68
+#define AK8973_WRAL1 0x60
+
+
+typedef struct AK8973State {
+ I2CAddressableState i2c_addressable;
+
+ uint8_t status;
+ uint8_t x;
+ uint8_t y;
+ uint8_t z;
+ uint8_t temp;
+ uint8_t ms1;
+ uint8_t dac[3];
+ uint8_t gain[3];
+ qemu_irq irq;
+} AK8973State;
+
+
+static void ak8973_reset(DeviceState *d)
+{
+ AK8973State *s =
+ FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+ s->status = 0;
+ /* Random coordinates */
+ s->x = 10;
+ s->y = 20;
+ s->z = 30;
+
+ /* 20 degree Celsius */
+ s->temp = 0x90;
+
+ /* EEPROM data-write disable / Powerdown mode */
+ s->ms1 = 0x3;
+
+ /* TODO: get the defaults */
+ s->dac[0] = 0;
+ s->dac[1] = 0;
+ s->dac[2] = 0;
+ s->gain[0] = 0;
+ s->gain[1] = 0;
+ s->gain[2] = 0;
+
+ return;
+}
+
+static uint8_t ak8973_read(void *opaque, uint32_t address, uint8_t offset)
+{
+ struct AK8973State *s = (struct AK8973State *)opaque;
+ uint32_t ret = 0, index = address + offset;
+
+ switch (index) {
+ case AK8973_ST:
+ ret = s->status;
+ break;
+ case AK8973_TMPS:
+ /* IRQ is lowered when any of data registers are read */
+ qemu_irq_lower(s->irq);
+ s->status &= ~1;
+ ret = s->temp;
+ break;
+ case AK8973_H1X:
+ qemu_irq_lower(s->irq);
+ s->status &= ~1;
+ ret = s->x;
+ break;
+ case AK8973_H1Y:
+ qemu_irq_lower(s->irq);
+ s->status &= ~1;
+ ret = s->y;
+ break;
+ case AK8973_H1Z:
+ qemu_irq_lower(s->irq);
+ s->status &= ~1;
+ ret = s->z;
+ break;
+ case AK8973_MS1:
+ ret = s->ms1;
+ break;
+ case AK8973_HXDA:
+ case AK8973_HYDA:
+ case AK8973_HZDA:
+ ret = s->dac[index - AK8973_HXDA];
+ break;
+ case AK8973_HXGA:
+ case AK8973_HYGA:
+ case AK8973_HZGA:
+ ret = s->gain[index - AK8973_HXGA];
+ break;
+ case AK8973_ETS:
+ case AK8973_EVIR:
+ case AK8973_EIHE:
+ case AK8973_ETST:
+ case AK8973_EHXGA:
+ case AK8973_EHYGA:
+ case AK8973_EHZGA:
+ case AK8973_WRAL1:
+ if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) != 0xA8) {
+ /* TODO: implement EEPROM reading */
+ ret = 0;
+ } else {
+ ret = 0;
+ }
+ break;
+ default:
+ hw_error("ak8973: bad read offset 0x%x\n", index);
+ }
+
+#ifdef DEBUG
+ printf("ak8973_read IDX = 0x%x, Data = 0x%x\n", index, ret);
+#endif
+
+ return ret;
+}
+
+static void ak8973_write(void *opaque, uint32_t address, uint8_t offset,
+ uint8_t val)
+{
+ struct AK8973State *s = (struct AK8973State *)opaque;
+ uint32_t index = address + offset;
+
+#ifdef DEBUG
+ printf("ak8973_write IDX = 0x%x, Data = 0x%x\n", index, val);
+#endif
+
+ switch (index) {
+ case AK8973_MS1:
+ if ((val & 3) == 2) {
+ /* EEPROM access mode */
+ /* TODO: implement this mode */
+ s->ms1 = val;
+ } else {
+ /* All other mode setting finishes in power-down mode */
+ s->ms1 |= 3;
+ }
+ if ((val & 3) == 0) {
+ /* Measurement mode */
+ qemu_irq_raise(s->irq);
+ s->status |= 1;
+ /* TODO: change measurement registers accordingly */
+ }
+ break;
+ case AK8973_HXDA:
+ case AK8973_HYDA:
+ case AK8973_HZDA:
+ /* TODO: implement DAC changes influence on reported data */
+ s->dac[index - AK8973_HXDA] = val;
+ break;
+ case AK8973_HXGA:
+ case AK8973_HYGA:
+ case AK8973_HZGA:
+ /* TODO: implement gain changes influence on reported data */
+ s->gain[index - AK8973_HXGA] = val;
+ break;
+ case AK8973_ETS:
+ case AK8973_EVIR:
+ case AK8973_EIHE:
+ case AK8973_ETST:
+ case AK8973_EHXGA:
+ case AK8973_EHYGA:
+ case AK8973_EHZGA:
+ case AK8973_WRAL1:
+ if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) == 0xA8) {
+ /* TODO: implement EEPROM writing */
+ }
+ break;
+ default:
+ hw_error("ak8973: bad write offset 0x%x\n", index);
+ }
+}
+
+static void ak8973_save(QEMUFile *f, void *opaque)
+{
+ struct AK8973State *s = (struct AK8973State *)opaque;
+
+ qemu_put_8s(f, &s->status);
+ qemu_put_8s(f, &s->ms1);
+ qemu_put_buffer(f, s->dac, 3);
+ qemu_put_buffer(f, s->gain, 3);
+}
+
+static int ak8973_load(QEMUFile *f, void *opaque, int version_id)
+{
+ struct AK8973State *s = (struct AK8973State *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_8s(f, &s->status);
+ qemu_get_8s(f, &s->ms1);
+ qemu_get_buffer(f, s->dac, 3);
+ qemu_get_buffer(f, s->gain, 3);
+
+ return 0;
+}
+
+void ak8973_update(DeviceState *dev,
+ uint8_t x, uint8_t y, uint8_t z, uint8_t temp)
+{
+ AK8973State *s =
+ FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(dev));
+ s->x = x;
+ s->y = y;
+ s->z = z;
+ s->temp = temp;
+#ifdef DEBUG
+ printf("compass update : %d %d %d %d\n", x, y, z, temp);
+#endif
+}
+
+
+static int ak8973_init(I2CAddressableState *s)
+{
+ AK8973State *t = FROM_I2CADDR_SLAVE(AK8973State, s);
+
+ /* Set irq address */
+ qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+
+ ak8973_reset(&s->i2c.qdev);
+
+ register_savevm(&s->i2c.qdev, "ak8973", -1, 1,
+ ak8973_save, ak8973_load, s);
+
+ return 0;
+}
+
+static I2CAddressableDeviceInfo ak8973_info = {
+ .i2c.qdev.name = "ak8973",
+ .i2c.qdev.size = sizeof(AK8973State),
+ .i2c.qdev.reset = ak8973_reset,
+ .init = ak8973_init,
+ .read = ak8973_read,
+ .write = ak8973_write,
+ .size = 1,
+ .rev = 0
+};
+
+static void ak8973_register_devices(void)
+{
+ i2c_addressable_register_device(&ak8973_info);
+}
+
+device_init(ak8973_register_devices)
--- /dev/null
+/*
+ * AK8973 Compass Emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+void ak8973_update(DeviceState *dev,
+ uint8_t x, uint8_t y, uint8_t z, uint8_t temp);
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;
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);
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);
struct QEMUMachine *next;
} QEMUMachine;
+int qemu_arch_is_arm(void);
int qemu_register_machine(QEMUMachine *m);
extern QEMUMachine *current_machine;
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);
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;
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);
}
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;
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;
}
}
KBD_DPRINTF("Translated keycode %2.2x\n", ch);
put_queue(s, ch | release);
+ return 0;
}
static void handle_kbd_command(ChannelState *s, int val)
"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;
/* 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 */
--- /dev/null
+/* GLES v1 implementation.
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gles2.h"
+#include "EGL/degl.h"
+#include "GLES/gl.h"
+
+#include "gles2_types.h"
+
+
+#define gles1_count_glClipPlanef 4
+#define gles1_count_glClipPlanex 4
+#define gles1_count_glLoadMatrixf 16
+#define gles1_count_glLoadMatrixx 16
+#define gles1_count_glMultMatrixf 16
+#define gles1_count_glMultMatrixx 16
+
+
+// Numbers from c->array's end
+#define gles1_num_glColorPointer 1
+#define gles1_num_glNormalPointer 2
+#define gles1_num_glTexCoordPointer 3
+#define gles1_num_glVertexPointer 4
+
+#define gles1_size_glNormalPointer 3
+
+//for debug only
+void checkGLESError(void)
+{
+ GLenum error;
+ if ((error = glGetError()) != 0) {
+ GLES2_PRINT("Error after call 0x%x!\n", error);
+ }
+ else {
+ GLES2_PRINT("Ok after call!\n");
+ }
+}
+
+static void gles1_apply_glColorPointer(gles2_Array *va)
+{
+ glColorPointer(va->size, va->type,
+ va->real_stride, va->ptr);
+ GLenum error;
+
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ GLES2_PRINT("glColorPointer(%d, 0x%x, %d, %p\n)"
+ " failed with 0x%x!\n", va->size, va->type,
+ va->stride, va->ptr, error);
+ }
+}
+
+static void gles1_apply_glNormalPointer(gles2_Array *va)
+{
+ glNormalPointer(va->type, va->real_stride, va->ptr);
+ GLenum error;
+
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ GLES2_PRINT("glNormalPointer(0x%x, %d, %p\n)"
+ " failed with 0x%x!\n", va->type,
+ va->stride, va->ptr, error);
+ }
+}
+
+static void gles1_apply_glTexCoordPointer(gles2_Array *va)
+{
+ glTexCoordPointer(va->size, va->type,
+ va->real_stride, va->ptr);
+ GLenum error;
+
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ GLES2_PRINT("glTexCoordPointer(%d, 0x%x, %d, %p\n)"
+ " failed with 0x%x!\n", va->size, va->type,
+ va->stride, va->ptr, error);
+ }
+}
+
+static void gles1_apply_glVertexPointer(gles2_Array *va)
+{
+ glVertexPointer(va->size, va->type,
+ va->real_stride, va->ptr);
+ GLenum error;
+
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ GLES2_PRINT("glVertexPointer(%d, 0x%x, %d, %p\n)"
+ " failed with 0x%x!\n", va->size, va->type,
+ va->stride, va->ptr, error);
+ }
+}
+
+
+unsigned gles1_glGetCount(TGLenum pname)
+{
+ unsigned count;
+ switch(pname) {
+ case GL_MAX_TEXTURE_UNITS:
+
+ case GL_ALPHA_BITS:
+ case GL_ALPHA_SCALE:
+ case GL_ALPHA_TEST:
+ case GL_ALPHA_TEST_FUNC:
+ case GL_ALPHA_TEST_REF:
+ case GL_BLEND:
+ case GL_BLEND_DST:
+ case GL_BLEND_SRC:
+ case GL_BLUE_BITS:
+ case GL_CLIP_PLANE0:
+ case GL_CLIP_PLANE1:
+ case GL_CLIP_PLANE2:
+ case GL_CLIP_PLANE3:
+ case GL_CLIP_PLANE4:
+ case GL_CLIP_PLANE5:
+ case GL_COLOR_MATERIAL:
+ case GL_CULL_FACE:
+ case GL_CULL_FACE_MODE:
+ case GL_DEPTH_BITS:
+ case GL_DEPTH_CLEAR_VALUE:
+ case GL_DEPTH_FUNC:
+ case GL_DEPTH_TEST:
+ case GL_DEPTH_WRITEMASK:
+ case GL_DITHER:
+ case GL_FOG:
+ case GL_FOG_DENSITY:
+ case GL_FOG_END:
+ case GL_FOG_HINT:
+ case GL_FOG_MODE:
+ case GL_FOG_START:
+ case GL_FRONT_FACE:
+ case GL_GREEN_BITS:
+ case GL_LIGHT0:
+ case GL_LIGHT1:
+ case GL_LIGHT2:
+ case GL_LIGHT3:
+ case GL_LIGHT4:
+ case GL_LIGHT5:
+ case GL_LIGHT6:
+ case GL_LIGHT7:
+ case GL_LIGHTING:
+ case GL_LIGHT_MODEL_TWO_SIDE:
+ case GL_LINE_SMOOTH:
+ case GL_LINE_SMOOTH_HINT:
+ case GL_LINE_WIDTH:
+ case GL_COLOR_LOGIC_OP:
+ case GL_LOGIC_OP_MODE:
+ case GL_MATRIX_MODE:
+ case GL_MAX_CLIP_PLANES:
+ case GL_MAX_LIGHTS:
+ case GL_MAX_MODELVIEW_STACK_DEPTH:
+ case GL_MAX_PROJECTION_STACK_DEPTH:
+ case GL_MAX_TEXTURE_SIZE:
+ case GL_MAX_TEXTURE_STACK_DEPTH:
+ case GL_MODELVIEW_STACK_DEPTH:
+ case GL_NORMALIZE:
+ case GL_PACK_ALIGNMENT:
+ case GL_PERSPECTIVE_CORRECTION_HINT:
+ case GL_POINT_SIZE:
+ case GL_POINT_SMOOTH:
+ case GL_POINT_SMOOTH_HINT:
+ case GL_POLYGON_OFFSET_FACTOR:
+ case GL_POLYGON_OFFSET_UNITS:
+ case GL_POLYGON_OFFSET_FILL:
+ case GL_PROJECTION_STACK_DEPTH:
+ case GL_RED_BITS:
+ case GL_RESCALE_NORMAL:
+ case GL_SCISSOR_TEST:
+ case GL_SHADE_MODEL:
+ case GL_STENCIL_BITS:
+ case GL_STENCIL_CLEAR_VALUE:
+ case GL_STENCIL_FAIL:
+ case GL_STENCIL_FUNC:
+ case GL_STENCIL_PASS_DEPTH_FAIL:
+ case GL_STENCIL_PASS_DEPTH_PASS:
+ case GL_STENCIL_REF:
+ case GL_STENCIL_TEST:
+ case GL_STENCIL_VALUE_MASK:
+ case GL_STENCIL_WRITEMASK:
+ case GL_SUBPIXEL_BITS:
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_BINDING_2D:
+ case GL_TEXTURE_STACK_DEPTH:
+ case GL_UNPACK_ALIGNMENT:
+ case GL_VERTEX_ARRAY:
+ case GL_VERTEX_ARRAY_SIZE:
+ case GL_VERTEX_ARRAY_TYPE:
+ case GL_VERTEX_ARRAY_STRIDE:
+ case GL_NORMAL_ARRAY:
+ case GL_NORMAL_ARRAY_TYPE:
+ case GL_NORMAL_ARRAY_STRIDE:
+ case GL_COLOR_ARRAY:
+ case GL_COLOR_ARRAY_SIZE:
+ case GL_COLOR_ARRAY_TYPE:
+ case GL_COLOR_ARRAY_STRIDE:
+ case GL_TEXTURE_COORD_ARRAY:
+ case GL_TEXTURE_COORD_ARRAY_SIZE:
+ case GL_TEXTURE_COORD_ARRAY_TYPE:
+ case GL_TEXTURE_COORD_ARRAY_STRIDE:
+ case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
+ case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
+ case GL_SPOT_EXPONENT:
+ case GL_SPOT_CUTOFF:
+ case GL_CONSTANT_ATTENUATION:
+ case GL_LINEAR_ATTENUATION:
+ case GL_QUADRATIC_ATTENUATION:
+ case GL_SHININESS:
+ case GL_TEXTURE_ENV_MODE:
+ case GL_COMBINE_RGB:
+ case GL_COMBINE_ALPHA:
+ case GL_SRC0_RGB:
+ case GL_SRC1_RGB:
+ case GL_SRC2_RGB:
+ case GL_SRC0_ALPHA:
+ case GL_SRC1_ALPHA:
+ case GL_SRC2_ALPHA:
+ case GL_OPERAND0_RGB:
+ case GL_OPERAND1_RGB:
+ case GL_OPERAND2_RGB:
+ case GL_OPERAND0_ALPHA:
+ case GL_OPERAND1_ALPHA:
+ case GL_OPERAND2_ALPHA:
+ case GL_RGB_SCALE:
+ case GL_POINT_SIZE_MIN:
+ case GL_POINT_SIZE_MAX:
+ case GL_POINT_FADE_THRESHOLD_SIZE:
+ count = 1;
+ break;
+
+ case GL_DEPTH_RANGE:
+ case GL_ALIASED_LINE_WIDTH_RANGE:
+ case GL_MAX_VIEWPORT_DIMS:
+ case GL_ALIASED_POINT_SIZE_RANGE:
+ count = 2;
+ break;
+
+ case GL_CURRENT_NORMAL:
+ case GL_SPOT_DIRECTION:
+ case GL_POINT_DISTANCE_ATTENUATION:
+ count = 3;
+ break;
+
+ case GL_COLOR_CLEAR_VALUE:
+ case GL_COLOR_WRITEMASK:
+ case GL_CURRENT_COLOR:
+ case GL_CURRENT_TEXTURE_COORDS:
+ case GL_FOG_COLOR:
+ case GL_LIGHT_MODEL_AMBIENT:
+ case GL_SCISSOR_BOX:
+ case GL_VIEWPORT:
+ case GL_AMBIENT:
+ case GL_DIFFUSE:
+ case GL_SPECULAR:
+ case GL_EMISSION:
+ case GL_TEXTURE_ENV_COLOR:
+ case GL_POSITION:
+ case GL_AMBIENT_AND_DIFFUSE:
+ count = 4;
+ break;
+
+
+ case GL_MODELVIEW_MATRIX:
+ case GL_PROJECTION_MATRIX:
+ case GL_TEXTURE_MATRIX:
+ count = 16;
+ break;
+
+ default:
+ GLES2_PRINT("ERROR: Unknown pname 0x%x in glGet!\n", pname);
+ //exit(0);
+ count = 1;
+ break;
+ }
+
+ GLES2_PRINT("glGet(0x%x) -> %u!\n", pname, count);
+
+ return count;
+}
+
+
+GLES2_CB(glGetPointerv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+
+ Tptr res = 0;
+
+ switch (pname)
+ {
+ case GL_COLOR_ARRAY_POINTER:
+ res = c->arrays[c->narrays - gles1_num_glColorPointer].tptr;
+ break;
+ case GL_NORMAL_ARRAY_POINTER:
+ res = c->arrays[c->narrays - gles1_num_glNormalPointer].tptr;
+ break;
+ case GL_TEXTURE_COORD_ARRAY_POINTER:
+ res = c->arrays[c->narrays - gles1_num_glTexCoordPointer].tptr;
+ break;
+ case GL_VERTEX_ARRAY_POINTER:
+ res = c->arrays[c->narrays - gles1_num_glVertexPointer].tptr;
+ break;
+
+ default:
+ GLES2_PRINT("ERROR: Unknown pname 0x%x in glGetPointerv!\n", pname);
+ //exit(0);
+ break;
+ }
+
+ GLES2_BARRIER_RET;
+ gles2_put_Tptr(s, paramsp, res);
+}
+
+GLES2_CB(glEnableClientState)
+{
+ GLES2_ARG(TGLenum, array);
+ GLES2_BARRIER_ARG_NORET;
+
+ switch (array)
+ {
+ case GL_COLOR_ARRAY:
+ c->arrays[c->narrays - gles1_num_glColorPointer].enabled = 1;
+ break;
+ case GL_NORMAL_ARRAY:
+ c->arrays[c->narrays - gles1_num_glNormalPointer].enabled = 1;
+ break;
+ case GL_TEXTURE_COORD_ARRAY:
+ c->arrays[c->narrays - gles1_num_glTexCoordPointer].enabled = 1;
+ break;
+ case GL_VERTEX_ARRAY:
+ c->arrays[c->narrays - gles1_num_glVertexPointer].enabled = 1;
+ break;
+
+ default:
+ GLES2_PRINT("ERROR: Unknown array 0x%x in glDisableClientState!\n", array);
+ exit(0);
+ break;
+ }
+
+ glEnableClientState(array);
+}
+
+GLES2_CB(glDisableClientState)
+{
+ GLES2_ARG(TGLenum, array);
+ GLES2_BARRIER_ARG_NORET;
+
+ switch (array)
+ {
+ case GL_COLOR_ARRAY:
+ c->arrays[c->narrays - gles1_num_glColorPointer].enabled = 0;
+ break;
+ case GL_NORMAL_ARRAY:
+ c->arrays[c->narrays - gles1_num_glNormalPointer].enabled = 0;
+ break;
+ case GL_TEXTURE_COORD_ARRAY:
+ c->arrays[c->narrays - gles1_num_glTexCoordPointer].enabled = 0;
+ break;
+ case GL_VERTEX_ARRAY:
+ c->arrays[c->narrays - gles1_num_glVertexPointer].enabled = 0;
+ break;
+
+ default:
+ GLES2_PRINT("ERROR: Unknown array 0x%x in glDisableClientState!\n", array);
+ exit(0);
+ break;
+ }
+
+ glDisableClientState(array);
+}
+
+
+//***************** Has been generated! *******************
+
+GLES2_CB(glAlphaFunc)
+{
+ GLES2_ARG(TGLenum, func);
+ GLES2_ARG(TGLclampf, ref);
+ GLES2_BARRIER_ARG_NORET;
+
+ glAlphaFunc(func, ref);
+}
+
+GLES2_CB(glClipPlanef)
+{
+ GLES2_ARG(TGLenum, plane);
+ GLES2_ARG(Tptr, equationp);
+ unsigned count = gles1_count_glClipPlanef;
+ GLfloat equation [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ equation[i] = gles2_get_TGLfloat(s, equationp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glClipPlanef(plane, equation);
+}
+
+GLES2_CB(glColor4f)
+{
+ GLES2_ARG(TGLfloat, red);
+ GLES2_ARG(TGLfloat, green);
+ GLES2_ARG(TGLfloat, blue);
+ GLES2_ARG(TGLfloat, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glColor4f(red, green, blue, alpha);
+}
+
+GLES2_CB(glFogf)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glFogf(pname, param);
+}
+
+GLES2_CB(glFogfv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glFogfv(pname, params);
+}
+
+GLES2_CB(glFrustumf)
+{
+ GLES2_ARG(TGLfloat, left);
+ GLES2_ARG(TGLfloat, right);
+ GLES2_ARG(TGLfloat, bottom);
+ GLES2_ARG(TGLfloat, top);
+ GLES2_ARG(TGLfloat, zNear);
+ GLES2_ARG(TGLfloat, zFar);
+ GLES2_BARRIER_ARG_NORET;
+
+ glFrustumf(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glGetClipPlanef)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, eqnp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat eqn [16];
+
+ glGetClipPlanef(pname, eqn);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, eqnp + i*sizeof(TGLfloat), eqn[i]);
+ }
+}
+
+GLES2_CB(glGetLightfv)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+
+ glGetLightfv(light, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetMaterialfv)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+
+ glGetMaterialfv(face, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetTexEnvfv)
+{
+ GLES2_ARG(TGLenum, env);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+
+ glGetTexEnvfv(env, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glLightModelf)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightModelf(pname, param);
+}
+
+GLES2_CB(glLightModelfv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightModelfv(pname, params);
+}
+
+GLES2_CB(glLightf)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightf(light, pname, param);
+}
+
+GLES2_CB(glLightfv)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightfv(light, pname, params);
+}
+
+GLES2_CB(glLoadMatrixf)
+{
+ GLES2_ARG(Tptr, mp);
+ unsigned count = gles1_count_glLoadMatrixf;
+ GLfloat m [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ m[i] = gles2_get_TGLfloat(s, mp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLoadMatrixf(m);
+}
+
+GLES2_CB(glMaterialf)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glMaterialf(face, pname, param);
+}
+
+GLES2_CB(glMaterialfv)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glMaterialfv(face, pname, params);
+}
+
+GLES2_CB(glMultMatrixf)
+{
+ GLES2_ARG(Tptr, mp);
+ unsigned count = gles1_count_glMultMatrixf;
+ GLfloat m [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ m[i] = gles2_get_TGLfloat(s, mp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glMultMatrixf(m);
+}
+
+GLES2_CB(glMultiTexCoord4f)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLfloat, ps);
+ GLES2_ARG(TGLfloat, t);
+ GLES2_ARG(TGLfloat, r);
+ GLES2_ARG(TGLfloat, q);
+ GLES2_BARRIER_ARG_NORET;
+
+ glMultiTexCoord4f(target, ps, t, r, q);
+}
+
+GLES2_CB(glNormal3f)
+{
+ GLES2_ARG(TGLfloat, nx);
+ GLES2_ARG(TGLfloat, ny);
+ GLES2_ARG(TGLfloat, nz);
+ GLES2_BARRIER_ARG_NORET;
+
+ glNormal3f(nx, ny, nz);
+}
+
+GLES2_CB(glOrthof)
+{
+ GLES2_ARG(TGLfloat, left);
+ GLES2_ARG(TGLfloat, right);
+ GLES2_ARG(TGLfloat, bottom);
+ GLES2_ARG(TGLfloat, top);
+ GLES2_ARG(TGLfloat, zNear);
+ GLES2_ARG(TGLfloat, zFar);
+ GLES2_BARRIER_ARG_NORET;
+
+ glOrthof(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glPointParameterf)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointParameterf(pname, param);
+}
+
+GLES2_CB(glPointParameterfv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointParameterfv(pname, params);
+}
+
+GLES2_CB(glPointSize)
+{
+ GLES2_ARG(TGLfloat, size);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointSize(size);
+}
+
+GLES2_CB(glRotatef)
+{
+ GLES2_ARG(TGLfloat, angle);
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_ARG(TGLfloat, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glRotatef(angle, x, y, z);
+}
+
+GLES2_CB(glScalef)
+{
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_ARG(TGLfloat, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glScalef(x, y, z);
+}
+
+GLES2_CB(glTexEnvf)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnvf(target, pname, param);
+}
+
+GLES2_CB(glTexEnvfv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfloat params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnvfv(target, pname, params);
+}
+
+GLES2_CB(glTranslatef)
+{
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_ARG(TGLfloat, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTranslatef(x, y, z);
+}
+
+GLES2_CB(glAlphaFuncx)
+{
+ GLES2_ARG(TGLenum, func);
+ GLES2_ARG(TGLclampx, ref);
+ GLES2_BARRIER_ARG_NORET;
+
+ glAlphaFuncx(func, ref);
+}
+
+GLES2_CB(glClearColorx)
+{
+ GLES2_ARG(TGLclampx, red);
+ GLES2_ARG(TGLclampx, green);
+ GLES2_ARG(TGLclampx, blue);
+ GLES2_ARG(TGLclampx, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glClearColorx(red, green, blue, alpha);
+}
+
+GLES2_CB(glClearDepthx)
+{
+ GLES2_ARG(TGLclampx, depth);
+ GLES2_BARRIER_ARG_NORET;
+
+ glClearDepthx(depth);
+}
+
+GLES2_CB(glClientActiveTexture)
+{
+ GLES2_ARG(TGLenum, texture);
+ GLES2_BARRIER_ARG_NORET;
+
+ glClientActiveTexture(texture);
+}
+
+GLES2_CB(glClipPlanex)
+{
+ GLES2_ARG(TGLenum, plane);
+ GLES2_ARG(Tptr, equationp);
+ unsigned count = gles1_count_glClipPlanex;
+ GLfixed equation [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ equation[i] = gles2_get_TGLfixed(s, equationp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glClipPlanex(plane, equation);
+}
+
+GLES2_CB(glColor4ub)
+{
+ GLES2_ARG(TGLubyte, red);
+ GLES2_ARG(TGLubyte, green);
+ GLES2_ARG(TGLubyte, blue);
+ GLES2_ARG(TGLubyte, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glColor4ub(red, green, blue, alpha);
+}
+
+GLES2_CB(glColor4x)
+{
+ GLES2_ARG(TGLfixed, red);
+ GLES2_ARG(TGLfixed, green);
+ GLES2_ARG(TGLfixed, blue);
+ GLES2_ARG(TGLfixed, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glColor4x(red, green, blue, alpha);
+}
+
+GLES2_CB(glColorPointer)
+{
+ GLES2_ARG(TGLint, size);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(TGLsizei, stride);
+ GLES2_ARG(Tptr, pointerp);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Array glColorPointer at 0x%x\n", pointerp);
+
+ gles2_Array *va = c->arrays + (c->narrays - gles1_num_glColorPointer);
+ va->size = size;
+ va->type = type;
+ va->stride = stride;
+ va->tptr = pointerp;
+ va->apply = gles1_apply_glColorPointer;
+ va->enabled = 1;
+}
+
+GLES2_CB(glDepthRangex)
+{
+ GLES2_ARG(TGLclampx, zNear);
+ GLES2_ARG(TGLclampx, zFar);
+ GLES2_BARRIER_ARG_NORET;
+
+ glDepthRangex(zNear, zFar);
+}
+
+GLES2_CB(glFogx)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glFogx(pname, param);
+}
+
+GLES2_CB(glFogxv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glFogxv(pname, params);
+}
+
+GLES2_CB(glFrustumx)
+{
+ GLES2_ARG(TGLfixed, left);
+ GLES2_ARG(TGLfixed, right);
+ GLES2_ARG(TGLfixed, bottom);
+ GLES2_ARG(TGLfixed, top);
+ GLES2_ARG(TGLfixed, zNear);
+ GLES2_ARG(TGLfixed, zFar);
+ GLES2_BARRIER_ARG_NORET;
+
+ glFrustumx(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glGetClipPlanex)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, eqnp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed eqn [16];
+
+ glGetClipPlanex(pname, eqn);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, eqnp + i*sizeof(TGLfixed), eqn[i]);
+ }
+}
+
+GLES2_CB(glGetFixedv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+
+ glGetFixedv(pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+ }
+}
+
+GLES2_CB(glGetLightxv)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+
+ glGetLightxv(light, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+ }
+}
+
+GLES2_CB(glGetMaterialxv)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+
+ glGetMaterialxv(face, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+ }
+}
+
+GLES2_CB(glGetTexEnviv)
+{
+ GLES2_ARG(TGLenum, env);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLint params [16];
+
+ glGetTexEnviv(env, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+ }
+}
+
+GLES2_CB(glGetTexEnvxv)
+{
+ GLES2_ARG(TGLenum, env);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+
+ glGetTexEnvxv(env, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+ }
+}
+
+GLES2_CB(glGetTexParameterxv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+
+ glGetTexParameterxv(target, pname, params);
+ GLES2_BARRIER_RET;
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ gles2_put_TGLfixed(s, paramsp + i*sizeof(TGLfixed), params[i]);
+ }
+}
+
+GLES2_CB(glLightModelx)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightModelx(pname, param);
+}
+
+GLES2_CB(glLightModelxv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightModelxv(pname, params);
+}
+
+GLES2_CB(glLightx)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightx(light, pname, param);
+}
+
+GLES2_CB(glLightxv)
+{
+ GLES2_ARG(TGLenum, light);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLightxv(light, pname, params);
+}
+
+GLES2_CB(glLineWidthx)
+{
+ GLES2_ARG(TGLfixed, width);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLineWidthx(width);
+}
+
+GLES2_CB(glLoadIdentity)
+{
+ GLES2_BARRIER_ARG_NORET;
+
+ glLoadIdentity();
+}
+
+GLES2_CB(glLoadMatrixx)
+{
+ GLES2_ARG(Tptr, mp);
+ unsigned count = gles1_count_glLoadMatrixx;
+ GLfixed m [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ m[i] = gles2_get_TGLfixed(s, mp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glLoadMatrixx(m);
+}
+
+GLES2_CB(glLogicOp)
+{
+ GLES2_ARG(TGLenum, opcode);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLogicOp(opcode);
+}
+
+GLES2_CB(glMaterialx)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glMaterialx(face, pname, param);
+}
+
+GLES2_CB(glMaterialxv)
+{
+ GLES2_ARG(TGLenum, face);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glMaterialxv(face, pname, params);
+}
+
+GLES2_CB(glMatrixMode)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_BARRIER_ARG_NORET;
+
+ glMatrixMode(mode);
+}
+
+GLES2_CB(glMultMatrixx)
+{
+ GLES2_ARG(Tptr, mp);
+ unsigned count = gles1_count_glMultMatrixx;
+ GLfixed m [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ m[i] = gles2_get_TGLfixed(s, mp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glMultMatrixx(m);
+}
+
+GLES2_CB(glMultiTexCoord4x)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLfixed, ps);
+ GLES2_ARG(TGLfixed, t);
+ GLES2_ARG(TGLfixed, r);
+ GLES2_ARG(TGLfixed, q);
+ GLES2_BARRIER_ARG_NORET;
+
+ glMultiTexCoord4x(target, ps, t, r, q);
+}
+
+GLES2_CB(glNormal3x)
+{
+ GLES2_ARG(TGLfixed, nx);
+ GLES2_ARG(TGLfixed, ny);
+ GLES2_ARG(TGLfixed, nz);
+ GLES2_BARRIER_ARG_NORET;
+
+ glNormal3x(nx, ny, nz);
+}
+
+GLES2_CB(glNormalPointer)
+{
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(TGLsizei, stride);
+ GLES2_ARG(Tptr, pointerp);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Array glNormalPointer at 0x%x\n", pointerp);
+
+ gles2_Array *va = c->arrays + (c->narrays - gles1_num_glNormalPointer);
+ va->type = type;
+ va->stride = stride;
+ va->tptr = pointerp;
+ va->size = gles1_size_glNormalPointer;
+ va->apply = gles1_apply_glNormalPointer;
+ va->enabled = 1;
+}
+
+GLES2_CB(glOrthox)
+{
+ GLES2_ARG(TGLfixed, left);
+ GLES2_ARG(TGLfixed, right);
+ GLES2_ARG(TGLfixed, bottom);
+ GLES2_ARG(TGLfixed, top);
+ GLES2_ARG(TGLfixed, zNear);
+ GLES2_ARG(TGLfixed, zFar);
+ GLES2_BARRIER_ARG_NORET;
+
+ glOrthox(left, right, bottom, top, zNear, zFar);
+}
+
+GLES2_CB(glPointParameterx)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointParameterx(pname, param);
+}
+
+GLES2_CB(glPointParameterxv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointParameterxv(pname, params);
+}
+
+GLES2_CB(glPointSizex)
+{
+ GLES2_ARG(TGLfixed, size);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPointSizex(size);
+}
+
+GLES2_CB(glPolygonOffsetx)
+{
+ GLES2_ARG(TGLfixed, factor);
+ GLES2_ARG(TGLfixed, units);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPolygonOffsetx(factor, units);
+}
+
+GLES2_CB(glPopMatrix)
+{
+ GLES2_BARRIER_ARG_NORET;
+
+ glPopMatrix();
+}
+
+GLES2_CB(glPushMatrix)
+{
+ GLES2_BARRIER_ARG_NORET;
+
+ glPushMatrix();
+}
+
+GLES2_CB(glRotatex)
+{
+ GLES2_ARG(TGLfixed, angle);
+ GLES2_ARG(TGLfixed, x);
+ GLES2_ARG(TGLfixed, y);
+ GLES2_ARG(TGLfixed, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glRotatex(angle, x, y, z);
+}
+
+GLES2_CB(glSampleCoveragex)
+{
+ GLES2_ARG(TGLclampx, value);
+ GLES2_ARG(TGLboolean, invert);
+ GLES2_BARRIER_ARG_NORET;
+
+ glSampleCoveragex(value, invert);
+}
+
+GLES2_CB(glScalex)
+{
+ GLES2_ARG(TGLfixed, x);
+ GLES2_ARG(TGLfixed, y);
+ GLES2_ARG(TGLfixed, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glScalex(x, y, z);
+}
+
+GLES2_CB(glShadeModel)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_BARRIER_ARG_NORET;
+
+ glShadeModel(mode);
+}
+
+GLES2_CB(glTexCoordPointer)
+{
+ GLES2_ARG(TGLint, size);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(TGLsizei, stride);
+ GLES2_ARG(Tptr, pointerp);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Array glTexCoordPointer at 0x%x\n", pointerp);
+
+ gles2_Array *va = c->arrays + (c->narrays - gles1_num_glTexCoordPointer);
+ va->size = size;
+ va->type = type;
+ va->stride = stride;
+ va->tptr = pointerp;
+ va->apply = gles1_apply_glTexCoordPointer;
+ va->enabled = 1;
+}
+
+GLES2_CB(glTexEnvi)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLint, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnvi(target, pname, param);
+}
+
+GLES2_CB(glTexEnvx)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnvx(target, pname, param);
+}
+
+GLES2_CB(glTexEnviv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLint params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLint(s, paramsp + i*sizeof(TGLint));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnviv(target, pname, params);
+}
+
+GLES2_CB(glTexEnvxv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexEnvxv(target, pname, params);
+}
+
+GLES2_CB(glTexParameterx)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfixed, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexParameterx(target, pname, param);
+}
+
+GLES2_CB(glTexParameterxv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ unsigned count = gles1_glGetCount(pname);
+ GLfixed params [16];
+ unsigned i = 0;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfixed(s, paramsp + i*sizeof(TGLfixed));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexParameterxv(target, pname, params);
+}
+
+GLES2_CB(glTranslatex)
+{
+ GLES2_ARG(TGLfixed, x);
+ GLES2_ARG(TGLfixed, y);
+ GLES2_ARG(TGLfixed, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTranslatex(x, y, z);
+}
+
+GLES2_CB(glVertexPointer)
+{
+ GLES2_ARG(TGLint, size);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(TGLsizei, stride);
+ GLES2_ARG(Tptr, pointerp);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Array glVertexPointer at 0x%x\n", pointerp);
+
+ gles2_Array *va = c->arrays + (c->narrays - gles1_num_glVertexPointer);
+ va->size = size;
+ va->type = type;
+ va->stride = stride;
+ va->tptr = pointerp;
+ va->apply = gles1_apply_glVertexPointer;
+ va->enabled = 1;
+}
--- /dev/null
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gles2.h"
+#include <pthread.h>
+
+// From target-arm/helper.c.
+extern int get_phys_addr(CPUState *env, uint32_t address,
+ int access_type, int is_user,
+ uint32_t *phys_ptr, int *prot,
+ target_ulong *page_size);
+
+
+// List of calls to used by the kernel module (page 0).
+static gles2_Call const gles2_kcalls[];
+// List of calls used by clients (page 1->15).
+static gles2_Call const gles2_calls[];
+
+// Translate a target virtual address to physical address.
+static target_ulong gles2_pa(gles2_State *s, target_ulong va,
+ int access_type)
+{
+ target_ulong pa, ps;
+ int prot;
+
+ if (get_phys_addr(s->env, va, access_type, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Page fault on guest!\n");
+ return 0;
+ }
+
+ return pa;
+}
+
+int gles2_transfer_compile(gles2_CompiledTransfer* tfr, gles2_State *s,
+ target_ulong va, target_ulong len)
+{
+ tfr->nsections = 0;
+ tfr->sections = 0;
+
+#if (GLES2_DEBUG == 1)
+ target_ulong first_page = TARGET_PAGE(va);
+#endif // GLES2_DEBUG == 1
+
+ target_ulong last_page = TARGET_PAGE(va + len - 1);
+ target_ulong start_addr = va;
+
+ GLES2_PRINT("DEBUG: Compiling transfer of %d bytes at 0x%x (0x%x->0x%x).\n",
+ len, va, first_page, last_page);
+
+ // Loop through the pages.
+ while (len) {
+ target_ulong start_page = TARGET_PAGE(start_addr);
+ target_ulong start_pa = gles2_pa(s, start_page, 0);
+ target_ulong end_pa = start_pa;
+
+ // Solve length of continuous section.
+ target_ulong end_page = start_page;
+ while(end_page < last_page) {
+ target_ulong next_page = end_page + TARGET_PAGE_SIZE;
+ target_ulong next_pa = gles2_pa(s, next_page, 0);
+
+ // If the target pages are not linearly spaced, stop..
+ if((next_pa < start_pa) ||
+ (next_pa - start_pa > next_page - start_page)) {
+ break;
+ }
+
+ end_page = next_page;
+ end_pa = next_pa;
+ }
+
+ unsigned id = tfr->nsections++;
+
+ GLES2_PRINT("\tContinuous from 0x%x to 0x%x (0x%x to 0x%x) #%d.\n",
+ start_page, end_page, start_pa, end_pa, id);
+ tfr->sections = realloc(tfr->sections,
+ tfr->nsections*sizeof(*(tfr->sections)));
+
+ target_phys_addr_t pages_len = end_page + TARGET_PAGE_SIZE - start_page;
+ void* target_pages = cpu_physical_memory_map(start_pa, &pages_len, 0);
+
+ if (!target_pages || !pages_len) {
+ fprintf(stderr, "ERROR: Failed to map memory to host!\n");
+ return 0;
+ }
+
+ target_ulong section_len = end_page + TARGET_PAGE_SIZE - start_addr;
+
+ if (section_len > len) {
+ section_len = len;
+ }
+
+ target_ulong offset = TARGET_OFFSET(start_addr);
+ void* target_data = target_pages + offset;
+
+ GLES2_PRINT("\tSlice of %d bytes at %p (offset = %x).\n",
+ section_len, target_data, offset);
+
+ tfr->sections[id].base = target_data;
+ tfr->sections[id].len = section_len;
+
+ cpu_physical_memory_unmap(target_pages, pages_len, 0, pages_len);
+ len -= section_len;
+ start_addr += section_len;
+ GLES2_PRINT("\t%d bytes remain...\n", len);
+ }
+ return 1;
+}
+
+void gles2_transfer_exec(gles2_CompiledTransfer* tfr, gles2_State *s,
+ void* data, int access_type)
+{
+ unsigned i;
+
+ for (i = 0; i < tfr->nsections; ++i) {
+ void* target_data = tfr->sections[i].base;
+ target_ulong len = tfr->sections[i].len;
+ if (access_type == 0) {
+ memcpy(data, target_data, len);
+ } else {
+ memcpy(target_data, data, len);
+ }
+ data += len;
+ }
+}
+
+void gles2_transfer_free(gles2_CompiledTransfer* tfr)
+{
+ free(tfr->sections);
+ tfr->sections = 0;
+ tfr->nsections = 0;
+}
+
+int gles2_transfer(gles2_State *s, target_ulong va, target_ulong len,
+ void* data, int access_type)
+{
+#if (GLES2_DEBUG == 1)
+ target_ulong first_page = TARGET_PAGE(va);
+#endif // GLES2_DEBUG == 1
+
+ target_ulong last_page = TARGET_PAGE(va + len - 1);
+ target_ulong start_addr = va;
+
+ GLES2_PRINT("DEBUG: Request transfer of %d bytes at 0x%x (0x%x->0x%x) (access=%d).\n",
+ len, va, first_page, last_page, access_type);
+
+ // Loop through the pages.
+ while (len) {
+ target_ulong start_page = TARGET_PAGE(start_addr);
+ target_ulong start_pa = gles2_pa(s, start_page, access_type);
+ target_ulong end_pa = start_pa;
+
+ // Solve length of continuous section.
+ target_ulong end_page = start_page;
+ while(end_page < last_page) {
+ target_ulong next_page = end_page + TARGET_PAGE_SIZE;
+ target_ulong next_pa = gles2_pa(s, next_page, access_type);
+
+ // If the target pages are not linearly spaced, stop..
+ if ((next_pa < start_pa) ||
+ (next_pa - start_pa > next_page - start_page)) {
+ break;
+ }
+
+ end_page = next_page;
+ end_pa = next_pa;
+ }
+
+ GLES2_PRINT("\tContinuous from 0x%x to 0x%x (0x%x to 0x%x).\n",
+ start_page, end_page, start_pa, end_pa);
+
+ target_phys_addr_t pages_len = end_page + TARGET_PAGE_SIZE - start_page;
+ void* target_pages = cpu_physical_memory_map(start_pa, &pages_len, access_type);
+ if (!target_pages || !pages_len) {
+ GLES2_PRINT("ERROR: Failed to map memory to host!\n");
+ return 0;
+ }
+
+ target_ulong section_len = end_page + TARGET_PAGE_SIZE - start_addr;
+ target_ulong offset = TARGET_OFFSET(start_addr);
+ void* target_data = target_pages + offset;
+
+ if (section_len > len) {
+ section_len = len;
+ }
+
+ GLES2_PRINT("\tTransfering %d bytes at %p (offset = %x).\n",
+ section_len, target_data, offset);
+
+ if (access_type == 0) {
+ memcpy(data, target_data, section_len);
+ } else {
+ memcpy(target_data, data, section_len);
+ }
+
+ cpu_physical_memory_unmap(target_pages, pages_len, access_type, pages_len);
+ len -= section_len;
+ start_addr += section_len;
+ data += section_len;
+
+ GLES2_PRINT("\t%d bytes remain...\n", len);
+ }
+ return 1;
+}
+
+void gles2_put_byte(gles2_State *s, target_ulong va, uint8_t byte)
+{
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return;
+ }
+
+ GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", byte, va, pa);
+ stb_phys(pa, byte);
+}
+
+uint8_t gles2_get_byte(gles2_State *s, target_ulong va)
+{
+ uint8_t byte;
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return 0xDE;
+ }
+
+ byte = ldub_phys(pa);
+
+ //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", byte, va, pa);
+
+ return byte;
+}
+
+void gles2_put_word(gles2_State *s, target_ulong va, uint16_t word)
+{
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return;
+ }
+
+ GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", word, va, pa);
+ stw_phys(pa, word);
+}
+
+uint16_t gles2_get_word(gles2_State *s, target_ulong va)
+{
+ uint16_t word;
+
+ int prot;
+ target_ulong pa, ps;
+ if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return 0xDEAD;
+ }
+
+ word = lduw_phys(pa);
+
+ //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", word, va, pa);
+
+ return word;
+}
+
+uint32_t gles2_get_dword(gles2_State *s, target_ulong va)
+{
+ uint32_t dword;
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return 0xDEAD000;
+ }
+
+ dword = ldl_phys(pa);
+
+ //GLES2_PRINT("DEBUG: Read 0x%x from 0x%x(0x%x)\n", dword, va, pa);
+
+ return dword;
+}
+
+void gles2_put_dword(gles2_State *s, target_ulong va, uint32_t dword)
+{
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return;
+ }
+
+ GLES2_PRINT("DEBUG: Written 0x%x to 0x%x(0x%x)\n", dword, va, pa);
+ stl_phys(pa, dword);
+}
+
+float gles2_get_float(gles2_State *s, target_ulong va)
+{
+ float flt;
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 0, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return -123456789.f;
+ }
+
+ cpu_physical_memory_read(pa, (unsigned char*)&flt, 4);
+ //flt = ldfl_p(pa);
+
+ //GLES2_PRINT("DEBUG: Read %f from 0x%x(0x%x)\n", flt, va, pa);
+
+ return flt;
+}
+
+void gles2_put_float(gles2_State *s, target_ulong va, float flt)
+{
+ int prot;
+ target_ulong pa, ps;
+
+ if (get_phys_addr(s->env, va, 1, 1, &pa, &prot, &ps)) {
+ GLES2_PRINT("ERROR: Memory mapping failed for 0x%x!\n", va);
+ return;
+ }
+
+ GLES2_PRINT("DEBUG: Written %f to 0x%x(0x%x)\n", flt, va, pa);
+ cpu_physical_memory_write(pa, (unsigned char*)&flt, 4);
+ //stfl_p(pa, flt);
+}
+
+uint32_t gles2_handle_create(gles2_State *s, void* data)
+{
+ uint32_t i = 0;
+
+ if (data) {
+ for (i = 0; i < GLES2_NHANDLES; ++i) {
+ if (!s->handles[i]) {
+ break;
+ }
+ }
+
+ if (i == GLES2_NHANDLES) {
+ fprintf(stderr, "ERROR: No free handles!\n");
+ return 0x0;
+ }
+ s->handles[i] = data;
+ i |= GLES2_HANDLE_BASE;
+ }
+
+ GLES2_PRINT("Handle %x created for %p!\n", i, data);
+ return i;
+}
+
+uint32_t gles2_handle_find(gles2_State *s, void* data)
+{
+ uint32_t i = 0;
+
+ if (data) {
+ for(i = 0; i < GLES2_NHANDLES; ++i) {
+ if(s->handles[i] == data) {
+ break;
+ }
+ }
+
+ if (i == GLES2_NHANDLES) {
+// fprintf(stderr, "ERROR: Handle not found!\n");
+ return 0x0;
+ }
+ i |= GLES2_HANDLE_BASE;
+ }
+
+ GLES2_PRINT("Handle %x found for %p!\n", i, data);
+ return i;
+}
+
+void* gles2_handle_get(gles2_State *s, uint32_t i)
+{
+#if(GLES2_DEBUG == 1)
+ if (i && (i & ~GLES2_HANDLE_MASK) != GLES2_HANDLE_BASE) {
+ GLES2_PRINT("ERROR: Invalid handle %x!\n", i);
+ exit(1);
+ }
+#endif // GLES2_DEBUG == 1
+
+ void* data = i ? s->handles[i & GLES2_HANDLE_MASK] : NULL;
+
+ GLES2_PRINT("Reading handle %x => %p\n", i, data);
+
+ return data;
+}
+
+void* gles2_handle_free(gles2_State *s, uint32_t i)
+{
+ void* data = NULL;
+ if (i) {
+ data = s->handles[i & GLES2_HANDLE_MASK];
+ s->handles[i & GLES2_HANDLE_MASK] = NULL;
+ }
+
+ GLES2_PRINT("Freed handle %i => %p\n", i, data);
+ return data;
+}
+
+// Virtual register area write operation handler.
+static void gles2_write(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ gles2_State *s = (gles2_State*)opaque;
+
+ target_ulong page = addr/(4*TARGET_PAGE_SIZE);
+ target_ulong callnr = ((addr) & (4*TARGET_PAGE_SIZE - 1))/0x04;
+
+ GLES2_PRINT("Page %d (0x%x) write (call nr. %d (0x%x)), addr = 0x%x, value = 0x%x.\n", page, page, callnr, callnr, (int32_t)addr, value);
+
+ if (page) {
+ gles2_Call const *call = gles2_calls + callnr;
+
+ if (page > 1) {
+ gles2_Client* client;
+
+ // Client API calls without active context should be ignored.
+ if ((page - 2 > GLES2_NCLIENTS) ||
+ !(client = s->clients[page - 2])) {
+ return;
+ }
+
+ // Make sure nothing is running.
+ GLES2_PRINT("Syncing with worker...\n");
+ pthread_mutex_lock(&client->mutex_wait);
+ while (client->state != gles2_ClientState_ready) {
+ pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+ }
+ pthread_mutex_lock(&client->mutex_run);
+ pthread_mutex_lock(&client->mutex_xcode);
+ client->call = call;
+ client->state = gles2_ClientState_pending;
+ client->phase_xcode = 0;
+ GLES2_PRINT("Requesting call %s...\n", call->name);
+ pthread_cond_signal(&client->cond_start);
+ pthread_mutex_unlock(&client->mutex_wait);
+
+ GLES2_PRINT("Releasing worker for decoding...\n");
+ pthread_mutex_unlock(&client->mutex_run);
+ do {
+ pthread_cond_wait(&client->cond_xcode, &client->mutex_xcode);
+ } while (client->phase_xcode < 1);
+
+ pthread_mutex_unlock(&client->mutex_xcode);
+ GLES2_PRINT("Decoding finished.\n");
+ } else {
+ gles2_decode_t d = 0;
+ GLES2_PRINT("Calling clientless function %s...\n", call->name);
+ call->callback(s, &d, 0);
+ }
+ } else {
+ gles2_Call const *call = gles2_kcalls + callnr;
+ gles2_decode_t d = 0;
+
+ GLES2_PRINT("Calling kernel function %s...\n", call->name);
+
+ call->callback(s, &d, 0);
+ }
+}
+
+// Virtual register area read operation handler.
+static uint32_t gles2_read(void *opaque, target_phys_addr_t addr)
+{
+ gles2_State *s = (gles2_State*)opaque;
+
+ target_ulong page = addr/(4*TARGET_PAGE_SIZE);
+
+ if (page) {
+ gles2_Client* client;
+
+ // Client API calls without active context should be ignored.
+ if ((page - 2 > GLES2_NCLIENTS) ||
+ !(client = s->clients[page - 2])) {
+ return 0;
+ }
+
+ if(pthread_mutex_trylock(&client->mutex_xcode)) {
+ return 1;
+ }
+
+ if(client->phase_xcode == 2) {
+ while (client->phase_xcode < 4) {
+ client->phase_xcode = 3;
+ pthread_cond_signal(&client->cond_return);
+ pthread_cond_wait(&client->cond_xcode, &client->mutex_xcode);
+ }
+ pthread_mutex_unlock(&client->mutex_xcode);
+ return 0;
+ }
+ int ret = (client->phase_xcode == 4 ? 0 : 1);
+ pthread_mutex_unlock(&client->mutex_xcode);
+ return ret;
+ }
+ return 0;
+}
+
+static CPUReadMemoryFunc *gles2_readfn[] = {
+ gles2_read,
+ gles2_read,
+ gles2_read,
+};
+
+static CPUWriteMemoryFunc *gles2_writefn[] = {
+ gles2_write,
+ gles2_write,
+ gles2_write,
+};
+
+// Initializes a new gles2 device.
+void *gles2_init(CPUState *env)
+{
+ gles2_State *s = qemu_malloc(sizeof(*s));
+ unsigned i;
+
+ s->env = env;
+ s->quality = gles2_quality;
+
+ GLES2_PRINT("GLES2 quality: %d\n", s->quality);
+
+ cpu_register_physical_memory(GLES2_HWBASE,
+ GLES2_HWSIZE,
+ cpu_register_io_memory(gles2_readfn,
+ gles2_writefn, s, DEVICE_NATIVE_ENDIAN));
+
+ for (i = 0; i < GLES2_NCLIENTS; ++i) {
+ s->clients[i] = NULL;
+ }
+
+ for (i = 0; i < GLES2_NHANDLES; ++i) {
+ s->handles[i] = NULL;
+ }
+
+ GLES2_PRINT("Registered IO memory!\n");
+ return s;
+}
+
+/******************************************************************************
+ *
+ * Kernel interface functions.
+ *
+ *****************************************************************************/
+
+static void* gles2_client_worker(void *opaque)
+{
+ gles2_Client *client = opaque;
+ int run = 1;
+
+ GLES2_PRINT("WORKER(%d): Starting!\n", client->nr);
+
+ pthread_mutex_lock(&client->mutex_xcode);
+ do
+ {
+ gles2_decode_t d = 0;
+ GLES2_PRINT("WORKER(%d): Waiting for call...\n", client->nr);
+ pthread_mutex_lock(&client->mutex_wait);
+
+ client->state = gles2_ClientState_ready;
+ pthread_cond_signal(&client->cond_state);
+ client->phase_xcode = 4;
+ pthread_cond_signal(&client->cond_xcode);
+ pthread_mutex_unlock(&client->mutex_xcode);
+ while (client->state != gles2_ClientState_pending) {
+ pthread_cond_wait(&client->cond_start, &client->mutex_wait);
+ }
+
+ GLES2_PRINT("WORKER(%d): Got call, waiting permission to run...\n", client->nr);
+
+ pthread_mutex_lock(&client->mutex_run);
+ GLES2_PRINT("WORKER(%d): Running!\n", client->nr);
+ client->state = gles2_ClientState_running;
+ pthread_mutex_unlock(&client->mutex_wait);
+
+ if (client->call) {
+ GLES2_PRINT("WORKER(%d): Calling function %s (%p)...\n",
+ client->nr, client->call->name, client->call);
+ client->call->callback(client->s, &d, client);
+
+ GLES2_PRINT("\tWORKER(%d): Done.\n", client->nr);
+ client->state = gles2_ClientState_done;
+ } else {
+ GLES2_PRINT("WORKER(%d): Exit requested!\n", client->nr);
+ run = 0;
+ client->state = gles2_ClientState_exit;
+ pthread_cond_signal(&client->cond_state);
+ }
+ pthread_mutex_unlock(&client->mutex_run);
+ } while (run);
+
+ GLES2_PRINT("WORKER(%d): Exiting!\n", client->nr);
+ return opaque;
+}
+
+// Called by kernel module when a new client connects.
+static void gles2_init_cb(gles2_State *s, gles2_decode_t *d, gles2_Client *c)
+{
+ unsigned i;
+ gles2_Client *client;
+ pthread_attr_t attr;
+
+ for (i = 0; i < GLES2_NCLIENTS; ++i) {
+ if (!s->clients[i]) {
+ break;
+ }
+ }
+
+ if (i == GLES2_NCLIENTS) {
+ GLES2_PRINT("ERROR: No free slots!\n");
+ gles2_ret_dword(s, 0);
+ return;
+ }
+
+ GLES2_PRINT("Initialization!\n");
+
+ client = malloc(sizeof(*client));
+ client->s = s;
+ client->nr = i + 1;
+ pthread_mutex_init(&client->mutex_wait, NULL);
+ pthread_mutex_init(&client->mutex_run, NULL);
+ pthread_mutex_init(&client->mutex_xcode, NULL);
+ pthread_cond_init(&client->cond_start, NULL);
+ pthread_cond_init(&client->cond_state, NULL);
+ pthread_cond_init(&client->cond_xcode, NULL);
+ pthread_cond_init(&client->cond_return, NULL);
+ client->state = gles2_ClientState_init;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_mutex_lock(&client->mutex_wait);
+
+ GLES2_PRINT("Creating worker...\n");
+ pthread_create(&client->thread, &attr, gles2_client_worker, client);
+
+ do {
+ pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+ } while(client->state != gles2_ClientState_ready);
+ pthread_mutex_unlock(&client->mutex_wait);
+
+ GLES2_PRINT("Worker initialized\n");
+
+ s->clients[i] = client;
+ gles2_ret_dword(s, client->nr);
+}
+
+// Called by kernel module when an existing client disconnects.
+static void gles2_exit_cb(gles2_State *s, gles2_decode_t *d, gles2_Client *c)
+{
+ uint32_t nr = gles2_arg_dword(s, d);
+ gles2_Client *client;
+
+ GLES2_PRINT("Exit called for client %d!\n", nr);
+
+ if ((nr > GLES2_NCLIENTS + 1) ||
+ (nr == 0)) {
+ GLES2_PRINT("Client number out of range!\n");
+ return;
+ } else {
+ client = s->clients[nr - 1];
+ }
+
+ if (!client) {
+ GLES2_PRINT("Can't exit NULL client!\n");
+ return;
+ }
+
+ GLES2_PRINT("\tRequesting worker to exit.\n");
+
+ // Make sure nothing is running.
+ GLES2_PRINT("Syncing with worker...\n");
+ pthread_mutex_lock(&client->mutex_wait);
+ while (client->state != gles2_ClientState_ready) {
+ pthread_cond_wait(&client->cond_state, &client->mutex_wait);
+ }
+ pthread_mutex_lock(&client->mutex_run);
+ client->call = NULL;
+ client->state = gles2_ClientState_pending;
+ GLES2_PRINT("Requesting exit...\n");
+ pthread_cond_signal(&client->cond_start);
+ pthread_mutex_unlock(&client->mutex_wait);
+
+ GLES2_PRINT("Waiting worker to exit...\n");
+ do {
+ pthread_cond_wait(&client->cond_state, &client->mutex_run);
+ } while (client->state != gles2_ClientState_exit);
+ pthread_mutex_unlock(&client->mutex_run);
+
+ GLES2_PRINT("\tJoining...\n");
+ pthread_join(client->thread, NULL);
+ pthread_mutex_destroy(&client->mutex_wait);
+ pthread_mutex_destroy(&client->mutex_run);
+ pthread_cond_destroy(&client->cond_start);
+ pthread_cond_destroy(&client->cond_state);
+
+ free(client);
+ s->clients[nr - 1] = NULL;
+
+ GLES2_PRINT("\tDone!\n");
+}
+
+/******************************************************************************
+ *
+ * Call tables
+ *
+ *****************************************************************************/
+
+/* Make a weak stub for every dummy function. */
+#define CALL_DUMMY(func) \
+ void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c); \
+ void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c) \
+ { \
+ GLES2_BARRIER_ARG_NORET; \
+ fprintf(stderr, "GLES2: DUMMY " #func "\n"); \
+ }
+
+#define CALL_ENTRY(func) \
+ void gles2_##func##_cb(gles2_State *s, gles2_decode_t *d, struct gles2_Client *c);
+
+#include "gles2_calls.h"
+
+#undef CALL_ENTRY
+#undef CALL_DUMMY
+
+#define CALL_ENTRY(func) { #func, gles2_##func##_cb },
+#define CALL_DUMMY(func) { #func, gles2_##func##_cb },
+
+static gles2_Call const gles2_kcalls[] =
+{
+ CALL_ENTRY(init)
+ CALL_ENTRY(exit)
+};
+
+static gles2_Call const gles2_calls[] =
+{
+ { "<<none>>", 0 },
+#include "gles2_calls.h"
+};
+
+#undef CALL_ENTRY
+
--- /dev/null
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GLES2_H__
+#define GLES2_H__
+
+#include "qemu-common.h"
+#include "cpu.h"
+#include <pthread.h>
+
+#define GLES2_HWBASE 0x6f000000
+#define GLES2_HWSIZE 0x00100000
+#define GLES2_NCLIENTS (GLES2_HWSIZE/TARGET_PAGE_SIZE - 2)
+#define GLES2_NHANDLES GLES2_NCLIENTS * 16
+// Address base for host to guest pointer handles.
+#define GLES2_HANDLE_BASE 0xCAFE0000
+#define GLES2_HANDLE_MASK 0x0000FFFF // Handle to index bitmask.
+
+#define GLES2_BARRIER_ARG \
+ if (c) { \
+ pthread_mutex_lock(&c->mutex_xcode); \
+ c->phase_xcode = 1; \
+ pthread_cond_signal(&c->cond_xcode); \
+ pthread_mutex_unlock(&c->mutex_xcode); \
+ GLES2_PRINT("-- ARG BARRIER --\n"); \
+ }
+#define GLES2_BARRIER_ARG_NORET \
+ if (c) { \
+ pthread_mutex_lock(&c->mutex_xcode); \
+ c->phase_xcode = 3; \
+ GLES2_PRINT("-- ARG & RETURN BARRIER --\n"); \
+ }
+#define GLES2_BARRIER_RET \
+ if (c) { \
+ pthread_mutex_lock(&c->mutex_xcode); \
+ c->phase_xcode = 2; \
+ do { \
+ pthread_cond_wait(&c->cond_return, &c->mutex_xcode); \
+ } while (c->phase_xcode == 2); \
+ GLES2_PRINT("-- RETURN BARRIER --\n"); \
+ }
+
+// Round address to lower page boundary.
+#define TARGET_PAGE(addr) ((addr) & ~(TARGET_PAGE_SIZE - 1))
+// Return the page offset part of address.
+#define TARGET_OFFSET(addr) ((addr) & (TARGET_PAGE_SIZE - 1))
+
+//#define GLES2_DEBUG 1
+#define GLES2_DEBUG 0
+#if(GLES2_DEBUG == 1)
+# define GLES2_PRINT(format, args...) \
+ fprintf(stderr, "GLES2: " format, ##args)
+#else
+# define GLES2_PRINT(format, args...) (void)0
+#endif // GLES_DEBUG != 1
+
+struct gles2_Array;
+typedef struct gles2_Array gles2_Array;
+struct gles2_Call;
+typedef struct gles2_Call gles2_Call;
+struct gles2_State;
+typedef struct gles2_State gles2_State;
+
+typedef enum gles2_ClientState
+{
+ gles2_ClientState_init,
+ gles2_ClientState_ready,
+ gles2_ClientState_pending,
+ gles2_ClientState_running,
+ gles2_ClientState_done,
+ gles2_ClientState_exit
+} gles2_ClientState;
+
+// A connected GLES2 client.
+typedef struct gles2_Client
+{
+ gles2_State *s; // Link to the device state the client was connected to.
+ target_ulong nr; // The client ID from kernel.
+
+ gles2_Call const *call; // Next/current call to perform.
+ pthread_t thread; // The worker thread.
+ pthread_cond_t cond_start; // To wake worker for a call.
+ pthread_cond_t cond_state; // Worker signals when state changes.
+ volatile gles2_ClientState state;// Status of the client.
+ pthread_mutex_t mutex_run; // Locked when thread is running, or is
+ // prevented from doing so.
+ pthread_mutex_t mutex_wait; // For synchronization of worker and caller.
+
+ pthread_mutex_t mutex_xcode; // For decode/encode synchronization.
+ volatile int phase_xcode; // Phase of call 0: pending 1: decode done
+ // 2: exec done 3: encode done
+ pthread_cond_t cond_xcode; // --''--
+ pthread_cond_t cond_return; // --''--
+
+ gles2_Array *arrays; // Host side vertex pointer arrays.
+ int narrays; // Number of arrays (the maximum too).
+} gles2_Client;
+
+// The GLES2 device state holder.
+struct gles2_State
+{
+ CPUState *env; // The CPU the device is attached to.
+ gles2_Client *clients[GLES2_NCLIENTS]; // Array of clients.
+ void *handles[GLES2_NHANDLES]; // Handles passed from host to guest.
+ int quality; // Rendering quality.
+};
+
+typedef unsigned int gles2_decode_t; // Function call decoding state.
+typedef uint32_t gles2_target_arg_t; // Target unit argument type.
+// Callback for register area access.
+typedef void gles2_Callback(gles2_State *s, gles2_decode_t *d,
+ gles2_Client *c);
+
+// Information holder for guest->host call.
+struct gles2_Call
+{
+#ifndef NDEBUG
+ char const* name;
+#endif //!NDEBUG
+ gles2_Callback* callback;
+};
+
+// Create and initialize a GLES2 device and attach to CPU.
+extern void *gles2_init(CPUState *env);
+// Rendering quality option.
+extern int gles2_quality;
+
+/******************************************************************************
+ *
+ * Guest memory continuous access functions
+ *
+ *****************************************************************************/
+
+// Holder for compiled transfer holder.
+typedef struct gles2_CompiledTransfer
+{
+ unsigned nsections; // Number of physical memory sections in the transfer.
+ struct
+ {
+ char* base; // Base address of the section.
+ target_ulong len; // Length of the section.
+ } *sections; // Sections of the transfer.
+} gles2_CompiledTransfer;
+
+// Pre-compile a transfer to or from virtual guest address.
+// NOTE: An assumption is made that the mapping is equal for read and write, for
+// complicated transfers, use gles2_transfer or Fix It!
+extern int gles2_transfer_compile(gles2_CompiledTransfer *tfr, gles2_State *s,
+ target_ulong va, target_ulong len);
+// Execute a pre-compiled transfer.
+extern void gles2_transfer_exec(gles2_CompiledTransfer *tfr, gles2_State *s,
+ void* data, int access_type);
+// Free a pre-compiled transfer.
+extern void gles2_transfer_free(gles2_CompiledTransfer *tfr);
+// Perform a non-compiled transfer between guest and host.
+// access_type, read = 0, write = 1, execute = 2
+extern int gles2_transfer(gles2_State *s, target_ulong va, target_ulong len,
+ void* data, int access_type);
+
+/******************************************************************************
+ *
+ * Guest memory random access functions
+ *
+ *****************************************************************************/
+
+// Read an 8-bit byte from target system memory.
+extern uint8_t gles2_get_byte(gles2_State *s, target_ulong va);
+// Write an 8-bit byte to target system memory.
+extern void gles2_put_byte(gles2_State *s, target_ulong va, uint8_t byte);
+// Read a 16-bit word from target system memory.
+extern uint16_t gles2_get_word(gles2_State *s, target_ulong va);
+// Write a 16-bit word to target system memory.
+extern void gles2_put_word(gles2_State *s, target_ulong va, uint16_t word);
+// Read a 32-bit double word from target system memory.
+extern uint32_t gles2_get_dword(gles2_State *s, target_ulong va);
+// Write a 32-bit double word to target system memory.
+extern void gles2_put_dword(gles2_State *s, target_ulong va,
+ uint32_t dword);
+// Read a 32-bit float from target system memory.
+extern float gles2_get_float(gles2_State *s, target_ulong va);
+// Write a 32-bit float to target system memory.
+extern void gles2_put_float(gles2_State *s, target_ulong va, float flt);
+
+#define gles2_put_handle(s, va, handle) gles2_put_dword(s, va, handle)
+#define gles2_get_handle(s, va) gles2_get_dword(s, va)
+
+/******************************************************************************
+ *
+ * Handle management functions
+ *
+ *****************************************************************************/
+
+// Create a handle from host side pointer to be passed to guest.
+extern uint32_t gles2_handle_create(gles2_State *s, void* data);
+// Find if there is previously created handle for pointer.
+extern uint32_t gles2_handle_find(gles2_State *s, void* data);
+// Get the host pointer by guest handle.
+extern void* gles2_handle_get(gles2_State *s, uint32_t i);
+// Release a handle for reuse.
+extern void* gles2_handle_free(gles2_State *s, uint32_t i);
+
+// Get the smallest function argument according to target CPU ABI.
+static inline gles2_target_arg_t gles2_arg_raw(gles2_State *s, unsigned i);
+static inline gles2_target_arg_t gles2_arg_raw(gles2_State *s, unsigned i)
+{
+ if (i < 4) {
+ return s->env->regs[i];
+ }
+
+ return gles2_get_dword(s, s->env->regs[13] + 2*0x04 + (i - 4)*0x04);
+}
+
+/******************************************************************************
+ *
+ * ABI function argument decoding functions
+ *
+ *****************************************************************************/
+
+#define GLES2_DEBUG_ARGS 1
+static inline uint8_t gles2_arg_byte(gles2_State *s, gles2_decode_t *d);
+static inline uint8_t gles2_arg_byte(gles2_State *s, gles2_decode_t *d)
+{
+ uint8_t byte = gles2_arg_raw(s, (*d)++) & 0xFF;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("byte arg(%d) = %x\n", *d - 1, byte);
+#endif
+ return byte;
+}
+
+static inline uint16_t gles2_arg_word(gles2_State *s, gles2_decode_t *d);
+static inline uint16_t gles2_arg_word(gles2_State *s, gles2_decode_t *d)
+{
+ uint16_t word = gles2_arg_raw(s, (*d)++) & 0xFFFF;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("word arg(%d) = %x\n", *d - 1, word);
+#endif
+ return word;
+}
+
+static inline uint32_t gles2_arg_dword(gles2_State *s, gles2_decode_t *d);
+static inline uint32_t gles2_arg_dword(gles2_State *s, gles2_decode_t *d)
+{
+ uint32_t dword = gles2_arg_raw(s, (*d)++);
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("dword arg(%d) = %x\n", *d - 1, dword);
+#endif
+ return dword;
+}
+
+static inline uint64_t gles2_arg_qword(gles2_State *s, gles2_decode_t *d);
+static inline uint64_t gles2_arg_qword(gles2_State *s, gles2_decode_t *d)
+{
+ uint64_t qword = gles2_arg_raw(s, (*d)++)
+ | ((uint64_t)gles2_arg_raw(s, (*d)++) << 32);
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("qword arg(%d) = %"PRIu64"\n", *d - 2, qword);
+#endif
+ return qword;
+}
+
+static inline uint32_t gles2_arg_handle(gles2_State *s, gles2_decode_t *d);
+static inline uint32_t gles2_arg_handle(gles2_State *s, gles2_decode_t *d)
+{
+ uint32_t handle = gles2_arg_raw(s, (*d)++);
+
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("handle arg(%d) = %x\n", *d - 1, handle);
+#endif
+ return handle;
+}
+
+// This needs to be its own special function, because we must preserve the byteorder.
+static inline float gles2_arg_float(gles2_State *s, gles2_decode_t *d);
+static inline float gles2_arg_float(gles2_State *s, gles2_decode_t *d)
+{
+ unsigned i = (*d)++;
+
+ if (i < 4) {
+ return *((float*)&s->env->regs[i]);
+ }
+
+ return gles2_get_float(s, s->env->regs[13] + 2*0x04 + (i - 4)*0x04);
+}
+
+/******************************************************************************
+ *
+ * ABI return value encoding functions
+ *
+ *****************************************************************************/
+
+static inline void gles2_ret_byte(gles2_State *s, uint8_t byte);
+static inline void gles2_ret_byte(gles2_State *s, uint8_t byte)
+{
+ s->env->regs[0] = byte;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("byte ret = %d\n", byte);
+#endif
+}
+
+static inline void gles2_ret_word(gles2_State *s, uint16_t word);
+static inline void gles2_ret_word(gles2_State *s, uint16_t word)
+{
+ s->env->regs[0] = word;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("word ret = %d\n", word);
+#endif
+}
+
+static inline void gles2_ret_dword(gles2_State *s, uint32_t dword);
+static inline void gles2_ret_dword(gles2_State *s, uint32_t dword)
+{
+ s->env->regs[0] = dword;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("dword ret = %d\n", dword);
+#endif
+}
+
+static inline void gles2_ret_qword(gles2_State *s, uint64_t qword);
+static inline void gles2_ret_qword(gles2_State *s, uint64_t qword)
+{
+ s->env->regs[0] = qword & 0xFFFFFFFF;
+ s->env->regs[1] = qword >> 32;
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("qword ret = %"PRIu64"\n", qword);
+#endif
+}
+
+static inline void gles2_ret_handle(gles2_State *s, uint32_t handle);
+static inline void gles2_ret_handle(gles2_State *s, uint32_t handle)
+{
+ s->env->regs[0] = handle;
+
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("handle ret = %x\n", handle);
+#endif
+}
+
+static inline void gles2_ret_float(gles2_State *s, float flt);
+static inline void gles2_ret_float(gles2_State *s, float flt)
+{
+ s->env->regs[0] = *(uint32_t*)&flt;
+
+#if (GLES2_DEBUG_ARGS == 1)
+ GLES2_PRINT("float ret = %f\n", flt);
+#endif
+}
+
+#endif // GLES2_H__
+
--- /dev/null
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gles2.h"
+#include "EGL/degl.h"
+#include "GLES2/gl2.h"
+
+#include "gles2_types.h"
+
+GLES2_CB(eglBindAPI)
+{
+ GLES2_ARG(TEGLenum, api);
+ GLES2_BARRIER_ARG;
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, eglBindAPI(api));
+}
+
+GLES2_CB(eglGetDisplay)
+{
+// GLES2_ARG(TEGLDisplay, dpy);
+// (void)dpy;
+ GLES2_BARRIER_ARG;
+
+ GLES2_PRINT("Getting display...\n");
+
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ GLES2_PRINT("\tGot host display %p...\n", dpy);
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLDisplay(s, gles2_handle_create(s, dpy));
+}
+
+GLES2_CB(eglInitialize)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(Tptr, majorp);
+ GLES2_ARG(Tptr, minorp);
+
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+ GLES2_PRINT("Request to initialize display %p...\n", dpy);
+
+ EGLint major, minor;
+ if (eglInitialize(dpy, &major, &minor)) {
+ GLES2_PRINT("Display initialized (EGL %d.%d)!\n", major, minor);
+ GLES2_BARRIER_RET;
+ gles2_put_TEGLint(s, majorp, major);
+ gles2_put_TEGLint(s, minorp, minor);
+ gles2_ret_TEGLBoolean(s, EGL_TRUE);
+ return;
+ }
+
+ GLES2_PRINT("Failed to initialize...\n");
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, EGL_FALSE);
+}
+
+GLES2_CB(eglTerminate)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+ GLES2_PRINT("Request to close display %p...\n", dpy);
+
+ EGLBoolean ret = eglTerminate(dpy);
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglGetConfigs)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(Tptr, configsp);
+ GLES2_ARG(TEGLint, config_size);
+ GLES2_ARG(Tptr, num_configp);
+
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+ EGLConfig* configs = configsp ? malloc(sizeof(EGLConfig)*config_size) : NULL;
+
+ EGLint num_config;
+ EGLBoolean ret = eglGetConfigs(dpy, configs, config_size, &num_config);
+
+ GLES2_BARRIER_RET;
+ if (configs) {
+ EGLint i;
+
+ for (i = 0; i < num_config; ++i) {
+ uint32_t handle;
+ if (!(handle = gles2_handle_find(s, configs[i]))) {
+ handle = gles2_handle_create(s, configs[i]);
+ }
+ gles2_put_TEGLConfig(s, configsp + i*sizeof(TEGLConfig), handle);
+ }
+
+ free(configs);
+ }
+ gles2_put_TEGLint(s, num_configp, num_config);
+
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglChooseConfig)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(Tptr, attrib_listp);
+ GLES2_ARG(Tptr, configsp);
+ GLES2_ARG(TEGLint, config_size);
+ GLES2_ARG(Tptr, num_configp);
+ (void)config_size;
+ (void)attrib_listp;
+
+ EGLint attrib_list_n = 0;
+ while (gles2_get_TEGLint(s, attrib_listp
+ + attrib_list_n*sizeof(EGLint)) != EGL_NONE) {
+ attrib_list_n += 2;
+ }
+ EGLint* attrib_list = malloc((attrib_list_n + 1)*sizeof(EGLint));
+ EGLint i;
+
+ for (i = 0; i < attrib_list_n; ++i) {
+ attrib_list[i] = gles2_get_TEGLint(s, attrib_listp
+ + i*sizeof(EGLint));
+ }
+ attrib_list[attrib_list_n] = EGL_NONE;
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+ EGLConfig* configs = configsp ? malloc(sizeof(EGLConfig)*config_size) : NULL;
+
+ EGLint num_config;
+ EGLBoolean ret = eglChooseConfig(dpy, attrib_list, configs, config_size, &num_config);
+ free(attrib_list);
+ GLES2_BARRIER_RET;
+ if (configs) {
+ EGLint i;
+
+ for (i = 0; i < num_config; ++i) {
+ uint32_t handle;
+ if (!(handle = gles2_handle_find(s, configs[i]))) {
+ handle = gles2_handle_create(s, configs[i]);
+ }
+ gles2_put_TEGLConfig(s, configsp + i*sizeof(TEGLConfig), handle);
+ }
+
+ free(configs);
+ }
+ gles2_put_TEGLint(s, num_configp, num_config);
+
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglGetConfigAttrib)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLConfig, config);
+ GLES2_ARG(TEGLint, attribute);
+ GLES2_ARG(Tptr, valuep);
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+
+ EGLint value;
+ EGLBoolean ret = eglGetConfigAttrib(dpy, gles2_handle_get(s, config), attribute, &value);
+
+ GLES2_BARRIER_RET;
+
+ gles2_put_TEGLint(s, valuep, value);
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+typedef struct gles2_Surface
+{
+ uint32_t ddrawp; // Pointer to the offscreen drawable in guest memory.
+ DEGLDrawable ddraw; // Offscreen drawable, read from guest memory.
+ EGLSurface surf; // Pointer to the EGL surface.
+ uint32_t pixelsp; // Pointer to pixels in guest memory.
+ int pixmap; // True if surface is pixmap.
+ gles2_CompiledTransfer tfr; // Framebuffer transfer.
+ int valid; // If the surface is valid.
+ int id; // DEBUG!
+} gles2_Surface;
+
+// See if guest offscreen drawable was changed and if so, update host copy.
+static int gles2_surface_update(gles2_State *s, gles2_Surface *surf)
+{
+ int ret = 0;
+
+ uint32_t width = gles2_get_dword(s, surf->ddrawp + 0*sizeof(uint32_t));
+ uint32_t height = gles2_get_dword(s, surf->ddrawp + 1*sizeof(uint32_t));
+ uint32_t depth = gles2_get_dword(s, surf->ddrawp + 2*sizeof(uint32_t));
+ uint32_t bpp = gles2_get_dword(s, surf->ddrawp + 3*sizeof(uint32_t));
+ uint32_t pixelsp = gles2_get_dword(s, surf->ddrawp + 4*sizeof(uint32_t));
+
+ if (width != surf->ddraw.width
+ || height != surf->ddraw.height
+ || depth != surf->ddraw.depth) {
+ surf->ddraw.width = width;
+ surf->ddraw.height = height;
+ surf->ddraw.depth = depth;
+ surf->ddraw.bpp = bpp;
+ ret = 1;
+ }
+
+ surf->pixelsp = pixelsp;
+
+ return ret;
+}
+
+// TODO: Support swapping of offscreen surfaces.
+static void gles2_eglSwapCallback(void* userdata)
+{
+ (void)userdata;
+ GLES2_PRINT("Swap called!\n");
+}
+
+static int surf_id = 1;
+
+GLES2_CB(eglCreateWindowSurface)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLConfig, config_);
+ GLES2_ARG(Tptr, winp);
+ GLES2_ARG(Tptr, attrib_listp);
+
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+ (void)attrib_listp;
+
+ gles2_Surface* fsurf;
+
+ if (!(fsurf = malloc(sizeof(*fsurf)))) {
+ GLES2_PRINT("\tFake window creation failed!\n");
+ GLES2_BARRIER_ARG;
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLSurface(s, 0);
+ return;
+ }
+
+ fsurf->id = surf_id++;
+
+ fsurf->ddrawp = winp;
+ fsurf->pixmap = 0;
+ gles2_surface_update(s, fsurf);
+ GLES2_BARRIER_ARG;
+
+ GLES2_PRINT("Host window creation requested, %dx%d@%d(Bpp=%d) at 0x%x, ID = %d...\n",
+ fsurf->ddraw.width, fsurf->ddraw.height,
+ fsurf->ddraw.depth, fsurf->ddraw.bpp, fsurf->pixelsp, fsurf->id);
+
+ unsigned nbytes = fsurf->ddraw.width*fsurf->ddraw.height*fsurf->ddraw.bpp;
+ fsurf->ddraw.pixels = malloc(nbytes);
+ fsurf->ddraw.userdata = fsurf;
+ fsurf->ddraw.swap = gles2_eglSwapCallback;
+
+ if((fsurf->surf = eglCreateWindowSurface(dpy, config,
+ (EGLNativeWindowType)&fsurf->ddraw, NULL)) == EGL_NO_CONTEXT)
+ {
+ GLES2_PRINT("\tHost window creation failed!\n");
+ free(fsurf->ddraw.pixels);
+ free(fsurf);
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLSurface(s, 0);
+ return;
+ }
+
+ GLES2_PRINT("Created at %p!\n", fsurf);
+ GLES2_BARRIER_RET;
+ gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+ gles2_ret_TEGLSurface(s, gles2_handle_create(s, fsurf));
+}
+
+GLES2_CB(eglCreatePixmapSurface)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLConfig, config_);
+ GLES2_ARG(Tptr, pixmapp);
+ GLES2_ARG(Tptr, attrib_listp);
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+ (void)attrib_listp;
+
+ gles2_Surface* fsurf;
+
+ if (!(fsurf = malloc(sizeof(*fsurf)))) {
+ GLES2_PRINT("\tFake pixmap creation failed!\n");
+ GLES2_BARRIER_ARG;
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLSurface(s, 0);
+ return;
+ }
+
+ fsurf->id = surf_id++;
+ fsurf->ddrawp = pixmapp;
+ fsurf->pixmap = 1;
+ gles2_surface_update(s, fsurf);
+ GLES2_BARRIER_ARG;
+
+ GLES2_PRINT("Host pixmap creation requested, %dx%d@%d(Bpp=%d) at 0x%x, ID = %d...\n",
+ fsurf->ddraw.width, fsurf->ddraw.height,
+ fsurf->ddraw.depth, fsurf->ddraw.bpp, fsurf->pixelsp, fsurf->id);
+
+ unsigned nbytes = fsurf->ddraw.width*fsurf->ddraw.height*fsurf->ddraw.bpp;
+ fsurf->ddraw.pixels = malloc(nbytes);
+ fsurf->ddraw.userdata = fsurf;
+ fsurf->ddraw.swap = gles2_eglSwapCallback;
+
+ if((fsurf->surf = eglCreatePixmapSurface(dpy, config,
+ (EGLNativeWindowType)&fsurf->ddraw, NULL)) == EGL_NO_CONTEXT) {
+ GLES2_PRINT("\tHost pixmap creation failed!\n");
+ free(fsurf->ddraw.pixels);
+ free(fsurf);
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLSurface(s, 0);
+ return;
+ }
+
+ GLES2_PRINT("Created at %p!\n", fsurf);
+ GLES2_BARRIER_RET;
+ gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+ gles2_ret_TEGLSurface(s, gles2_handle_create(s, fsurf));
+}
+
+GLES2_CB(eglDestroySurface)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLSurface, surface_);
+ GLES2_BARRIER_ARG_NORET;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ gles2_Surface* fsurf = (EGLSurface)gles2_handle_get(s, surface_);
+ gles2_handle_free(s, surface_);
+
+ GLES2_PRINT("Destroyed surface ID = %d...\n", fsurf->id);
+ fsurf->id = -1;
+
+ eglDestroySurface(dpy, fsurf->surf);
+ free(fsurf->ddraw.pixels);
+
+// if(fsurf->pixmap == 0) {
+ gles2_transfer_free(&fsurf->tfr);
+// }
+ free(fsurf);
+}
+
+GLES2_CB(eglBindTexImage)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLSurface, surface_);
+ GLES2_ARG(TEGLint, buffer);
+ gles2_CompiledTransfer tfr;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ gles2_Surface* fsurf = (gles2_Surface*)gles2_handle_get(s, surface_);
+
+ // FIXME: Not a very clean way..
+ uint32_t pixelsp = gles2_get_dword(s, fsurf->ddrawp + 4*sizeof(uint32_t));
+ if (pixelsp) {
+ unsigned nbytes = fsurf->ddraw.width
+ * fsurf->ddraw.height*fsurf->ddraw.bpp;
+ gles2_transfer_compile(&tfr, s, pixelsp, nbytes);
+ }
+ GLES2_BARRIER_ARG;
+
+ if (pixelsp) {
+// gles2_transfer(s, pixelsp, nbytes, fsurf->ddraw.pixels, 0);
+ gles2_transfer_exec(&tfr, s, fsurf->ddraw.pixels, 0);
+ }
+
+ GLES2_PRINT("Binding surface ID = %d!\n", fsurf->id);
+
+ EGLBoolean ret = eglBindTexImage(dpy, &fsurf->ddraw, buffer);
+ if (pixelsp) {
+ gles2_transfer_free(&tfr);
+ }
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglReleaseTexImage)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLSurface, surface_);
+ GLES2_ARG(TEGLint, buffer);
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ gles2_Surface* fsurf = (gles2_Surface*)gles2_handle_get(s, surface_);
+
+ GLES2_PRINT("Unbinding surface ID = %d!\n", fsurf->id);
+
+ EGLBoolean ret = eglReleaseTexImage(dpy, &fsurf->ddraw, buffer);
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, ret);
+}
+
+GLES2_CB(eglCreateContext)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLConfig, config_);
+ GLES2_ARG(TEGLContext, share_context_);
+ GLES2_ARG(Tptr, attrib_listp);
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ EGLConfig config = (EGLConfig)gles2_handle_get(s, config_);
+ EGLContext share_context = (EGLContext)gles2_handle_get(s, share_context_);
+
+ GLES2_PRINT("TODO: Handle attribs...\n");
+ (void)attrib_listp;
+
+ GLES2_PRINT("Host context creation requested...\n");
+ EGLContext ctx;
+ if ((ctx = eglCreateContext(dpy, config,
+ share_context, NULL)) == EGL_NO_CONTEXT) {
+ GLES2_PRINT("\tContext creation failed!\n");
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLContext(s, 0);
+ return;
+ }
+ GLES2_PRINT("Created at %p!\n", ctx);
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLContext(s, gles2_handle_create(s, ctx));
+}
+
+GLES2_CB(eglDestroyContext)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLContext, ctx_);
+ GLES2_BARRIER_ARG;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ EGLContext ctx = (EGLContext)gles2_handle_get(s, ctx_);
+ gles2_handle_free(s, ctx_);
+ GLES2_PRINT("Destroyed %p!\n", ctx);
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, eglDestroyContext(dpy, ctx));
+}
+
+
+GLES2_CB(eglMakeCurrent)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLSurface, draw_);
+ GLES2_ARG(TEGLSurface, read_);
+ GLES2_ARG(TEGLContext, ctx_);
+ GLES2_BARRIER_ARG;
+ int i;
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ gles2_Surface* draw = (EGLSurface)gles2_handle_get(s, draw_);
+ gles2_Surface* read = (EGLSurface)gles2_handle_get(s, read_);
+ EGLContext ctx = (EGLContext)gles2_handle_get(s, ctx_);
+
+ GLES2_PRINT("Making host context current...\n");
+
+ if (!eglMakeCurrent(dpy,
+ draw ? draw->surf : NULL,
+ read ? read->surf : NULL,
+ ctx)) {
+ GLES2_PRINT("\tMakeCurrent failed!\n");
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, EGL_FALSE);
+ return;
+ }
+
+ // Initialize client state.
+ if (ctx) {
+ c->narrays = 0;
+ int vattrib_num = 0;
+
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &vattrib_num);
+ GLES2_PRINT("Maximum number of host vertex arrays: %d.\n", vattrib_num);
+
+ c->narrays += vattrib_num;
+
+ // for gles v1.
+ c->narrays += 4; //glVertexPointer, glNormalPointer, glColorPointer, glTexCoordPointer
+
+ c->arrays = malloc(c->narrays * sizeof(*c->arrays));
+ for (i = 0; i < c->narrays; ++i) {
+ c->arrays[i].type = GL_NONE;
+ c->arrays[i].enabled = 0;
+ c->arrays[i].ptr = 0;
+ c->arrays[i].apply = 0;
+ c->arrays[i].tptr = 0;
+ }
+ }
+
+ GLES2_PRINT("Made %p current (DRAW = %d, READ = %d)!\n", ctx, draw ? draw->id : 0, read ? read->id : 0);
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, EGL_TRUE);
+}
+
+GLES2_CB(eglSwapBuffers)
+{
+ GLES2_ARG(TEGLDisplay, dpy_);
+ GLES2_ARG(TEGLSurface, surface_);
+
+ EGLDisplay dpy = (EGLDisplay)gles2_handle_get(s, dpy_);
+ gles2_Surface* fsurf = (EGLSurface)gles2_handle_get(s, surface_);
+ if (!fsurf) {
+ fprintf(stderr, "ERROR: Trying to swap NULL surface!\n");
+ GLES2_BARRIER_ARG;
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, EGL_TRUE);
+ return;
+ }
+
+ if (gles2_surface_update(s, fsurf)) {
+ GLES2_BARRIER_ARG;
+ GLES2_PRINT("DIMENSIONS CHANGED!\n");
+ glFinish();
+ free(fsurf->ddraw.pixels);
+ unsigned nbytes = fsurf->ddraw.width
+ * fsurf->ddraw.height*fsurf->ddraw.bpp;
+ fsurf->ddraw.pixels = malloc(nbytes);
+
+ gles2_transfer_free(&fsurf->tfr);
+ GLES2_BARRIER_RET;
+ gles2_transfer_compile(&fsurf->tfr, s, fsurf->pixelsp, nbytes);
+ eglSwapBuffers(dpy, fsurf->surf);
+ gles2_ret_TEGLBoolean(s, EGL_TRUE);
+ return;
+ }
+ GLES2_BARRIER_ARG;
+
+ GLES2_PRINT("Swapping DGLES2 surface ID = %d!\n", fsurf->id);
+ eglSwapBuffers(dpy, fsurf->surf);
+
+ GLES2_PRINT("Transferring frame!\n");
+ gles2_transfer_exec(&fsurf->tfr, s, fsurf->ddraw.pixels, 1);
+ GLES2_PRINT("\tDone!\n");
+ GLES2_BARRIER_RET;
+ gles2_ret_TEGLBoolean(s, EGL_TRUE);
+}
+
+GLES2_CB(glClearColor)
+{
+ GLES2_ARG(TGLclampf, red);
+ GLES2_ARG(TGLclampf, green);
+ GLES2_ARG(TGLclampf, blue);
+ GLES2_ARG(TGLclampf, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glClearColor(red, green, blue, alpha);
+}
+
+
+GLES2_CB(glClear)
+{
+ GLES2_ARG(TGLbitfield, mask);
+ GLES2_BARRIER_ARG_NORET;
+
+ glClear(mask);
+}
+
+GLES2_CB(glDisableVertexAttribArray)
+{
+ GLES2_ARG(TGLuint, index);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Disabling array %d\n", index);
+ c->arrays[index].enabled = 0;
+ glDisableVertexAttribArray(index);
+}
+
+static void fglTransferArrays(gles2_State *s, gles2_Client *c,
+ GLint first, GLsizei count)
+{
+ int i;
+
+ for(i = 0; i < c->narrays; ++i) {
+ gles2_Array* va = c->arrays + i;
+ if(!va->enabled) {
+ continue;
+ }
+ unsigned esize = 1;
+ switch (va->type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE: esize = 1; break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT: esize = 2; break;
+ case GL_FIXED:
+ case GL_FLOAT: esize = 4; break;
+
+ default:
+ GLES2_PRINT("ERROR: Unknown type 0x%x in fglTransferArrays!\n", va->type);
+ exit(0);
+ break;
+
+ }
+ if (!va->stride) {
+ va->stride = va->size*esize;
+ }
+ va->real_stride = va->size*esize;
+
+ if (va->ptr) {
+ free(va->ptr);
+ }
+ unsigned nbytes = esize*count*va->size;
+ va->ptr = malloc(nbytes);
+
+ GLsizei j;
+ for (j = 0; j < count; ++j) {
+ signed k;
+ for (k = 0; k < va->size; ++k) {
+ switch (esize) {
+ case 1:
+ ((TGLubyte*)va->ptr)[j*va->size + k] =
+ gles2_get_byte(s, va->tptr + va->stride*(first + j)
+ + k*sizeof(TGLubyte));
+ break;
+ case 2:
+ ((TGLushort*)va->ptr)[j*va->size + k] =
+ gles2_get_word(s, va->tptr + va->stride*(first + j)
+ + k*sizeof(TGLushort));
+ break;
+ case 4:
+ if(va->type == GL_FLOAT) {
+ ((TGLfloat*)va->ptr)[j*va->size + k] =
+ gles2_get_float(s, va->tptr
+ + va->stride*(first + j)
+ + k*sizeof(TGLfloat));
+ } else {
+ ((TGLuint*)va->ptr)[j*va->size + k] =
+ gles2_get_dword(s, va->tptr
+ + va->stride*(first + j)
+ + k*sizeof(TGLuint));
+ }
+ break;
+ }
+ }
+ }
+
+ va->apply(va);
+ }
+}
+
+static void fglApplyVertexAttrib(gles2_Array *va)
+{
+ glVertexAttribPointer(va->indx, va->size, va->type,
+ va->normalized, 0, va->ptr);
+ GLenum error;
+
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ GLES2_PRINT("glVertexAttribPointer(%d, %d, 0x%x, 0, %d, %p\n)"
+ " failed with 0x%x!\n", va->indx, va->size, va->type,
+ va->normalized, va->ptr, error);
+ }
+}
+
+GLES2_CB(glDrawArrays)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_ARG(TGLint, first);
+ GLES2_ARG(TGLsizei, count);
+
+ fglTransferArrays(s, c, first, count);
+ GLES2_BARRIER_ARG_NORET;
+
+ glDrawArrays(mode, 0, count);
+}
+
+GLES2_CB(glDrawElements)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_ARG(TGLsizei, count);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(Tptr, indicesp);
+
+ (void)indicesp;
+ (void)count;
+ (void)mode;
+
+ GLint indice_size;
+ switch (type) {
+ case GL_UNSIGNED_BYTE: indice_size = sizeof(TGLubyte); break;
+ case GL_UNSIGNED_SHORT: indice_size = sizeof(TGLushort); break;
+ default:
+ fprintf(stderr, "ERROR: Invalid type %d!\n", type);
+ return;
+ }
+
+ int i, first = -1, last = -1;
+ void *copied_indices = malloc(indice_size * count);
+ for (i = 0; i < count; i++) {
+ TGLushort idx = 0;
+ switch (type) {
+ case GL_UNSIGNED_BYTE:
+ idx = gles2_get_byte(s, indicesp++);
+ ((TGLubyte *)copied_indices)[i] = (TGLubyte)idx;
+ break;
+ case GL_UNSIGNED_SHORT:
+ idx = gles2_get_word(s, indicesp);
+ ((TGLushort *)copied_indices)[i] = idx;
+ indicesp += 2;
+ break;
+ default:
+ break;
+ }
+ if (first < 0 || idx < first) {
+ first = idx;
+ }
+ if (last < 0 || idx > last) {
+ last = idx;
+ }
+ }
+ fglTransferArrays(s, c, first, last - first + 1);
+ GLES2_BARRIER_ARG_NORET;
+
+ glDrawElements(mode, count, type, copied_indices);
+ free(copied_indices);
+}
+
+GLES2_CB(glEnableVertexAttribArray)
+{
+ GLES2_ARG(TGLuint, index);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Enabling array %d\n", index);
+ c->arrays[index].enabled = 1;
+ c->arrays[index].apply = fglApplyVertexAttrib;
+ glEnableVertexAttribArray(index);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glGetVertexAttribfv(GLuint index, GLenum pname,
+ GLfloat* params)
+{
+ DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetVertexAttribiv(GLuint index, GLenum pname,
+ GLint* params)
+{
+ DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv(GLuint index,
+ GLenum pname, void** pointer)
+{
+ DUMMY();
+}
+#endif //0
+
+GLES2_CB(glVertexAttrib1f)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLfloat, x);
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib1f(indx, x);
+}
+
+GLES2_CB(glVertexAttrib1fv)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(Tptr, valuesp);
+ GLfloat x = gles2_get_float(s, valuesp);
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib1f(indx, x);
+}
+
+GLES2_CB(glVertexAttrib2f)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib2f(indx, x, y);
+}
+
+GLES2_CB(glVertexAttrib2fv)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(Tptr, valuesp);
+
+ GLfloat x = gles2_get_float(s, valuesp);
+ GLfloat y = gles2_get_float(s, valuesp + sizeof(TGLfloat));
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib2f(indx, x, y);
+}
+
+GLES2_CB(glVertexAttrib3f)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_ARG(TGLfloat, z);
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib3f(indx, x, y, z);
+}
+
+GLES2_CB(glVertexAttrib3fv)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(Tptr, valuesp);
+
+ GLfloat x = gles2_get_float(s, valuesp + 0*sizeof(TGLfloat));
+ GLfloat y = gles2_get_float(s, valuesp + 1*sizeof(TGLfloat));
+ GLfloat z = gles2_get_float(s, valuesp + 2*sizeof(TGLfloat));
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib3f(indx, x, y, z);
+}
+
+GLES2_CB(glVertexAttrib4f)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLfloat, x);
+ GLES2_ARG(TGLfloat, y);
+ GLES2_ARG(TGLfloat, z);
+ GLES2_ARG(TGLfloat, w);
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib4f(indx, x, y, z, w);
+}
+
+GLES2_CB(glVertexAttrib4fv)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(Tptr, valuesp);
+
+ GLfloat x = gles2_get_float(s, valuesp + 0*sizeof(TGLfloat));
+ GLfloat y = gles2_get_float(s, valuesp + 1*sizeof(TGLfloat));
+ GLfloat z = gles2_get_float(s, valuesp + 2*sizeof(TGLfloat));
+ GLfloat w = gles2_get_float(s, valuesp + 3*sizeof(TGLfloat));
+ GLES2_BARRIER_ARG_NORET;
+
+ glVertexAttrib4f(indx, x, y, z, w);
+}
+
+GLES2_CB(glVertexAttribPointer)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLint, size);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(TGLboolean, normalized);
+ GLES2_ARG(TGLsizei, stride);
+ GLES2_ARG(Tptr, tptr);
+ GLES2_BARRIER_ARG_NORET;
+
+ GLES2_PRINT("Array %d at 0x%x (%d elements every %d bytes)\n",
+ indx, tptr, size, stride);
+
+ gles2_Array *va = c->arrays + indx;
+ va->type = type;
+ va->indx = indx;
+ va->size = size;
+ va->normalized = normalized;
+ va->stride = stride;
+ va->tptr = tptr;
+}
+
+GLES2_CB(glGetVertexAttribPointerv)
+{
+ GLES2_ARG(TGLuint, indx);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, tptr);
+
+ Tptr res = 0;
+
+ if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER)
+ GLES2_PRINT("ERROR: Unknown pname 0x%x in glGetVertexAttribPointerv!\n", pname);
+
+ res = c->arrays[indx].tptr;
+
+ GLES2_BARRIER_RET;
+ gles2_put_Tptr(s, tptr, res);
+}
+
+
+static unsigned gles2_glGetCount(TGLenum pname)
+{
+ unsigned count;
+ switch(pname) {
+ case GL_ACTIVE_TEXTURE: count = 1; break;
+ case GL_ALIASED_LINE_WIDTH_RANGE: count = 2; break;
+ case GL_ALIASED_POINT_SIZE_RANGE: count = 2; break;
+ case GL_ALPHA_BITS: count = 1; break;
+ case GL_ARRAY_BUFFER_BINDING: count = 1; break;
+ case GL_BLEND: count = 1; break;
+ case GL_BLEND_COLOR: count = 4; break;
+ case GL_BLEND_DST_ALPHA: count = 1; break;
+ case GL_BLEND_DST_RGB: count = 1; break;
+ case GL_BLEND_EQUATION_ALPHA: count = 1; break;
+ case GL_BLEND_EQUATION_RGB: count = 1; break;
+ case GL_BLEND_SRC_ALPHA: count = 1; break;
+ case GL_BLEND_SRC_RGB: count = 1; break;
+ case GL_BLUE_BITS: count = 1; break;
+ case GL_COLOR_CLEAR_VALUE: count = 4; break;
+ case GL_COLOR_WRITEMASK: count = 4; break;
+ case GL_COMPRESSED_TEXTURE_FORMATS: count = GL_NUM_COMPRESSED_TEXTURE_FORMATS; break;
+ case GL_CULL_FACE: count = 1; break;
+ case GL_CULL_FACE_MODE: count = 1; break;
+ case GL_CURRENT_PROGRAM: count = 1; break;
+ case GL_DEPTH_BITS: count = 1; break;
+ case GL_DEPTH_CLEAR_VALUE: count = 1; break;
+ case GL_DEPTH_FUNC: count = 1; break;
+ case GL_DEPTH_RANGE: count = 2; break;
+ case GL_DEPTH_TEST: count = 1; break;
+ case GL_DEPTH_WRITEMASK: count = 1; break;
+ case GL_DITHER: count = 1; break;
+ case GL_ELEMENT_ARRAY_BUFFER_BINDING: count = 1; break;
+ case GL_FRAMEBUFFER_BINDING: count = 1; break;
+ case GL_FRONT_FACE: count = 1; break;
+ case GL_GENERATE_MIPMAP_HINT: count = 1; break;
+ case GL_GREEN_BITS: count = 1; break;
+ case GL_IMPLEMENTATION_COLOR_READ_FORMAT: count = 1; break;
+ case GL_IMPLEMENTATION_COLOR_READ_TYPE: count = 1; break;
+ case GL_LINE_WIDTH: count = 1; break;
+ case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: count = 1; break;
+ case GL_MAX_CUBE_MAP_TEXTURE_SIZE: count = 1; break;
+ case GL_MAX_FRAGMENT_UNIFORM_VECTORS: count = 1; break;
+ case GL_MAX_RENDERBUFFER_SIZE: count = 1; break;
+ case GL_MAX_TEXTURE_IMAGE_UNITS: count = 1; break;
+ case GL_MAX_TEXTURE_SIZE: count = 1; break;
+ case GL_MAX_VARYING_VECTORS: count = 1; break;
+ case GL_MAX_VERTEX_ATTRIBS: count = 1; break;
+ case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: count = 1; break;
+ case GL_MAX_VERTEX_UNIFORM_VECTORS: count = 1; break;
+ case GL_MAX_VIEWPORT_DIMS: count = 2; break;
+ case GL_NUM_COMPRESSED_TEXTURE_FORMATS: count = 1; break;
+ case GL_NUM_SHADER_BINARY_FORMATS: count = 1; break;
+ case GL_PACK_ALIGNMENT: count = 1; break;
+ case GL_POLYGON_OFFSET_FACTOR: count = 1; break;
+ case GL_POLYGON_OFFSET_FILL: count = 1; break;
+ case GL_POLYGON_OFFSET_UNITS: count = 1; break;
+ case GL_RED_BITS: count = 1; break;
+ case GL_RENDERBUFFER_BINDING: count = 1; break;
+ case GL_SAMPLE_BUFFERS: count = 1; break;
+ case GL_SAMPLE_COVERAGE_INVERT: count = 1; break;
+ case GL_SAMPLE_COVERAGE_VALUE: count = 1; break;
+ case GL_SAMPLES: count = 1; break;
+ case GL_SCISSOR_BOX: count = 4; break;
+ case GL_SCISSOR_TEST: count = 1; break;
+ case GL_SHADER_BINARY_FORMATS: count = GL_NUM_SHADER_BINARY_FORMATS; break;
+ case GL_SHADER_COMPILER: count = 1; break;
+ case GL_STENCIL_BACK_FAIL: count = 1; break;
+ case GL_STENCIL_BACK_FUNC: count = 1; break;
+ case GL_STENCIL_BACK_PASS_DEPTH_FAIL: count = 1; break;
+ case GL_STENCIL_BACK_PASS_DEPTH_PASS: count = 1; break;
+ case GL_STENCIL_BACK_REF: count = 1; break;
+ case GL_STENCIL_BACK_VALUE_MASK: count = 1; break;
+ case GL_STENCIL_BACK_WRITEMASK: count = 1; break;
+ case GL_STENCIL_BITS: count = 1; break;
+ case GL_STENCIL_CLEAR_VALUE: count = 1; break;
+ case GL_STENCIL_FAIL: count = 1; break;
+ case GL_STENCIL_FUNC: count = 1; break;
+ case GL_STENCIL_PASS_DEPTH_FAIL: count = 1; break;
+ case GL_STENCIL_PASS_DEPTH_PASS: count = 1; break;
+ case GL_STENCIL_REF: count = 1; break;
+ case GL_STENCIL_TEST: count = 1; break;
+ case GL_STENCIL_VALUE_MASK: count = 1; break;
+ case GL_STENCIL_WRITEMASK: count = 1; break;
+ case GL_SUBPIXEL_BITS: count = 1; break;
+ case GL_TEXTURE_BINDING_2D: count = 1; break;
+ case GL_TEXTURE_BINDING_CUBE_MAP: count = 1; break;
+ case GL_UNPACK_ALIGNMENT: count = 1; break;
+ case GL_VIEWPORT: count = 4; break;
+ default:
+ count = gles1_glGetCount(pname);
+ //GLES2_PRINT("ERROR: Unknown pname 0x%x in glGet!\n", pname);
+ //count = 1;
+ break;
+ }
+
+ GLES2_PRINT("glGet(0x%x) -> %u!\n", pname, count);
+
+ return count;
+}
+
+GLES2_CB(glGetBooleanv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLboolean params[4];
+ glGetBooleanv(pname, params);
+ unsigned const count = gles2_glGetCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLboolean(s, paramsp + i*sizeof(TGLboolean), params[i]);
+ }
+}
+
+GLES2_CB(glGetError)
+{
+ GLES2_BARRIER_ARG;
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLenum(s, glGetError());
+}
+
+GLES2_CB(eglGetError)
+{
+ GLES2_BARRIER_ARG;
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLint(s, eglGetError());
+}
+
+GLES2_CB(glGetFloatv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLfloat params[4];
+ glGetFloatv(pname, params);
+ unsigned const count = gles2_glGetCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetIntegerv)
+{
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLint params[4];
+ glGetIntegerv(pname, params);
+ unsigned const count = gles2_glGetCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+ }
+}
+
+GLES2_CB(glColorMask)
+{
+ GLES2_ARG(TGLboolean, red);
+ GLES2_ARG(TGLboolean, green);
+ GLES2_ARG(TGLboolean, blue);
+ GLES2_ARG(TGLboolean, alpha);
+ GLES2_BARRIER_ARG_NORET;
+
+ glColorMask(red, green, blue, alpha);
+}
+
+GLES2_CB(glCullFace)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_BARRIER_ARG_NORET;
+
+ glCullFace(mode);
+}
+
+GLES2_CB(glDisable)
+{
+ GLES2_ARG(TGLenum, cap);
+ GLES2_BARRIER_ARG;
+
+ glDisable(cap);
+ GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glEnable)
+{
+ GLES2_ARG(TGLenum, cap);
+ GLES2_BARRIER_ARG;
+
+ glEnable(cap);
+
+ GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glFinish)
+{
+ // Important to do this way, so that we don't return too early.
+ GLES2_BARRIER_ARG;
+ glFinish();
+ GLES2_BARRIER_RET;
+}
+
+GLES2_CB(glFlush)
+{
+ GLES2_BARRIER_ARG_NORET;
+ glFlush();
+}
+
+GLES2_CB(glFrontFace)
+{
+ GLES2_ARG(TGLenum, mode);
+ GLES2_BARRIER_ARG_NORET;
+
+ glFrontFace(mode);
+}
+
+GLES2_CB(glIsEnabled)
+{
+ GLES2_ARG(TGLenum, cap);
+ GLES2_BARRIER_ARG;
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLboolean(s, glIsEnabled(cap));
+}
+
+GLES2_CB(glHint)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, mode);
+ GLES2_BARRIER_ARG_NORET;
+
+ if(s->quality <= 75)
+ {
+ switch(target)
+ {
+ default: mode = GL_FASTEST; break;
+ }
+ }
+
+ glHint(target, mode);
+}
+
+GLES2_CB(glLineWidth)
+{
+ GLES2_ARG(TGLfloat, width);
+ GLES2_BARRIER_ARG_NORET;
+
+ glLineWidth(width);
+}
+
+GLES2_CB(glPolygonOffset)
+{
+ GLES2_ARG(TGLfloat, factor);
+ GLES2_ARG(TGLfloat, units);
+ GLES2_BARRIER_ARG_NORET;
+
+ glPolygonOffset(factor, units);
+}
+
+GLES2_CB(glSampleCoverage)
+{
+ GLES2_ARG(TGLclampf, value);
+ GLES2_ARG(TGLboolean, invert);
+ GLES2_BARRIER_ARG_NORET;
+
+ glSampleCoverage(value, invert);
+}
+
+GLES2_CB(glScissor)
+{
+ GLES2_ARG(TGLint, x);
+ GLES2_ARG(TGLint, y);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_BARRIER_ARG_NORET;
+
+ glScissor(x, y, width, height);
+}
+
+GLES2_CB(glViewport)
+{
+ GLES2_ARG(TGLint, x);
+ GLES2_ARG(TGLint, y);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_BARRIER_ARG_NORET;
+
+ glViewport(x, y, width, height);
+}
+
+GLES2_CB(glActiveTexture)
+{
+ GLES2_ARG(TGLenum, texture);
+ GLES2_BARRIER_ARG_NORET;
+
+ glActiveTexture(texture);
+}
+
+GLES2_CB(glBindTexture)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLuint, texture);
+ GLES2_BARRIER_ARG_NORET;
+
+ glBindTexture(target, texture);
+}
+
+#if 0
+GL_APICALL void GL_APIENTRY glCompressedTexImage2D(GLenum target,
+ GLint level, GLenum internalformat, GLsizei width, GLsizei height,
+ GLint border, GLsizei imageSize, const void* data)
+{
+ DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D(GLenum target,
+ GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+ GLenum format, GLsizei imageSize, const void* data)
+{
+ DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCopyTexImage2D(GLenum target, GLint level,
+ GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height,
+ GLint border)
+{
+ DUMMY();
+}
+
+GL_APICALL void GL_APIENTRY glCopyTexSubImage2D(GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint x, GLint y,
+ GLsizei width, GLsizei height)
+{
+ DUMMY();
+}
+#endif // 0
+
+GLES2_CB(glDeleteTextures)
+{
+ GLES2_ARG(TGLsizei, n);
+ GLES2_ARG(Tptr, texturesp);
+
+ GLsizei i;
+ GLuint* textures = (GLuint*)malloc(sizeof(GLuint)*n);
+ for(i = 0; i < n; ++i) {
+ textures[i] = gles2_get_TGLuint(s, texturesp + i*sizeof(TGLuint));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glDeleteTextures(n, textures);
+ free(textures);
+}
+
+GLES2_CB(glGenerateMipmap)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_BARRIER_ARG_NORET;
+
+ glGenerateMipmap(target);
+}
+
+GLES2_CB(glGenTextures)
+{
+ GLES2_ARG(TGLsizei, n);
+ GLES2_ARG(Tptr, texturesp);
+ GLES2_BARRIER_ARG;
+
+ GLsizei i;
+ GLuint* textures = (GLuint*)malloc(sizeof(GLuint)*n);
+ glGenTextures(n, textures);
+ GLES2_BARRIER_RET;
+ for(i = 0; i < n; ++i) {
+ gles2_put_TGLuint(s, texturesp + i*sizeof(TGLuint), textures[i]);
+ }
+ free(textures);
+}
+
+static unsigned gles2_glTexParameterCount(GLenum pname)
+{
+ unsigned count;
+
+ switch(pname) {
+ case GL_TEXTURE_MIN_FILTER: count = 1; break;
+ case GL_TEXTURE_MAG_FILTER: count = 1; break;
+ case GL_TEXTURE_WRAP_S: count = 1; break;
+ case GL_TEXTURE_WRAP_T: count = 1; break;
+ default:
+ GLES2_PRINT("ERROR: Unknown texture parameter 0x%x!\n", pname);
+ count = 1;
+ break;
+ }
+
+ return count;
+}
+
+GLES2_CB(glGetTexParameterfv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLfloat params[4];
+ glGetTexParameterfv(target, pname, params);
+ unsigned const count = gles2_glTexParameterCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetTexParameteriv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLint params[4];
+ glGetTexParameteriv(target, pname, params);
+ unsigned const count = gles2_glTexParameterCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+ }
+}
+
+GLES2_CB(glGetUniformfv)
+{
+ GLES2_ARG(TGLuint, program);
+ GLES2_ARG(TGLint, location);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ const int MAX_SIZE = 16;
+ GLfloat reserve[MAX_SIZE];
+ GLfloat params[MAX_SIZE];
+ gles2_transfer(s, paramsp, sizeof(GLfloat)*MAX_SIZE, params, 0);
+
+ memcpy(reserve, params, sizeof(GLfloat)*MAX_SIZE);
+
+ glGetUniformfv(program, location, params);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < MAX_SIZE; ++i) {
+ if (params[i] != reserve[i])
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetUniformiv)
+{
+ GLES2_ARG(TGLuint, program);
+ GLES2_ARG(TGLint, location);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ const int MAX_SIZE = 16;
+ GLint reserve[MAX_SIZE];
+ GLint params[MAX_SIZE];
+ gles2_transfer(s, paramsp, sizeof(GLint)*MAX_SIZE, params, 0);
+
+ memcpy(reserve, params, sizeof(GLint)*MAX_SIZE);
+
+ glGetUniformiv(program, location, params);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < MAX_SIZE; ++i) {
+ if (params[i] != reserve[i])
+ gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+ }
+}
+
+static unsigned gles2_glGetVertexAttribCount(GLenum pname)
+{
+ unsigned count;
+
+ switch(pname) {
+ case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: count = 1; break;
+ case GL_VERTEX_ATTRIB_ARRAY_ENABLED: count = 1; break;
+ case GL_VERTEX_ATTRIB_ARRAY_SIZE: count = 1; break;
+ case GL_VERTEX_ATTRIB_ARRAY_STRIDE: count = 1; break;
+ case GL_VERTEX_ATTRIB_ARRAY_TYPE: count = 1; break;
+ case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: count = 1; break;
+ case GL_CURRENT_VERTEX_ATTRIB: count = 4; break;
+ default:
+ GLES2_PRINT("ERROR: Unknown texture parameter 0x%x!\n", pname);
+ count = 1;
+ break;
+ }
+
+ return count;
+}
+
+GLES2_CB(glGetVertexAttribfv)
+{
+ GLES2_ARG(TGLuint, index);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ const int MAX_SIZE = 4;
+ GLfloat params[MAX_SIZE];
+
+ glGetVertexAttribfv(index, pname, params);
+ unsigned count = gles2_glGetVertexAttribCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLfloat(s, paramsp + i*sizeof(TGLfloat), params[i]);
+ }
+}
+
+GLES2_CB(glGetVertexAttribiv)
+{
+ GLES2_ARG(TGLuint, index);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ const int MAX_SIZE = 4;
+ GLint params[MAX_SIZE];
+
+ glGetVertexAttribiv(index, pname, params);
+ unsigned count = gles2_glGetVertexAttribCount(pname);
+ unsigned i;
+ GLES2_BARRIER_RET;
+ for(i = 0; i < count; ++i) {
+ gles2_put_TGLint(s, paramsp + i*sizeof(TGLint), params[i]);
+ }
+}
+
+GLES2_CB(glIsTexture)
+{
+ GLES2_ARG(TGLuint, texture);
+ GLES2_BARRIER_ARG;
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLboolean(s, glIsTexture(texture));
+}
+
+
+
+GLES2_CB(glReadPixels)
+{
+ GLES2_ARG(TGLint, x);
+ GLES2_ARG(TGLint, y);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLenum, format);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(Tptr, pixelsp);
+ GLES2_BARRIER_ARG;
+
+ unsigned bpp;
+ switch (format) {
+ case GL_ALPHA: bpp = 1; break;
+ case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+ case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+ case GL_LUMINANCE: bpp = 1; break;
+ case GL_LUMINANCE_ALPHA: bpp = 2; break;
+ default:
+ GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+ bpp = 1;
+ break;
+ }
+
+ GLES2_PRINT("Reading %dx%dx%d image at %d,%d...\n",
+ width, height, bpp, x, y);
+ char* pixels = NULL;
+ unsigned nbytes = width*height*bpp;
+ pixels = malloc(nbytes);
+
+ glReadPixels(x, y, width, height, format, type, pixels);
+ GLES2_BARRIER_RET;
+ gles2_transfer(s, pixelsp, nbytes, pixels, 1);
+ free(pixels);
+}
+
+GLES2_CB(glReleaseShaderCompiler)
+{
+ GLES2_BARRIER_ARG_NORET;
+
+ glReleaseShaderCompiler();
+}
+
+GLES2_CB(glTexImage2D)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLint, level);
+ GLES2_ARG(TGLint, internalformat);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLint, border);
+ GLES2_ARG(TGLenum, format);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(Tptr, pixelsp);
+
+ unsigned bpp;
+
+ switch(format) {
+ case GL_ALPHA: bpp = 1; break;
+ case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+ case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+ case GL_LUMINANCE: bpp = 1; break;
+ case GL_LUMINANCE_ALPHA: bpp = 2; break;
+ default:
+ GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+ bpp = 1;
+ break;
+ }
+
+ GLES2_PRINT("Uploading %dx%dx%d image...\n", width, height, bpp);
+ char* pixels = NULL;
+ if (pixelsp) {
+ unsigned nbytes = width*height*bpp;
+ pixels = malloc(nbytes);
+ gles2_transfer(s, pixelsp, nbytes, pixels, 0);
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+ free(pixels);
+}
+
+GLES2_CB(glTexParameterf)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLfloat, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexParameterf(target, pname, param);
+}
+
+GLES2_CB(glTexParameterfv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+
+ GLfloat params[4];
+ unsigned const count = gles2_glTexParameterCount(pname);
+ unsigned i;
+ for (i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLfloat(s, paramsp + i*sizeof(TGLfloat));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexParameterfv(target, pname, params);
+}
+
+GLES2_CB(glTexParameteri)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(TGLint, param);
+ GLES2_BARRIER_ARG_NORET;
+
+ if(s->quality <= 50)
+ {
+ switch(pname)
+ {
+ case GL_TEXTURE_MIN_FILTER: param = GL_NEAREST; break;
+ case GL_TEXTURE_MAG_FILTER: param = GL_NEAREST; break;
+ default: break;
+ }
+ }
+
+ glTexParameterf(target, pname, param);
+}
+
+GLES2_CB(glTexParameteriv)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+
+ GLint params[4];
+ unsigned const count = gles2_glTexParameterCount(pname);
+ unsigned i;
+ for(i = 0; i < count; ++i) {
+ params[i] = gles2_get_TGLint(s, paramsp + i*sizeof(GLint));
+ }
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexParameteriv(target, pname, params);
+}
+
+GLES2_CB(glTexSubImage2D)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLint, level);
+ GLES2_ARG(TGLint, xoffset);
+ GLES2_ARG(TGLint, yoffset);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLenum, format);
+ GLES2_ARG(TGLenum, type);
+ GLES2_ARG(Tptr, pixelsp);
+
+ unsigned bpp;
+ switch (format) {
+ case GL_ALPHA: bpp = 1; break;
+ case GL_RGB: bpp = (type == GL_UNSIGNED_BYTE) ? 3 : 2; break;
+ case GL_RGBA: bpp = (type == GL_UNSIGNED_BYTE) ? 4 : 2; break;
+ case GL_LUMINANCE: bpp = 1; break;
+ case GL_LUMINANCE_ALPHA: bpp = 2; break;
+ default:
+ GLES2_PRINT("ERROR: Unknown format 0x%x...\n", format);
+ bpp = 1;
+ break;
+ }
+
+ GLES2_PRINT("Uploading partial %dx%dx%d image at %d,%d...\n",
+ width, height, bpp, xoffset, yoffset);
+
+ unsigned nbytes = width*height*bpp;
+ char* pixels = malloc(nbytes);
+ gles2_transfer(s, pixelsp, nbytes, pixels, 0);
+ GLES2_BARRIER_ARG_NORET;
+
+ glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+ free(pixels);
+}
+
+
+GLES2_CB(glCompileShader)
+{
+ GLES2_ARG(TGLuint, shader);
+ GLES2_BARRIER_ARG_NORET;
+
+ glCompileShader(shader);
+}
+
+GLES2_CB(glCompressedTexImage2D)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLint, level);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLenum, internalformat);
+ GLES2_ARG(TGLint, border);
+ GLES2_ARG(TGLsizei, imageSize);
+ GLES2_ARG(Tptr, data);
+
+ GLES2_PRINT("Uploading compressed %dx%d image with size 0x%x and border %d...\n",
+ width, height, xoffset, yoffset, imageSize, border);
+
+ char* pixels = malloc(imageSize);
+ gles2_transfer(s, data, imageSize, pixels, 0);
+ GLES2_BARRIER_ARG_NORET;
+
+ glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, pixels);
+ free(pixels);
+}
+
+GLES2_CB(glCopyTexImage2D)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLint, level);
+ GLES2_ARG(TGLenum, internalformat);
+ GLES2_ARG(TGLint, x);
+ GLES2_ARG(TGLint, y);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLint, border);
+
+ GLES2_BARRIER_ARG;
+
+ glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+
+ GLES2_BARRIER_RET;
+}
+
+
+GLES2_CB(glCompressedTexSubImage2D)
+{
+ GLES2_ARG(TGLenum, target);
+ GLES2_ARG(TGLint, level);
+ GLES2_ARG(TGLint, xoffset);
+ GLES2_ARG(TGLint, yoffset);
+ GLES2_ARG(TGLsizei, width);
+ GLES2_ARG(TGLsizei, height);
+ GLES2_ARG(TGLenum, format);
+ GLES2_ARG(TGLsizei, imageSize);
+ GLES2_ARG(Tptr, data);
+
+ GLES2_PRINT("Uploading compressed partial %dx%d image at %d,%d with size 0x%x...\n",
+ width, height, xoffset, yoffset, imageSize);
+
+ char* pixels = malloc(imageSize);
+ gles2_transfer(s, data, imageSize, pixels, 0);
+ GLES2_BARRIER_ARG_NORET;
+
+ glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, pixels);
+ free(pixels);
+}
+
+
+GLES2_CB(glCreateShader)
+{
+ GLES2_ARG(TGLenum, type);
+ GLES2_BARRIER_ARG;
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLuint(s, glCreateShader(type));
+}
+
+GLES2_CB(glDeleteShader)
+{
+ GLES2_ARG(TGLuint, shader);
+ GLES2_BARRIER_ARG_NORET;
+
+ glDeleteShader(shader);
+}
+
+GLES2_CB(glIsShader)
+{
+ GLES2_ARG(TGLuint, shader);
+ GLES2_BARRIER_ARG;
+
+ GLES2_BARRIER_RET;
+ gles2_ret_TGLboolean(s, glIsShader(shader));
+}
+
+GLES2_CB(glGetShaderiv)
+{
+ GLES2_ARG(TGLuint, shader);
+ GLES2_ARG(TGLenum, pname);
+ GLES2_ARG(Tptr, paramsp);
+ GLES2_BARRIER_ARG;
+
+ GLint param;
+ glGetShaderiv(shader, pname, ¶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);
+}
+
--- /dev/null
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*egl*/
+CALL_ENTRY(eglGetError)
+CALL_ENTRY(eglGetDisplay)
+CALL_ENTRY(eglInitialize)
+CALL_ENTRY(eglTerminate)
+CALL_DUMMY(eglQueryString)
+CALL_ENTRY(eglGetConfigs)
+CALL_ENTRY(eglChooseConfig)
+CALL_ENTRY(eglGetConfigAttrib)
+CALL_ENTRY(eglCreateWindowSurface)
+CALL_DUMMY(eglCreatePbufferSurface)
+CALL_ENTRY(eglCreatePixmapSurface)
+CALL_ENTRY(eglDestroySurface)
+CALL_DUMMY(eglQuerySurface)
+CALL_ENTRY(eglBindAPI)
+CALL_DUMMY(eglQueryAPI)
+CALL_DUMMY(eglWaitClient)
+CALL_DUMMY(eglReleaseThread)
+CALL_DUMMY(eglCreatePbufferFromClientBuffer)
+CALL_DUMMY(eglSurfaceAttrib)
+CALL_ENTRY(eglBindTexImage)
+CALL_ENTRY(eglReleaseTexImage)
+CALL_DUMMY(eglSwapInterval)
+CALL_ENTRY(eglCreateContext)
+CALL_ENTRY(eglDestroyContext)
+CALL_ENTRY(eglMakeCurrent)
+CALL_DUMMY(eglGetCurrentContext)
+CALL_DUMMY(eglGetCurrentSurface)
+CALL_DUMMY(eglGetCurrentDisplay)
+CALL_DUMMY(eglQueryContext)
+CALL_DUMMY(eglWaitGL)
+CALL_DUMMY(eglWaitNative)
+CALL_ENTRY(eglSwapBuffers)
+CALL_DUMMY(eglCopyBuffers)
+CALL_DUMMY(eglGetProcAddress)
+
+/* gles v2*/
+CALL_ENTRY(glActiveTexture)
+CALL_ENTRY(glAttachShader)
+CALL_ENTRY(glBindAttribLocation)
+CALL_ENTRY(glBindBuffer)
+CALL_ENTRY(glBindFramebuffer)
+CALL_ENTRY(glBindRenderbuffer)
+CALL_ENTRY(glBindTexture)
+CALL_ENTRY(glBlendColor)
+CALL_ENTRY(glBlendEquation)
+CALL_ENTRY(glBlendEquationSeparate)
+CALL_ENTRY(glBlendFunc)
+CALL_ENTRY(glBlendFuncSeparate)
+CALL_DUMMY(glBufferData)
+CALL_DUMMY(glBufferSubData)
+CALL_ENTRY(glCheckFramebufferStatus)
+CALL_ENTRY(glClear)
+CALL_ENTRY(glClearColor)
+CALL_ENTRY(glClearDepthf)
+CALL_ENTRY(glClearStencil)
+CALL_ENTRY(glColorMask)
+CALL_ENTRY(glCompileShader)
+CALL_ENTRY(glCompressedTexImage2D)
+CALL_ENTRY(glCompressedTexSubImage2D)
+CALL_ENTRY(glCopyTexImage2D)
+CALL_ENTRY(glCopyTexSubImage2D)
+CALL_ENTRY(glCreateProgram)
+CALL_ENTRY(glCreateShader)
+CALL_ENTRY(glCullFace)
+CALL_DUMMY(glDeleteBuffers)
+CALL_ENTRY(glDeleteFramebuffers)
+CALL_ENTRY(glDeleteProgram)
+CALL_ENTRY(glDeleteRenderbuffers)
+CALL_ENTRY(glDeleteShader)
+CALL_ENTRY(glDeleteTextures)
+CALL_ENTRY(glDepthFunc)
+CALL_ENTRY(glDepthMask)
+CALL_ENTRY(glDepthRangef)
+CALL_ENTRY(glDetachShader)
+CALL_ENTRY(glDisable)
+CALL_ENTRY(glDisableVertexAttribArray)
+CALL_ENTRY(glDrawArrays)
+CALL_ENTRY(glDrawElements)
+CALL_ENTRY(glEnable)
+CALL_ENTRY(glEnableVertexAttribArray)
+CALL_ENTRY(glFinish)
+CALL_ENTRY(glFlush)
+CALL_ENTRY(glFramebufferRenderbuffer)
+CALL_ENTRY(glFramebufferTexture2D)
+CALL_ENTRY(glFrontFace)
+CALL_DUMMY(glGenBuffers)
+CALL_ENTRY(glGenerateMipmap)
+CALL_ENTRY(glGenFramebuffers)
+CALL_ENTRY(glGenRenderbuffers)
+CALL_ENTRY(glGenTextures)
+CALL_ENTRY(glGetActiveAttrib)
+CALL_ENTRY(glGetActiveUniform)
+CALL_ENTRY(glGetAttachedShaders)
+CALL_ENTRY(glGetAttribLocation)
+CALL_ENTRY(glGetBooleanv)
+CALL_DUMMY(glGetBufferParameteriv)
+CALL_ENTRY(glGetError)
+CALL_ENTRY(glGetFloatv)
+CALL_DUMMY(glGetFramebufferAttachmentParameteriv)
+CALL_ENTRY(glGetIntegerv)
+CALL_ENTRY(glGetProgramiv)
+CALL_ENTRY(glGetProgramInfoLog)
+CALL_DUMMY(glGetRenderbufferParameteriv)
+CALL_ENTRY(glGetShaderiv)
+CALL_ENTRY(glGetShaderInfoLog)
+CALL_ENTRY(glGetShaderPrecisionFormat)
+CALL_ENTRY(glGetShaderSource)
+CALL_DUMMY(glGetString)
+CALL_ENTRY(glGetTexParameterfv)
+CALL_ENTRY(glGetTexParameteriv)
+CALL_ENTRY(glGetUniformfv)
+CALL_ENTRY(glGetUniformiv)
+CALL_ENTRY(glGetUniformLocation)
+CALL_ENTRY(glGetVertexAttribfv)
+CALL_ENTRY(glGetVertexAttribiv)
+CALL_ENTRY(glGetVertexAttribPointerv)
+CALL_ENTRY(glHint)
+CALL_DUMMY(glIsBuffer)
+CALL_ENTRY(glIsEnabled)
+CALL_DUMMY(glIsFramebuffer)
+CALL_ENTRY(glIsProgram)
+CALL_DUMMY(glIsRenderbuffer)
+CALL_ENTRY(glIsShader)
+CALL_ENTRY(glIsTexture)
+CALL_ENTRY(glLineWidth)
+CALL_ENTRY(glLinkProgram)
+CALL_ENTRY(glPixelStorei)
+CALL_ENTRY(glPolygonOffset)
+CALL_ENTRY(glReadPixels)
+CALL_ENTRY(glReleaseShaderCompiler)
+CALL_ENTRY(glRenderbufferStorage)
+CALL_ENTRY(glSampleCoverage)
+CALL_ENTRY(glScissor)
+CALL_DUMMY(glShaderBinary)
+CALL_ENTRY(glShaderSource)
+CALL_ENTRY(glStencilFunc)
+CALL_ENTRY(glStencilFuncSeparate)
+CALL_ENTRY(glStencilMask)
+CALL_ENTRY(glStencilMaskSeparate)
+CALL_ENTRY(glStencilOp)
+CALL_ENTRY(glStencilOpSeparate)
+CALL_ENTRY(glTexImage2D)
+CALL_ENTRY(glTexParameterf)
+CALL_ENTRY(glTexParameterfv)
+CALL_ENTRY(glTexParameteri)
+CALL_ENTRY(glTexParameteriv)
+CALL_ENTRY(glTexSubImage2D)
+CALL_ENTRY(glUniform1f)
+CALL_ENTRY(glUniform1fv)
+CALL_ENTRY(glUniform1i)
+CALL_ENTRY(glUniform1iv)
+CALL_ENTRY(glUniform2f)
+CALL_ENTRY(glUniform2fv)
+CALL_ENTRY(glUniform2i)
+CALL_ENTRY(glUniform2iv)
+CALL_ENTRY(glUniform3f)
+CALL_ENTRY(glUniform3fv)
+CALL_ENTRY(glUniform3i)
+CALL_ENTRY(glUniform3iv)
+CALL_ENTRY(glUniform4f)
+CALL_ENTRY(glUniform4fv)
+CALL_ENTRY(glUniform4i)
+CALL_ENTRY(glUniform4iv)
+CALL_ENTRY(glUniformMatrix2fv)
+CALL_ENTRY(glUniformMatrix3fv)
+CALL_ENTRY(glUniformMatrix4fv)
+CALL_ENTRY(glUseProgram)
+CALL_ENTRY(glValidateProgram)
+CALL_ENTRY(glVertexAttrib1f)
+CALL_ENTRY(glVertexAttrib1fv)
+CALL_ENTRY(glVertexAttrib2f)
+CALL_ENTRY(glVertexAttrib2fv)
+CALL_ENTRY(glVertexAttrib3f)
+CALL_ENTRY(glVertexAttrib3fv)
+CALL_ENTRY(glVertexAttrib4f)
+CALL_ENTRY(glVertexAttrib4fv)
+CALL_ENTRY(glVertexAttribPointer)
+CALL_ENTRY(glViewport)
+
+/* gles v1 without gles v2*/
+CALL_ENTRY(glAlphaFunc)
+CALL_ENTRY(glClipPlanef)
+CALL_ENTRY(glColor4f)
+CALL_ENTRY(glFogf)
+CALL_ENTRY(glFogfv)
+CALL_ENTRY(glFrustumf)
+CALL_ENTRY(glGetClipPlanef)
+CALL_ENTRY(glGetLightfv)
+CALL_ENTRY(glGetMaterialfv)
+CALL_ENTRY(glGetTexEnvfv)
+CALL_ENTRY(glLightModelf)
+CALL_ENTRY(glLightModelfv)
+CALL_ENTRY(glLightf)
+CALL_ENTRY(glLightfv)
+CALL_ENTRY(glLoadMatrixf)
+CALL_ENTRY(glMaterialf)
+CALL_ENTRY(glMaterialfv)
+CALL_ENTRY(glMultMatrixf)
+CALL_ENTRY(glMultiTexCoord4f)
+CALL_ENTRY(glNormal3f)
+CALL_ENTRY(glOrthof)
+CALL_ENTRY(glPointParameterf)
+CALL_ENTRY(glPointParameterfv)
+CALL_ENTRY(glPointSize)
+CALL_ENTRY(glRotatef)
+CALL_ENTRY(glScalef)
+CALL_ENTRY(glTexEnvf)
+CALL_ENTRY(glTexEnvfv)
+CALL_ENTRY(glTranslatef)
+CALL_ENTRY(glAlphaFuncx)
+CALL_ENTRY(glClearColorx)
+CALL_ENTRY(glClearDepthx)
+CALL_ENTRY(glClientActiveTexture)
+CALL_ENTRY(glClipPlanex)
+CALL_ENTRY(glColor4ub)
+CALL_ENTRY(glColor4x)
+CALL_ENTRY(glColorPointer)
+CALL_ENTRY(glDepthRangex)
+CALL_ENTRY(glDisableClientState)
+CALL_ENTRY(glEnableClientState)
+CALL_ENTRY(glFogx)
+CALL_ENTRY(glFogxv)
+CALL_ENTRY(glFrustumx)
+CALL_ENTRY(glGetClipPlanex)
+CALL_ENTRY(glGetFixedv)
+CALL_ENTRY(glGetLightxv)
+CALL_ENTRY(glGetMaterialxv)
+CALL_ENTRY(glGetPointerv)
+CALL_ENTRY(glGetTexEnviv)
+CALL_ENTRY(glGetTexEnvxv)
+CALL_ENTRY(glGetTexParameterxv)
+CALL_ENTRY(glLightModelx)
+CALL_ENTRY(glLightModelxv)
+CALL_ENTRY(glLightx)
+CALL_ENTRY(glLightxv)
+CALL_ENTRY(glLineWidthx)
+CALL_ENTRY(glLoadIdentity)
+CALL_ENTRY(glLoadMatrixx)
+CALL_ENTRY(glLogicOp)
+CALL_ENTRY(glMaterialx)
+CALL_ENTRY(glMaterialxv)
+CALL_ENTRY(glMatrixMode)
+CALL_ENTRY(glMultMatrixx)
+CALL_ENTRY(glMultiTexCoord4x)
+CALL_ENTRY(glNormal3x)
+CALL_ENTRY(glNormalPointer)
+CALL_ENTRY(glOrthox)
+CALL_ENTRY(glPointParameterx)
+CALL_ENTRY(glPointParameterxv)
+CALL_ENTRY(glPointSizex)
+CALL_ENTRY(glPolygonOffsetx)
+CALL_ENTRY(glPopMatrix)
+CALL_ENTRY(glPushMatrix)
+CALL_ENTRY(glRotatex)
+CALL_ENTRY(glSampleCoveragex)
+CALL_ENTRY(glScalex)
+CALL_ENTRY(glShadeModel)
+CALL_ENTRY(glTexCoordPointer)
+CALL_ENTRY(glTexEnvi)
+CALL_ENTRY(glTexEnvx)
+CALL_ENTRY(glTexEnviv)
+CALL_ENTRY(glTexEnvxv)
+CALL_ENTRY(glTexParameterx)
+CALL_ENTRY(glTexParameterxv)
+CALL_ENTRY(glTranslatex)
+CALL_ENTRY(glVertexPointer)
--- /dev/null
+/* Copyright (c) 2009-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GLES2_TYPES_H_
+#define GLES2_TYPES_H_
+
+// Automatically create the prototype and function definition.
+#define GLES2_CB(FUNC) \
+ void gles2_##FUNC##_cb(gles2_State *s, \
+ gles2_decode_t *d, gles2_Client *c); \
+ void gles2_##FUNC##_cb(gles2_State *s, \
+ gles2_decode_t *d, gles2_Client *c)
+
+// Sizes of primitive types in the ABI.
+#define GLES2_HTYPE_byte uint8_t
+#define GLES2_HTYPE_word uint16_t
+#define GLES2_HTYPE_dword uint32_t
+#define GLES2_HTYPE_float float
+#define GLES2_HTYPE_handle uint32_t
+
+// Defines shorthands for handling types.
+#define GLES2_TYPE(TYPE, SIZE) \
+ typedef GLES2_HTYPE_##SIZE TYPE; \
+ static inline void gles2_ret_##TYPE(gles2_State *s, TYPE value) \
+ { gles2_ret_##SIZE(s, value); } \
+ static inline void gles2_put_##TYPE(gles2_State *s, target_ulong va, TYPE value) \
+ { gles2_put_##SIZE(s, va, value); } \
+ static inline TYPE gles2_get_##TYPE(gles2_State *s, target_ulong va) \
+ { return (TYPE)gles2_get_##SIZE(s, va); } \
+ static inline TYPE gles2_arg_##TYPE(gles2_State *s, gles2_decode_t *d) \
+ { return (TYPE)gles2_arg_##SIZE(s, d); }
+
+// Bunch of expansions of previous macro to ease things up.
+GLES2_TYPE(Tptr, dword)
+GLES2_TYPE(TEGLBoolean, dword)
+GLES2_TYPE(TEGLenum, dword)
+GLES2_TYPE(TEGLint, dword)
+GLES2_TYPE(TEGLDisplay, handle)
+GLES2_TYPE(TEGLConfig, handle)
+GLES2_TYPE(TEGLContext, handle)
+GLES2_TYPE(TEGLSurface, handle)
+
+GLES2_TYPE(TGLclampf, float)
+GLES2_TYPE(TGLbitfield, dword)
+GLES2_TYPE(TGLboolean, byte)
+GLES2_TYPE(TGLint, dword)
+GLES2_TYPE(TGLuint, dword)
+GLES2_TYPE(TGLushort, word)
+GLES2_TYPE(TGLubyte, byte)
+GLES2_TYPE(TGLenum, dword)
+GLES2_TYPE(TGLsizei, dword)
+GLES2_TYPE(TGLfloat, float)
+GLES2_TYPE(TGLfixed, dword)
+GLES2_TYPE(TGLclampx, dword)
+
+
+// Just one more macro for even less typing.
+#define GLES2_ARG(TYPE, NAME) \
+ TYPE NAME = gles2_arg_##TYPE(s, d)
+
+// pthread_cond_signal(&s->cond_xcode);
+
+
+// Host to guest vertex array copy.
+struct gles2_Array
+{
+ GLuint indx; // Parameter of the call.
+ GLint size; // --''--
+ GLenum type; // --''--
+ GLboolean normalized; // --''--
+ GLsizei stride; // --''--
+ GLsizei real_stride; // --''--
+ Tptr tptr; // Pointer in the guest memory.
+ void* ptr; // Pointer in the host memory.
+
+ void (*apply) (struct gles2_Array *va);
+
+ GLboolean enabled; // State.
+};
+
+unsigned gles1_glGetCount(TGLenum pname);
+void checkGLESError(void);
+
+#endif /* GLES2_TYPES_H_ */
--- /dev/null
+/*
+ * Interface to support I2C devices with control registers accessed by I2C
+ *
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vmonakhov@ispras.ru>
+ */
+
+#include "i2c-addressable.h"
+
+
+static int i2c_addressable_rx(i2c_slave *s)
+{
+ I2CAddressableDeviceInfo *t =
+ DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+ I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+ /* Get next byte of the value and transfer it */
+ if (t->read) {
+ return t->read(dev, dev->addr, dev->num++);
+ }
+ return 0;
+}
+
+static int i2c_addressable_tx(i2c_slave *s, uint8_t data)
+{
+ I2CAddressableDeviceInfo *t =
+ DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+ I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+ if (dev->num < t->size) {
+ /* First fully get control register address byte after byte */
+ if (!t->rev) {
+ /* Less significant bytes come first */
+ dev->addr |= data << (dev->num * 8);
+ } else {
+ /* More significant bytes come first */
+ dev->addr |= data << ((t->size - dev->num - 1) * 8);
+ }
+ } else {
+ /* Then get corresponding data byte after byte */
+ if (t->write) {
+ t->write(dev, dev->addr, dev->num - t->size, data);
+ }
+ }
+
+ dev->num++;
+ return 1;
+}
+
+static void i2c_addressable_event(i2c_slave *s, enum i2c_event event)
+{
+ I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+ switch (event) {
+ case I2C_START_SEND:
+ dev->addr = 0;
+ /* fallthrough */
+ case I2C_START_RECV:
+ /* Save address from the previous send */
+ dev->num = 0;
+ break;
+ case I2C_FINISH:
+ default:
+ break;
+ }
+}
+
+static int i2c_addressable_device_init(i2c_slave *s)
+{
+ I2CAddressableDeviceInfo *t =
+ DO_UPCAST(I2CAddressableDeviceInfo, i2c, s->info);
+ I2CAddressableState *dev = FROM_I2C_SLAVE(I2CAddressableState, s);
+
+ return t->init(dev);
+}
+
+void i2c_addressable_register_device(I2CAddressableDeviceInfo *info)
+{
+ assert(info->i2c.qdev.size >= sizeof(I2CAddressableState));
+ info->i2c.init = i2c_addressable_device_init;
+ info->i2c.event = i2c_addressable_event;
+ info->i2c.recv = i2c_addressable_rx;
+ info->i2c.send = i2c_addressable_tx;
+ i2c_register_slave(&info->i2c);
+}
--- /dev/null
+#ifndef QEMU_I2C_ADDRESSABLE_H
+#define QEMU_I2C_ADDRESSABLE_H
+
+/*
+ * Interface to support I2C devices with control registers accessed by I2C
+ *
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * Many I2C devices have common logic which closely corresponds to control
+ * registers. These devices are first passed with an ID of the needed command
+ * to be performed. This ID may be considered as control register address.
+ * Then the device is either passed with some data or is awaited to return
+ * some data. This may be considered as control register write and read
+ * respectively. This interface provides a generic way to implement such
+ * I2C devices emulation by writing two functions for reading and writing of
+ * control registers similar to iomem interface. One difference is that
+ * control registers may not have fixed sizes so along with the register
+ * address these functions get another parameter - offset inside this register
+ * data.
+ */
+
+#include "i2c.h"
+
+#define I2CADDR_SLAVE_FROM_QDEV(dev) DO_UPCAST(I2CAddressableState, i2c.qdev, dev)
+#define FROM_I2CADDR_SLAVE(type, dev) DO_UPCAST(type, i2c_addressable, dev)
+
+typedef struct I2CAddressableState {
+ /* I2C slave for the device */
+ i2c_slave i2c;
+ /* Address of currently processing data */
+ uint32_t addr;
+ /* Number of transferred bytes */
+ uint8_t num;
+} I2CAddressableState;
+
+typedef uint8_t (*i2c_addressable_read) (void *opaque, uint32_t address,
+ uint8_t offset);
+typedef void (*i2c_addressable_write) (void *opaque, uint32_t address,
+ uint8_t offset, uint8_t val);
+typedef int (*i2c_addressable_init) (I2CAddressableState *i2c);
+
+typedef struct I2CAddressableDeviceInfo {
+ I2CSlaveInfo i2c;
+
+ /* Read, write and init handlers */
+ i2c_addressable_read read;
+ i2c_addressable_write write;
+ i2c_addressable_init init;
+
+ /* Size of passed addresses in bytes */
+ int size;
+ /* Byte order: reversed is big-endian (more significant bytes come before
+ * less significant ones) */
+ int rev;
+} I2CAddressableDeviceInfo;
+
+void i2c_addressable_register_device(I2CAddressableDeviceInfo *info);
+
+#endif
/* 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
typedef struct {
IDEBus bus;
int shift;
+ int offset;
} MMIOState;
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
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
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);
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * Melfas MCS-5000 Touchkey
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * NB: Only features used in the kernel driver is implemented currently.
+ * NB2: This device may also be used in touchscreen mode. Not supported now.
+ */
+
+#include "console.h"
+#include "i2c-addressable.h"
+
+
+#define MCS5000_TK_LED_ONOFF 0x01
+#define MCS5000_TK_LED_DIMMING 0x02
+#define MCS5000_TK_RESERVED 0x03
+#define MCS5000_TK_VALUE_STATUS 0x04
+#define MCS5000_TK_RESERVED1 0x05
+#define MCS5000_TK_HW_VERSION 0x06
+#define MCS5000_TK_FW_VERSION 0x0A
+#define MCS5000_TK_MI_VERSION 0x0B
+
+#define MCS5000_TK_FW_ADDR 0x7e
+
+
+typedef struct MCS5000State {
+
+ I2CAddressableState i2c_addressable;
+
+ uint8_t hw_version;
+ uint8_t fw_version;
+ uint8_t mi_version;
+
+ uint8_t status;
+
+ int32_t download_fw_state;
+ int32_t download_fw_size;
+ qemu_irq irq;
+} MCS5000State;
+
+
+static int map[0x80];
+
+static int universal_tk_keymap[0x80] = {
+ [0x61] = 100, /* 100 is KEY_PHONE / KEY_SEND */
+ [0x62] = 101, /* 101 is KEY_FRONT / KEY_HOME */
+ [0x63] = 102, /* 102 is KEY_EXIT / KEY_END */
+};
+
+
+static void mcs5000_reset(DeviceState *d)
+{
+ MCS5000State *s =
+ FROM_I2CADDR_SLAVE(MCS5000State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+ s->hw_version = 0x9;
+ s->fw_version = 0x9;
+ s->mi_version = 0x0;
+
+ s->status = 0;
+
+ s->download_fw_state = 0;
+ s->download_fw_size = 0;
+}
+
+static uint8_t mcs5000_read(void *opaque, uint32_t address, uint8_t offset)
+{
+ MCS5000State *s = (MCS5000State *)opaque;
+
+ if (offset > 0) {
+ hw_error("mcs5000: bad read size\n");
+ }
+
+ switch (address) {
+ case MCS5000_TK_VALUE_STATUS:
+ return s->status;
+ case MCS5000_TK_HW_VERSION:
+ return s->hw_version;
+ case MCS5000_TK_FW_VERSION:
+ return s->fw_version;
+ case MCS5000_TK_MI_VERSION:
+ return s->mi_version;
+ case MCS5000_TK_FW_ADDR:
+ s->download_fw_state++;
+ switch (s->download_fw_state - 1) {
+ case 0: /* Request for firmware download */
+ return 0x55;
+ case 1: /* Prepare erase done */
+ return 0x8F;
+ case 2: /* Erase done */
+ return 0x82;
+ case 3: /* Read flash */
+ return 0x84;
+ case 4: /* Check firmware */
+ if (s->download_fw_size > 0) {
+ s->download_fw_size--;
+ s->download_fw_state = 4;
+ }
+ return 0xFF;
+ case 5: /* Prepare program */
+ return 0x8F;
+ case 6: /* Program flash */
+ return 0x83;
+ case 7: /* Read flash */
+ return 0x84;
+ case 8: /* Verify data */
+ /* TODO */
+ if (s->download_fw_size > 0) {
+ s->download_fw_size--;
+ s->download_fw_state = 8;
+ }
+ return 0xFF;
+ default:
+ s->download_fw_state = 0;
+ return 0;
+ }
+ default:
+ hw_error("mcs5000: bad read offset 0x%x\n", address);
+ }
+}
+
+
+static void mcs5000_write(void *opaque, uint32_t address, uint8_t offset,
+ uint8_t val)
+{
+ MCS5000State *s = (MCS5000State *)opaque;
+
+ if (offset > 0) {
+ hw_error("mcs5000: bad write size\n");
+ }
+
+ switch (address) {
+ case MCS5000_TK_LED_ONOFF:
+ break;
+ case MCS5000_TK_LED_DIMMING:
+ break;
+ case MCS5000_TK_FW_ADDR:
+ if (s->download_fw_state == 3 || s->download_fw_state == 7) {
+ s->download_fw_size = val;
+ } else if (s->download_fw_state == 6) {
+ /* TODO */
+ /* Get firmware image */
+ }
+ break;
+ default:
+ hw_error("mcs5000: bad write offset 0x%x\n", address);
+ }
+}
+
+static int mcs5000_tk_event(void *opaque, int keycode)
+{
+ MCS5000State *s = (MCS5000State *)opaque;
+ int k, push = 0;
+
+ push = (keycode & 0x80) ? 0 : 1; /* Key push from qemu */
+ keycode &= ~(0x80); /* Strip qemu key release bit */
+
+ k = map[keycode];
+
+ /* Don't report unknown keypress */
+ if (k < 0) {
+ return -1;
+ }
+
+ s->status = k | (push << 7);
+ qemu_irq_raise(s->irq);
+ return 0;
+}
+
+static void mcs5000_init_keymap(int keycodes[])
+{
+ int i;
+
+ for (i = 0; i < 0x80; i++) {
+ map[i] = -1;
+ }
+ for (i = 0; i < 0x80; i++) {
+ map[keycodes[i]] = i;
+ }
+}
+
+static void mcs5000_save(QEMUFile *f, void *opaque)
+{
+ MCS5000State *s = (MCS5000State *)opaque;
+
+ qemu_put_8s(f, &s->status);
+
+ qemu_put_sbe32s(f, &s->download_fw_state);
+ qemu_put_sbe32s(f, &s->download_fw_size);
+}
+
+static int mcs5000_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MCS5000State *s = (MCS5000State *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_8s(f, &s->status);
+
+ qemu_get_sbe32s(f, &s->download_fw_state);
+ qemu_get_sbe32s(f, &s->download_fw_size);
+
+ return 0;
+}
+
+static int mcs5000_init(I2CAddressableState *s, int keycodes[])
+{
+ MCS5000State *t = FROM_I2CADDR_SLAVE(MCS5000State, s);
+
+ qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+ qemu_add_kbd_event_handler(mcs5000_tk_event, t, "MCS5000 Touchkey");
+
+ mcs5000_reset(&s->i2c.qdev);
+ mcs5000_init_keymap(keycodes);
+
+ register_savevm(&s->i2c.qdev, "mcs5000", -1, 1,
+ mcs5000_save, mcs5000_load, s);
+
+ return 0;
+}
+
+static int mcs5000_universal_init(I2CAddressableState *i2c)
+{
+ return mcs5000_init(i2c, universal_tk_keymap);
+}
+
+static I2CAddressableDeviceInfo mcs5000_universal_info = {
+ .i2c.qdev.name = "mcs5000.universal",
+ .i2c.qdev.size = sizeof(MCS5000State),
+ .i2c.qdev.reset = mcs5000_reset,
+ .init = mcs5000_universal_init,
+ .read = mcs5000_read,
+ .write = mcs5000_write,
+ .size = 1,
+ .rev = 0
+};
+
+static void mcs5000_register_devices(void)
+{
+ i2c_addressable_register_device(&mcs5000_universal_info);
+}
+
+device_init(mcs5000_register_devices)
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;
if (keycode == KEYCODE_EXTENDED) {
s->kbd_extended = 1;
- return;
+ return 0;
}
if (s->kbd_extended) {
}
s->kbd_extended = 0;
+ return 0;
}
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;
}
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));
#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];
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] = {
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);
}
}
/* 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];
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
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. */
#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;
int secs_cur;
int blocks;
uint8_t *blockwp;
+
+ int page_shift;
+ int block_shift;
+ int dbuf_num;
+
+ int superload;
} OneNANDState;
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)
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);
s->bdrv_cur = s->bdrv;
s->current = s->image;
s->secs_cur = s->secs;
+ s->superload = 0;
if (cold) {
/* Lock the whole flash */
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;
}
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;
}
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))
((((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]; \
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 */
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)
*/
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)
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)
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;
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;
{
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 */
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;
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;
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;
}
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;
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;
}
}
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;
}
}
+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;
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);
[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;
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)
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);
--- /dev/null
+/*
+ * 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;
+}
#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
--- /dev/null
+/*
+ * ARM PrimeCell PL192 Vector Interrupt Controller
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "primecell.h"
+
+
+#define PL192_INT_SOURCES 32
+#define PL192_DAISY_IRQ PL192_INT_SOURCES
+#define PL192_NO_IRQ PL192_INT_SOURCES+1
+#define PL192_PRIO_LEVELS 16
+
+#define PL192_IRQSTATUS 0x00
+#define PL192_FIQSTATUS 0x04
+#define PL192_RAWINTR 0x08
+#define PL192_INTSELECT 0x0C
+#define PL192_INTENABLE 0x10
+#define PL192_INTENCLEAR 0x14
+#define PL192_SOFTINT 0x18
+#define PL192_SOFTINTCLEAR 0x1C
+#define PL192_PROTECTION 0x20
+#define PL192_SWPRIORITYMASK 0x24
+#define PL192_PRIORITYDAISY 0x28
+#define PL192_VECTADDR 0xF00
+#define PL192_IOMEM_SIZE 0x1000
+
+#define PL190_ITCR 0x300
+#define PL190_VECTADDR 0x30
+#define PL190_DEFVECTADDR 0x34
+
+#define PL192_IOMEM_SIZE 0x1000
+
+
+typedef struct pl192_state_s {
+ SysBusDevice busdev;
+ uint32_t instance;
+
+ /* Control registers */
+ uint32_t irq_status;
+ uint32_t fiq_status;
+ uint32_t rawintr;
+ uint32_t intselect;
+ uint32_t intenable;
+ uint32_t softint;
+ uint32_t protection;
+ uint32_t sw_priority_mask;
+ uint32_t vect_addr[PL192_INT_SOURCES];
+ uint32_t vect_priority[PL192_INT_SOURCES];
+ uint32_t address;
+
+ /* Currently processed interrupt and
+ highest priority interrupt */
+ uint32_t current;
+ uint32_t current_highest;
+
+ /* Priority masking logic */
+ int32_t stack_i;
+ uint32_t priority_stack[PL192_PRIO_LEVELS+1];
+ uint8_t irq_stack[PL192_PRIO_LEVELS+1];
+ uint32_t priority;
+
+ /* Daisy-chain interface */
+ uint32_t daisy_vectaddr;
+ uint32_t daisy_priority;
+ struct pl192_state_s *daisy_callback;
+ uint8_t daisy_input;
+
+ /* Parent interrupts */
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ /* Next controller in chain */
+ struct pl192_state_s *daisy;
+} pl192_state;
+
+const unsigned char pl192_id[] =
+{ 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
+
+
+static void pl192_update(pl192_state *);
+
+static void pl192_raise(pl192_state *s, int is_fiq)
+{
+ if (is_fiq) {
+ if (s->fiq) {
+ /* Raise parent FIQ */
+ qemu_irq_raise(s->fiq);
+ } else {
+ if (s->daisy) {
+ /* FIQ is directly propagated through daisy chain */
+ pl192_raise(s->daisy, is_fiq);
+ } else {
+ hw_error("pl192: cannot raise FIQ. This usually means that "
+ "initialization was done incorrectly.\n");
+ }
+ }
+ } else {
+ if (s->irq) {
+ /* Raise parent IRQ */
+ qemu_irq_raise(s->irq);
+ } else {
+ if (s->daisy) {
+ /* Setup daisy input of the next chained contorller and force
+ it to update it's state */
+ s->daisy->daisy_vectaddr = s->address;
+ s->daisy->daisy_callback = s;
+ s->daisy->daisy_input = 1;
+ pl192_update(s->daisy);
+ } else {
+ hw_error("pl192: cannot raise IRQ. This usually means that "
+ "initialization was done incorrectly.\n");
+ }
+ }
+ }
+}
+
+static void pl192_lower(pl192_state *s, int is_fiq)
+{
+ /* Lower parrent interrupt if there is one */
+ if (is_fiq && s->fiq) {
+ qemu_irq_lower(s->fiq);
+ }
+ if (!is_fiq && s->irq) {
+ qemu_irq_lower(s->irq);
+ }
+ /* Propagate to the previous controller in chain if needed */
+ if (s->daisy) {
+ if (!is_fiq) {
+ s->daisy->daisy_input = 0;
+ pl192_update(s->daisy);
+ } else {
+ pl192_lower(s->daisy, is_fiq);
+ }
+ }
+}
+
+/* Find interrupt of the highest priority */
+static uint32_t pl192_priority_sorter(pl192_state *s)
+{
+ int i;
+ uint32_t prio_irq[PL192_PRIO_LEVELS];
+
+ for (i = 0; i < PL192_PRIO_LEVELS; i++) {
+ prio_irq[i] = PL192_NO_IRQ;
+ }
+ if (s->daisy_input) {
+ prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
+ }
+ for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
+ if (s->irq_status & (1 << i)) {
+ prio_irq[s->vect_priority[i]] = i;
+ }
+ }
+ for (i = 0; i < PL192_PRIO_LEVELS; i++) {
+ if ((s->sw_priority_mask & (1 << i)) &&
+ prio_irq[i] <= PL192_DAISY_IRQ) {
+ return prio_irq[i];
+ }
+ }
+ return PL192_NO_IRQ;
+}
+
+static void pl192_update(pl192_state *s)
+{
+ /* TODO: does SOFTINT affects IRQ_STATUS??? */
+ s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
+ s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
+ if (s->fiq_status) {
+ pl192_raise(s, 1);
+ } else {
+ pl192_lower(s, 1);
+ }
+ if (s->irq_status || s->daisy_input) {
+ s->current_highest = pl192_priority_sorter(s);
+ if (s->current_highest < PL192_INT_SOURCES) {
+ s->address = s->vect_addr[s->current_highest];
+ } else {
+ s->address = s->daisy_vectaddr;
+ }
+ if (s->current_highest != s->current) {
+ if (s->current_highest < PL192_INT_SOURCES) {
+ if (s->vect_priority[s->current_highest] >= s->priority) {
+ return ;
+ }
+ }
+ if (s->current_highest == PL192_DAISY_IRQ) {
+ if (s->daisy_priority >= s->priority) {
+ return ;
+ }
+ }
+ if (s->current_highest <= PL192_DAISY_IRQ) {
+ pl192_raise(s, 0);
+ } else {
+ pl192_lower(s, 0);
+ }
+ }
+ } else {
+ s->current_highest = PL192_NO_IRQ;
+ pl192_lower(s, 0);
+ }
+}
+
+/* Set priority level when an interrupt have been acknoledged by CPU.
+ Also save interrupt id and priority to stack so it can be restored
+ lately. */
+static inline void pl192_mask_priority(pl192_state *s)
+{
+ if (s->stack_i >= PL192_INT_SOURCES) {
+ hw_error("pl192: internal error\n");
+ }
+ s->stack_i++;
+ if (s->current == PL192_DAISY_IRQ) {
+ s->priority = s->daisy_priority;
+ } else {
+ s->priority = s->vect_priority[s->current];
+ }
+ s->priority_stack[s->stack_i] = s->priority;
+ s->irq_stack[s->stack_i] = s->current;
+}
+
+/* Set priority level when interrupt have been successfully processed by CPU.
+ Also restore previous interrupt id and priority level. */
+static inline void pl192_unmask_priority(pl192_state *s)
+{
+ if (s->stack_i < 1) {
+ hw_error("pl192: internal error\n");
+ }
+ s->stack_i--;
+ s->priority = s->priority_stack[s->stack_i];
+ s->current = s->irq_stack[s->stack_i];
+}
+
+/* IRQ was acknoledged by CPU. Update controller state accordingly */
+static uint32_t pl192_irq_ack(pl192_state *s)
+{
+ int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
+ uint32_t res = s->address;
+
+ s->current = s->current_highest;
+ pl192_mask_priority(s);
+ if (is_daisy) {
+ pl192_mask_priority(s->daisy_callback);
+ }
+ pl192_update(s);
+ return res;
+}
+
+/* IRQ was processed by CPU. Update controller state accrodingly */
+static void pl192_irq_fin(pl192_state *s)
+{
+ int is_daisy = (s->current == PL192_DAISY_IRQ);
+
+ pl192_unmask_priority(s);
+ if (is_daisy) {
+ pl192_unmask_priority(s->daisy_callback);
+ }
+ pl192_update(s);
+ if (s->current == PL192_NO_IRQ) {
+ pl192_lower(s, 0);
+ }
+}
+
+static uint32_t pl192_read(void *opaque, target_phys_addr_t offset)
+{
+ pl192_state *s = (pl192_state *) opaque;
+
+ if (offset & 3) {
+ hw_error("pl192: bad read offset " TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl192_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x180) {
+ return s->vect_addr[(offset - 0x100) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x280) {
+ return s->vect_priority[(offset - 0x200) >> 2];
+ }
+
+ switch (offset) {
+ case PL192_IRQSTATUS:
+ return s->irq_status;
+ case PL192_FIQSTATUS:
+ return s->fiq_status;
+ case PL192_RAWINTR:
+ return s->rawintr;
+ case PL192_INTSELECT:
+ return s->intselect;
+ case PL192_INTENABLE:
+ return s->intenable;
+ case PL192_SOFTINT:
+ return s->softint;
+ case PL192_PROTECTION:
+ return s->protection;
+ case PL192_SWPRIORITYMASK:
+ return s->sw_priority_mask;
+ case PL192_PRIORITYDAISY:
+ return s->daisy_priority;
+ case PL192_INTENCLEAR:
+ case PL192_SOFTINTCLEAR:
+ hw_error("pl192: attempt to read write-only register (offset = "
+ TARGET_FMT_plx ")\n", offset);
+ case PL192_VECTADDR:
+ return pl192_irq_ack(s);
+ /* Workaround for kernel code using PL190 */
+ case PL190_ITCR:
+ case PL190_VECTADDR:
+ case PL190_DEFVECTADDR:
+ return 0;
+ default:
+ hw_error("pl192: bad read offset " TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+static void pl192_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl192_state *s = (pl192_state *) opaque;
+
+ if (offset & 3) {
+ hw_error("pl192: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ hw_error("pl192: attempt to write to a read-only register (offset = "
+ TARGET_FMT_plx ")\n", offset);
+ }
+ if (offset >= 0x100 && offset < 0x180) {
+ s->vect_addr[(offset - 0x100) >> 2] = value;
+ pl192_update(s);
+ return;
+ }
+ if (offset >= 0x200 && offset < 0x280) {
+ s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
+ pl192_update(s);
+ return;
+ }
+
+ switch (offset) {
+ case PL192_IRQSTATUS:
+ /* This is a readonly register, but linux tries to write to it
+ anyway. Ignore the write. */
+ return;
+ case PL192_FIQSTATUS:
+ case PL192_RAWINTR:
+ hw_error("pl192: attempt to write to a read-only register (offset = "
+ TARGET_FMT_plx ")\n", offset);
+ break;
+ case PL192_INTSELECT:
+ s->intselect = value;
+ break;
+ case PL192_INTENABLE:
+ s->intenable |= value;
+ break;
+ case PL192_INTENCLEAR:
+ s->intenable &= ~value;
+ break;
+ case PL192_SOFTINT:
+ s->softint |= value;
+ break;
+ case PL192_SOFTINTCLEAR:
+ s->softint &= ~value;
+ break;
+ case PL192_PROTECTION:
+ /* TODO: implement protection */
+ s->protection = value & 1;
+ break;
+ case PL192_SWPRIORITYMASK:
+ s->sw_priority_mask = value & 0xffff;
+ break;
+ case PL192_PRIORITYDAISY:
+ s->daisy_priority = value & 0xf;
+ break;
+ case PL192_VECTADDR:
+ pl192_irq_fin(s);
+ return;
+ case PL190_ITCR:
+ case PL190_VECTADDR:
+ case PL190_DEFVECTADDR:
+ /* NB: This thing is not present here, but linux wants to write it */
+ /* Ignore written value */
+ return;
+ default:
+ hw_error("pl192: bad write offset " TARGET_FMT_plx "\n", offset);
+ return;
+ }
+
+ pl192_update(s);
+}
+
+static void pl192_irq_handler(void *opaque, int irq, int level)
+{
+ pl192_state *s = (pl192_state *) opaque;
+
+ if (level) {
+ s->rawintr |= 1 << irq;
+ } else {
+ s->rawintr &= ~(1 << irq);
+ }
+ pl192_update(opaque);
+}
+
+static void pl192_reset(DeviceState *d)
+{
+ pl192_state *s = FROM_SYSBUS(pl192_state, sysbus_from_qdev(d));
+ int i;
+
+ for (i = 0; i < PL192_INT_SOURCES; i++) {
+ s->vect_priority[i] = 0xf;
+ }
+ s->sw_priority_mask = 0xffff;
+ s->daisy_priority = 0xf;
+ s->current = PL192_NO_IRQ;
+ s->current_highest = PL192_NO_IRQ;
+ s->stack_i = 0;
+ s->priority_stack[0] = 0x10;
+ s->irq_stack[0] = PL192_NO_IRQ;
+ s->priority = 0x10;
+}
+
+static CPUReadMemoryFunc * const pl192_readfn[] = {
+ pl192_read,
+ pl192_read,
+ pl192_read
+};
+
+static CPUWriteMemoryFunc * const pl192_writefn[] = {
+ pl192_write,
+ pl192_write,
+ pl192_write
+};
+
+static void pl192_save(QEMUFile *f, void *opaque)
+{
+ pl192_state *s = (pl192_state *) opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->irq_status);
+ qemu_put_be32s(f, &s->fiq_status);
+ qemu_put_be32s(f, &s->rawintr);
+ qemu_put_be32s(f, &s->intselect);
+ qemu_put_be32s(f, &s->intenable);
+ qemu_put_be32s(f, &s->softint);
+ qemu_put_be32s(f, &s->protection);
+ qemu_put_be32s(f, &s->sw_priority_mask);
+
+ for (i = 0; i < PL192_INT_SOURCES; i++) {
+ qemu_put_be32s(f, &s->vect_addr[i]);
+ qemu_put_be32s(f, &s->vect_priority[i]);
+ }
+
+ qemu_put_be32s(f, &s->address);
+ qemu_put_be32s(f, &s->current);
+ qemu_put_be32s(f, &s->current_highest);
+ qemu_put_sbe32s(f, &s->stack_i);
+
+ for (i = 0; i <= PL192_PRIO_LEVELS; i++) {
+ qemu_put_be32s(f, &s->priority_stack[i]);
+ qemu_put_8s (f, &s->irq_stack[i]);
+ }
+
+ qemu_put_be32s(f, &s->priority);
+ qemu_put_be32s(f, &s->daisy_vectaddr);
+ qemu_put_be32s(f, &s->daisy_priority);
+ qemu_put_8s (f, &s->daisy_input);
+}
+
+static int pl192_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pl192_state *s = (pl192_state *) opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->irq_status);
+ qemu_get_be32s(f, &s->fiq_status);
+ qemu_get_be32s(f, &s->rawintr);
+ qemu_get_be32s(f, &s->intselect);
+ qemu_get_be32s(f, &s->intenable);
+ qemu_get_be32s(f, &s->softint);
+ qemu_get_be32s(f, &s->protection);
+ qemu_get_be32s(f, &s->sw_priority_mask);
+
+ for (i = 0; i < PL192_INT_SOURCES; i++) {
+ qemu_get_be32s(f, &s->vect_addr[i]);
+ qemu_get_be32s(f, &s->vect_priority[i]);
+ }
+
+ qemu_get_be32s(f, &s->address);
+ qemu_get_be32s(f, &s->current);
+ qemu_get_be32s(f, &s->current_highest);
+ qemu_get_sbe32s(f, &s->stack_i);
+
+ for (i = 0; i <= PL192_PRIO_LEVELS; i++) {
+ qemu_get_be32s(f, &s->priority_stack[i]);
+ qemu_get_8s (f, &s->irq_stack[i]);
+ }
+
+ qemu_get_be32s(f, &s->priority);
+ qemu_get_be32s(f, &s->daisy_vectaddr);
+ qemu_get_be32s(f, &s->daisy_priority);
+ qemu_get_8s (f, &s->daisy_input);
+
+ return 0;
+}
+
+DeviceState *pl192_init(target_phys_addr_t base, int instance, ...)
+{
+ va_list va;
+ qemu_irq irq;
+ int n;
+
+ DeviceState *dev = qdev_create(NULL, "pl192");
+ SysBusDevice *s = sysbus_from_qdev(dev);
+ qdev_prop_set_uint32(dev, "instance", instance);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, base);
+
+ va_start(va, instance);
+ n = 0;
+ while (1) {
+ irq = va_arg(va, qemu_irq);
+ if (!irq)
+ break;
+ sysbus_connect_irq(s, n, irq);
+ n++;
+ }
+ return dev;
+}
+
+static int pl192_init1(SysBusDevice *dev)
+{
+ pl192_state *s = FROM_SYSBUS(pl192_state, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ /* Allocate IRQs */
+ qdev_init_gpio_in(&dev->qdev, pl192_irq_handler, PL192_INT_SOURCES);
+
+ /* Map Interrupt Controller registers to memory */
+ iomemtype = cpu_register_io_memory(pl192_readfn, pl192_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, PL192_IOMEM_SIZE, iomemtype);
+
+ /* TODO: Interrupt Controller coprocessor??? */
+ pl192_reset(&s->busdev.qdev);
+
+ register_savevm(&dev->qdev, "pl192", s->instance, 1,
+ pl192_save, pl192_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo pl192_info = {
+ .init = pl192_init1,
+ .qdev.name = "pl192",
+ .qdev.size = sizeof(pl192_state),
+ .qdev.reset = pl192_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("instance", pl192_state, instance, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pl192_register_devices(void)
+{
+ sysbus_register_withprop(&pl192_info);
+}
+
+void pl192_chain(void *first, void *next)
+{
+ pl192_state *s1 = FROM_SYSBUS(pl192_state, (SysBusDevice *)first);
+ pl192_state *s2 = FROM_SYSBUS(pl192_state, (SysBusDevice *)next);
+
+ s2->daisy = s1;
+}
+
+device_init(pl192_register_devices)
--- /dev/null
+/*
+ * ARM PrimeCell PL330 DMA Controller
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "primecell.h"
+#include "qemu-timer.h"
+
+
+#define PL330_CHAN_NUM 8
+#define PL330_PERIPH_NUM 32
+#define PL330_MAX_BURST_LEN 128
+#define PL330_INSN_MAXSIZE 6
+
+#define PL330_FIFO_OK 0
+#define PL330_FIFO_STALL 1
+#define PL330_FIFO_ERR (-1)
+
+#define PL330_FAULT_UNDEF_INSTR (1 << 0)
+#define PL330_FAULT_OPERAND_INVALID (1 << 1)
+#define PL330_FAULT_DMAGO_ER (1 << 4)
+#define PL330_FAULT_EVENT_ER (1 << 5)
+#define PL330_FAULT_CH_PERIPH_ER (1 << 6)
+#define PL330_FAULT_CH_RDWR_ER (1 << 7)
+#define PL330_FAULT_MFIFO_ER (1 << 12)
+#define PL330_FAULT_INSTR_FETCH_ER (1 << 16)
+#define PL330_FAULT_DATA_WRITE_ER (1 << 17)
+#define PL330_FAULT_DATA_READ_ER (1 << 18)
+#define PL330_FAULT_DBG_INSTR (1 << 30)
+#define PL330_FAULT_LOCKUP_ER (1 << 31)
+
+#define PL330_UNTAGGED 0xff
+
+#define PL330_SINGLE 0x0
+#define PL330_BURST 0x1
+
+#define PL330_WATCHDOG_LIMIT 1024
+
+/* IOMEM mapped registers */
+#define PL330_REG_DS 0x000
+#define PL330_REG_DPC 0x004
+#define PL330_REG_INTEN 0x020
+#define PL330_REG_ES 0x024
+#define PL330_REG_INTSTATUS 0x028
+#define PL330_REG_INTCLR 0x02C
+#define PL330_REG_FSM 0x030
+#define PL330_REG_FSC 0x034
+#define PL330_REG_FTM 0x038
+#define PL330_REG_FTC_BASE 0x040
+#define PL330_REG_CS_BASE 0x100
+#define PL330_REG_CPC_BASE 0x104
+#define PL330_REG_CHANCTRL 0x400
+#define PL330_REG_DBGSTATUS 0xD00
+#define PL330_REG_DBGCMD 0xD04
+#define PL330_REG_DBGINST0 0xD08
+#define PL330_REG_DBGINST1 0xD0C
+#define PL330_REG_CONFIG 0xE00
+#define PL330_REG_ID 0xFE0
+
+#define PL330_IOMEM_SIZE 0x1000
+
+
+static const uint32_t pl330_id[] =
+{ 0x30, 0x13, 0x04, 0x00, 0xB1, 0x05, 0xF0, 0x0D };
+
+
+/* DMA chanel states as they are described in PL330 Technical Reference Manual
+ Most of them will not be used in emulation. */
+enum pl330_chan_enum {
+ pl330_chan_stopped = 0,
+ pl330_chan_executing = 1,
+ pl330_chan_completing,
+ pl330_chan_waiting_periph,
+ pl330_chan_at_barrier,
+ pl330_chan_waiting_event = 4,
+ pl330_chan_updating_pc = 3,
+ pl330_chan_cache_miss,
+ pl330_chan_fault_completing,
+ pl330_chan_fault = 15,
+ pl330_chan_killing
+};
+
+struct pl330_chan_state;
+struct pl330_fifo;
+struct pl330_queue_entry;
+struct pl330_queue;
+struct pl330_state;
+struct pl330_insn_desc;
+
+typedef struct pl330_chan_state pl330_chan_state;
+typedef struct pl330_fifo pl330_fifo;
+typedef struct pl330_queue_entry pl330_queue_entry;
+typedef struct pl330_queue pl330_queue;
+typedef struct pl330_state pl330_state;
+typedef struct pl330_insn_desc pl330_insn_desc;
+
+
+struct pl330_chan_state {
+ uint32_t src;
+ uint32_t dst;
+ uint32_t pc;
+ uint32_t control;
+ uint32_t status;
+ uint32_t lc[2];
+ uint32_t fault_type;
+
+ uint8_t ns;
+ uint8_t is_manager;
+ uint8_t request_flag;
+ uint8_t wakeup;
+ uint8_t wfp_sbp;
+
+ enum pl330_chan_enum state;
+ uint8_t stall;
+
+ pl330_state *parent;
+ uint8_t tag;
+ uint32_t watchdog_timer;
+};
+
+struct pl330_fifo {
+ uint8_t *buf;
+ uint8_t *tag;
+ int32_t s, t;
+ int32_t buf_size;
+};
+
+struct pl330_queue_entry {
+ uint32_t addr;
+ int32_t len;
+ int32_t n;
+ int32_t inc;
+ int32_t z;
+ uint8_t tag;
+ uint8_t seqn;
+};
+
+struct pl330_queue {
+ pl330_queue_entry *queue;
+ int32_t queue_size;
+ uint8_t *lo_seqn;
+ uint8_t *hi_seqn;
+};
+
+struct pl330_state {
+ SysBusDevice busdev;
+ uint32_t instance;
+
+ pl330_chan_state manager;
+ pl330_chan_state *chan;
+ pl330_fifo fifo;
+ pl330_queue read_queue;
+ pl330_queue write_queue;
+
+ /* Config registers. cfg[5] = CfgDn. */
+ uint32_t inten;
+ uint32_t int_status;
+ uint32_t ev_status;
+ uint32_t cfg[6];
+ uint32_t dbg[2];
+ uint8_t debug_status;
+
+ qemu_irq irq_abort;
+ qemu_irq *irq;
+
+ uint8_t num_faulting;
+
+ int32_t chan_num;
+ int32_t periph_num;
+ uint32_t event_num;
+
+ QEMUTimer *timer; /* is used for restore dma. */
+ int8_t periph_busy[PL330_PERIPH_NUM];
+};
+
+struct pl330_insn_desc {
+ /* OPCODE of the instruction */
+ uint8_t opcode;
+ /* Mask so we can select several sibling instructions, such as
+ DMALD, DMALDS and DMALDB */
+ uint8_t opmask;
+ /* Size of instruction in bytes */
+ uint8_t size;
+ /* Interpreter */
+ void (*exec)(pl330_chan_state *, uint8_t opcode, uint8_t *args, int len);
+};
+
+
+/* MFIFO Implementation */
+
+/*
+ * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are
+ * stored in this buffer. Data is stored in BUF field, tags - in the
+ * corresponding array elemnets of TAG field.
+ */
+
+/* Initialize queue. */
+static void pl330_fifo_init(pl330_fifo *s, uint32_t size)
+{
+ s->buf = qemu_mallocz(size * sizeof(uint8_t));
+ s->tag = qemu_mallocz(size * sizeof(uint8_t));
+ s->buf_size = size;
+}
+
+/* Cyclic increment */
+static inline int pl330_fifo_inc(int x, int size)
+{
+ return (x + 1) % size;
+}
+
+/* Number of empty bytes in MFIFO */
+static inline int pl330_fifo_num_free(pl330_fifo *s)
+{
+ if (s->t < s->s) {
+ return s->s - s->t;
+ } else {
+ return s->buf_size - s->t + s->s;
+ }
+}
+
+/* Number of bytes in MFIFO */
+static inline int pl330_fifo_num_used(pl330_fifo *s)
+{
+ if (s->t >= s->s) {
+ return s->t - s->s;
+ } else {
+ return s->buf_size - s->s + s->t;
+ }
+}
+
+/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG.
+ Zero returned on success, PL330_FIFO_STALL if there is no enough free
+ space in MFIFO to store requested amount of data. If push was unsaccessful
+ no data is stored to MFIFO. */
+static int pl330_fifo_push(pl330_fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+ int i;
+ int old_s, old_t;
+
+ old_s = s->s;
+ old_t = s->t;
+ for (i = 0; i < len; i++) {
+ if (pl330_fifo_inc(s->t, s->buf_size) != s->s) {
+ s->buf[s->t] = buf[i];
+ s->tag[s->t] = tag;
+ s->t = pl330_fifo_inc(s->t, s->buf_size);
+ } else {
+ /* Rollback transaction */
+ s->s = old_s;
+ s->t = old_t;
+ return PL330_FIFO_STALL;
+ }
+ }
+ return PL330_FIFO_OK;
+}
+
+/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each
+ byte is veryfied. Zero returned on success, PL330_FIFO_ERR on tag missmatch
+ and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was
+ unsaccessful no data is removed from MFIFO. */
+static int pl330_fifo_get(pl330_fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+ int i, ret;
+ int old_s, old_t;
+
+ old_s = s->s;
+ old_t = s->t;
+ for (i = 0; i < len; i++) {
+ if (s->t != s->s && s->tag[s->s] == tag) {
+ buf[i] = s->buf[s->s];
+ s->s = pl330_fifo_inc(s->s, s->buf_size);
+ } else {
+ /* Rollback transaction */
+ if (s->t == s->s)
+ ret = PL330_FIFO_STALL;
+ else
+ ret = PL330_FIFO_ERR;
+ s->s = old_s;
+ s->t = old_t;
+ return ret;
+ }
+ }
+ return PL330_FIFO_OK;
+}
+
+/* Reset MFIFO. This completely erases all data in it. */
+static inline void pl330_fifo_reset(pl330_fifo *s)
+{
+ s->s = 0;
+ s->t = 0;
+}
+
+/* Return tag of the first byte stored in MFIFO. If MFIFO is empty
+ PL330_UNTAGGED is returned. */
+static inline uint8_t pl330_fifo_tag(pl330_fifo *s)
+{
+ if (s->t == s->s)
+ return PL330_UNTAGGED;
+ return s->tag[s->s];
+}
+
+/* Returns non-zero if tag TAG is present in fifo or zero otherwise */
+static int pl330_fifo_has_tag(pl330_fifo *s, uint8_t tag)
+{
+ int i;
+
+ for (i = s->s; i != s->t; i = pl330_fifo_inc(i, s->buf_size)) {
+ if (s->tag[i] == tag)
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove all entry tagged with TAG from MFIFO */
+static void pl330_fifo_tagged_remove(pl330_fifo *s, uint8_t tag)
+{
+ int i, t;
+
+ t = s->s;
+ for (i = s->s; i != s->t; i = pl330_fifo_inc(i, s->buf_size)) {
+ if (s->tag[i] != tag) {
+ s->buf[t] = s->buf[i];
+ s->tag[t] = s->tag[i];
+ t++;
+ }
+ }
+ s->t = t;
+}
+
+
+/* Read-Write Queue implementation */
+
+/*
+ * Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores).
+ * Each instructions is described by source (for loads) or destination (for
+ * stores) address ADDR, width of data to be loaded/stored LEN, number of
+ * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel
+ * this instruction belongs to. Queue does not store any information about
+ * nature of the instruction: is it load or store. PL330 has different queues
+ * for loads and stores so this is already known at the top level where it matters.
+ *
+ * Queue works as FIFO for instructions with equivalent tags, but can issue
+ * instructions with different tags in arbitrary order. SEQN field attached to
+ * each instruction helps to achieve this. For each TAG queue contains
+ * instructions with consecutive SEQN values ranged from LO_SEQN[TAG] to
+ * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is
+ * followed by SEQN=0.
+ *
+ * Z bit indicates that zeroes should be stored. Thus no MFIFO fetches
+ * are performed in this case.
+ */
+
+static void pl330_queue_reset(pl330_queue *s)
+{
+ int i;
+
+ for (i = 0; i < s->queue_size; i++)
+ s->queue[i].tag = PL330_UNTAGGED;
+}
+
+/* Initialize queue */
+static void pl330_queue_init(pl330_queue *s, int size, int channum)
+{
+ s->queue = (pl330_queue_entry *)
+ qemu_mallocz(size * sizeof(pl330_queue_entry));
+ s->lo_seqn = (uint8_t *)qemu_mallocz(channum * sizeof(uint8_t));
+ s->hi_seqn = (uint8_t *)qemu_mallocz(channum * sizeof(uint8_t));
+ s->queue_size = size;
+}
+
+/* Returns pointer to an empty slot or NULL if queue is full */
+static pl330_queue_entry *pl330_queue_find_empty(pl330_queue *s)
+{
+ int i;
+
+ for (i = 0; i < s->queue_size; i++)
+ if (s->queue[i].tag == PL330_UNTAGGED)
+ return &s->queue[i];
+ return NULL;
+}
+
+/* Puts instruction to queue.
+ Return value:
+ - zero - OK
+ - non-zero - queue is full */
+static int pl330_insn_to_queue(pl330_queue *s, uint32_t addr,
+ int len, int n, int inc, int z, uint8_t tag)
+{
+ pl330_queue_entry *entry = pl330_queue_find_empty(s);
+
+ if (! entry)
+ return 1;
+ entry->tag = tag;
+ entry->addr = addr;
+ entry->len = len;
+ entry->n = n;
+ entry->z = z;
+ entry->inc = inc;
+ entry->seqn = s->hi_seqn[tag];
+ s->hi_seqn[tag]++;
+ return 0;
+}
+
+/* Returns a pointer to queue slot containing instruction which satisfies
+ following conditions:
+ - it has valid tag value (not PL330_UNTAGGED)
+ - it can be issued without violating queue logic (see above)
+ - if TAG argument is not PL330_UNTAGGED this instruction has tag value
+ equivalent to the argument TAG value.
+ If such instruction cannot be found NULL is returned. */
+static pl330_queue_entry *pl330_queue_find_insn(pl330_queue *s,
+ uint8_t tag)
+{
+ int i;
+
+ for (i = 0; i < s->queue_size; i++) {
+ if (s->queue[i].tag != PL330_UNTAGGED) {
+ if (s->queue[i].seqn == s->lo_seqn[s->queue[i].tag] &&
+ (s->queue[i].tag == tag ||
+ tag == PL330_UNTAGGED ||
+ s->queue[i].z))
+ return &s->queue[i];
+ }
+ }
+ return NULL;
+}
+
+/* Removes instruction from queue. */
+static inline void pl330_insn_from_queue(pl330_queue *s,
+ pl330_queue_entry *e)
+{
+ s->lo_seqn[e->tag]++;
+ e->tag = PL330_UNTAGGED;
+}
+
+/* Removes all instructions tagged with TAG from queue. */
+static inline void pl330_tag_from_queue(pl330_queue *s, uint8_t tag)
+{
+ int i;
+
+ for (i = 0; i < s->queue_size; i++) {
+ if (s->queue[i].tag == tag)
+ s->queue[i].tag = PL330_UNTAGGED;
+ }
+}
+
+
+/* DMA instruction execution engine */
+
+/* Moves DMA channel to the FAULT state and updates it's status. */
+static inline void pl330_fault(pl330_chan_state *ch, uint32_t flags)
+{
+ ch->fault_type |= flags;
+ ch->state = pl330_chan_fault;
+ ch->parent->num_faulting++;
+ if (ch->parent->num_faulting == 1) {
+ qemu_irq_raise(ch->parent->irq_abort);
+ }
+}
+
+/*
+ * For information about instructions see PL330 Technical Reference Manual.
+ *
+ * Arguments:
+ * CH - chanel executing the instruction
+ * OPCODE - opcode
+ * ARGS - array of 8-bit arguments
+ * LEN - number of elements in ARGS array
+ */
+static void pl330_dmaaddh(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint16_t im = (((uint16_t)args[0]) << 8) | ((uint16_t)args[1]);
+ uint8_t ra = (opcode >> 1) & 1;
+
+ if (ra) {
+ ch->dst += im;
+ } else {
+ ch->src += im;
+ }
+}
+
+static void pl330_dmaend(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ if (ch->state == pl330_chan_executing && !ch->is_manager) {
+ /* Wait for all transfers to complete */
+ if (pl330_fifo_has_tag(&ch->parent->fifo, ch->tag) ||
+ pl330_queue_find_insn(&ch->parent->read_queue, ch->tag) != NULL ||
+ pl330_queue_find_insn(&ch->parent->write_queue, ch->tag) != NULL) {
+
+ ch->stall = 1;
+ return;
+ }
+ }
+ pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
+ pl330_tag_from_queue(&ch->parent->read_queue, ch->tag);
+ pl330_tag_from_queue(&ch->parent->write_queue, ch->tag);
+ ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmaflushp(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t periph_id;
+
+ if (args[0] & 7) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ periph_id = (args[0] >> 3) & 0x1f;
+ if (periph_id >= ch->parent->periph_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+ pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+ return;
+ }
+ /* Do nothing */
+}
+
+static void pl330_dmago(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t chan_id;
+ uint8_t ns;
+ uint32_t pc;
+ pl330_chan_state *s;
+
+ if (! ch->is_manager) {
+ /* TODO: what error actually is it? */
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ ns = (opcode >> 1) & 1;
+ chan_id = args[0] & 7;
+ if ((args[0] >> 3)) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (chan_id >= ch->parent->chan_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+ (((uint32_t)args[2]) << 8) | (((uint32_t)args[1]));
+ if (ch->parent->chan[chan_id].state != pl330_chan_stopped) {
+ /* TODO: what error actually is it? */
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ns) {
+ pl330_fault(ch, PL330_FAULT_DMAGO_ER);
+ return;
+ }
+ s = &ch->parent->chan[chan_id];
+ s->ns = ns;
+ s->pc = pc;
+ s->state = pl330_chan_executing;
+}
+
+static void pl330_dmald(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t bs = opcode & 3;
+ uint32_t size, num, inc;
+
+ if (bs == 2) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+ (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+ /* Perform NOP */
+ return;
+ }
+ num = ((ch->control >> 4) & 0xf) + 1;
+ size = (uint32_t)1 << ((ch->control >> 1) & 0x7);
+ inc = ch->control & 1;
+ ch->stall = pl330_insn_to_queue(&ch->parent->read_queue, ch->src,
+ size, num, inc, 0, ch->tag);
+ if (inc) {
+ ch->src += size * num;
+ }
+}
+
+static void pl330_dmaldp(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t periph_id;
+
+ if (args[0] & 7) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ periph_id = (args[0] >> 3) & 0x1f;
+ if (periph_id >= ch->parent->periph_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+ pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+ return;
+ }
+ pl330_dmald(ch, opcode, args, len);
+}
+
+static void pl330_dmalp(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t lc = (opcode & 2) >> 1;
+
+ ch->lc[lc] = args[0];
+}
+
+static void pl330_dmalpend(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t nf = (opcode & 0x10) >> 4;
+ uint8_t bs = opcode & 3;
+ uint8_t lc = (opcode & 4) >> 2;
+
+ if (bs == 2) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+ (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+ /* Perform NOP */
+ return;
+ }
+ if (!nf || ch->lc[lc]) {
+ if (nf) {
+ ch->lc[lc]--;
+ }
+ ch->pc -= args[0];
+ ch->pc -= len + 1;
+ /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
+ }
+}
+
+static void pl330_dmakill(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ if (ch->state == pl330_chan_fault ||
+ ch->state == pl330_chan_fault_completing) {
+ /* This is the only way for chanel from faulting state */
+ ch->fault_type = 0;
+ ch->parent->num_faulting--;
+ if (ch->parent->num_faulting == 0) {
+ qemu_irq_lower(ch->parent->irq_abort);
+ }
+ }
+ ch->state = pl330_chan_killing;
+ pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
+ pl330_tag_from_queue(&ch->parent->read_queue, ch->tag);
+ pl330_tag_from_queue(&ch->parent->write_queue, ch->tag);
+ ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmamov(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t rd = args[0] & 7;
+ uint32_t im;
+
+ if ((args[0] >> 3)) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+ (((uint32_t)args[2]) << 8) | (((uint32_t)args[1]));
+ switch (rd) {
+ case 0:
+ ch->src = im;
+ break;
+ case 1:
+ ch->control = im;
+ break;
+ case 2:
+ ch->dst = im;
+ break;
+ default:
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+}
+
+static void pl330_dmanop(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ /* NOP is NOP. */
+}
+
+static void pl330_dmarmb(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ /* Do nothing. Since we do not emulate AXI Bus transactions there is no
+ stalls. So we are allways on barrier. */
+}
+
+static void pl330_dmasev(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t ev_id;
+
+ if (args[0] & 7) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ ev_id = (args[0] >> 3) & 0x1f;
+ if (ev_id >= ch->parent->event_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[3] & (1 << ev_id)) >> ev_id)) {
+ pl330_fault(ch, PL330_FAULT_EVENT_ER);
+ return;
+ }
+ if (ch->parent->inten & (1 << ev_id)) {
+ ch->parent->int_status |= (1 << ev_id);
+ qemu_irq_raise(ch->parent->irq[ev_id]);
+ } else {
+ ch->parent->ev_status |= (1 << ev_id);
+ }
+}
+
+static void pl330_dmast(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t bs = opcode & 3;
+ uint32_t size, num, inc;
+
+ if (bs == 2) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+ (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+ /* Perform NOP */
+ return;
+ }
+ num = ((ch->control >> 18) & 0xf) + 1;
+ size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+ inc = (ch->control >> 14) & 1;
+ ch->stall = pl330_insn_to_queue(&ch->parent->write_queue, ch->dst,
+ size, num, inc, 0, ch->tag);
+ if (inc) {
+ ch->dst += size * num;
+ }
+}
+
+static void pl330_dmastp(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t periph_id;
+
+ if (args[0] & 7) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ periph_id = (args[0] >> 3) & 0x1f;
+ if (periph_id >= ch->parent->periph_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+ pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+ return;
+ }
+ pl330_dmast(ch, opcode, args, len);
+}
+
+static void pl330_dmastz(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint32_t size, num, inc;
+
+ num = ((ch->control >> 18) & 0xf) + 1;
+ size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+ inc = (ch->control >> 14) & 1;
+ ch->stall = pl330_insn_to_queue(&ch->parent->write_queue, ch->dst,
+ size, num, inc, 1, ch->tag);
+ if (inc) {
+ ch->dst += size * num;
+ }
+}
+
+static void pl330_dmawfe(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t ev_id;
+
+ if (args[0] & 5) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ ev_id = (args[0] >> 3) & 0x1f;
+ if (ev_id >= ch->parent->event_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[3] & (1 << ev_id)) >> ev_id)) {
+ pl330_fault(ch, PL330_FAULT_EVENT_ER);
+ return;
+ }
+ ch->wakeup = ev_id;
+}
+
+static void pl330_dmawfp(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ uint8_t bs = opcode & 3;
+ uint8_t periph_id;
+
+ if (args[0] & 7) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ periph_id = (args[0] >> 3) & 0x1f;
+ if (periph_id >= ch->parent->periph_num) {
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+ if (ch->ns > ((ch->parent->cfg[4] & (1 << periph_id)) >> periph_id)) {
+ pl330_fault(ch, PL330_FAULT_CH_PERIPH_ER);
+ return;
+ }
+ switch (bs) {
+ case 0: /* S */
+ ch->request_flag = PL330_SINGLE;
+ ch->wfp_sbp = 0;
+ break;
+ case 1: /* P */
+ ch->request_flag = PL330_BURST;
+ ch->wfp_sbp = 2;
+ break;
+ case 2: /* B */
+ ch->request_flag = PL330_BURST;
+ ch->wfp_sbp = 1;
+ break;
+ default:
+ pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+ return;
+ }
+
+ if (ch->parent->periph_busy[periph_id]) {
+ ch->state = pl330_chan_waiting_periph;
+ } else if (ch->state == pl330_chan_waiting_periph) {
+ ch->state = pl330_chan_executing;
+ }
+}
+
+static void pl330_dmawmb(pl330_chan_state *ch, uint8_t opcode,
+ uint8_t *args, int len)
+{
+ /* Do nothing. Since we do not emulate AXI Bus transactions there is no
+ stalls. So we are allways on barrier. */
+}
+
+/* "NULL" terminated array of the instruction descriptions. */
+static const pl330_insn_desc insn_desc[] = {
+ { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh },
+ { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend },
+ { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp },
+ { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago },
+ { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald },
+ { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp },
+ { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp },
+ /* dmastp must be before dmalpend in tis map, because thay maps
+ * are overrided */
+ { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp },
+ { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend },
+ { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill },
+ { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov },
+ { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop },
+ { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb },
+ { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev },
+ { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast },
+ { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz },
+ { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe },
+ { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp },
+ { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb },
+ { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL }
+};
+
+/* Instructions which can be issued via debug registers. */
+static const pl330_insn_desc debug_insn_desc[] = {
+ { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago },
+ { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill },
+ { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev },
+ { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL }
+};
+
+static inline const pl330_insn_desc *
+ pl330_fetch_insn(pl330_chan_state *ch)
+{
+ uint8_t opcode;
+ int i;
+
+ cpu_physical_memory_read(ch->pc, &opcode, 1);
+ for (i = 0; insn_desc[i].size; i++)
+ if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode)
+ return &insn_desc[i];
+ return NULL;
+}
+
+static inline void pl330_exec_insn(pl330_chan_state *ch,
+ const pl330_insn_desc *insn)
+{
+ uint8_t buf[PL330_INSN_MAXSIZE];
+
+ cpu_physical_memory_read(ch->pc, buf, insn->size);
+ insn->exec(ch, buf[0], &buf[1], insn->size - 1);
+}
+
+static inline void pl330_update_pc(pl330_chan_state *ch,
+ const pl330_insn_desc *insn)
+{
+ ch->pc += insn->size;
+}
+
+/* Try to execute current instruction in channel CH. Number of executed
+ instructions is returned (0 or 1). */
+static int pl330_chan_exec(pl330_chan_state *ch)
+{
+ const pl330_insn_desc *insn;
+
+ if (ch->state != pl330_chan_executing && ch->state != pl330_chan_waiting_periph) {
+ return 0;
+ }
+ ch->stall = 0;
+ insn = pl330_fetch_insn(ch);
+ if (! insn) {
+ pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+ return 0;
+ }
+ pl330_exec_insn(ch, insn);
+ if (ch->state == pl330_chan_executing && !ch->stall) {
+ pl330_update_pc(ch, insn);
+ ch->watchdog_timer = 0;
+ return 1;
+ } else {
+ if (ch->stall) {
+ ch->watchdog_timer++;
+ if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) {
+ pl330_fault(ch, PL330_FAULT_LOCKUP_ER);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Try to execute 1 instruction in each channel, one instruction from read
+ queue and one instruction from write queue. Number of successfully executed
+ instructions is returned. */
+static int pl330_exec_cycle(pl330_chan_state *channel)
+{
+ pl330_state *s = channel->parent;
+ pl330_queue_entry *q;
+ int i;
+ int num_exec = 0;
+ int fifo_res = 0;
+ uint8_t buf[PL330_MAX_BURST_LEN];
+
+ /* Execute one instruction in each channel */
+ num_exec += pl330_chan_exec(channel);
+
+ /* Execute one instruction from read queue */
+ q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED);
+ if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) {
+ cpu_physical_memory_read(q->addr, buf, q->len);
+ fifo_res = pl330_fifo_push(&s->fifo, buf, q->len, q->tag);
+ if (fifo_res == PL330_FIFO_OK) {
+ if (q->inc) {
+ q->addr += q->len;
+ }
+ q->n--;
+ if (! q->n) {
+ pl330_insn_from_queue(&s->read_queue, q);
+ }
+ num_exec++;
+ }
+ }
+
+ /* Execute one instruction from write queue. */
+ q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo));
+ if (q != NULL && (q->z || q->len <= pl330_fifo_num_used(&s->fifo))) {
+ if (q->z) {
+ for (i = 0; i < q->len; i++) {
+ buf[i] = 0;
+ }
+ } else {
+ fifo_res = pl330_fifo_get(&s->fifo, buf, q->len, q->tag);
+ }
+ if (fifo_res == PL330_FIFO_OK || q->z) {
+ cpu_physical_memory_write(q->addr, buf, q->len);
+ if (q->inc) {
+ q->addr += q->len;
+ }
+ q->n--;
+ if (! q->n) {
+ pl330_insn_from_queue(&s->write_queue, q);
+ }
+ num_exec++;
+ }
+ }
+
+ return num_exec;
+}
+
+static int pl330_exec_channel(pl330_chan_state *channel)
+{
+ int insr_exec = 0;
+
+ /* TODO: Is it all right to execute everything or should we do per-cycle
+ simulation? */
+ while (pl330_exec_cycle(channel)) {
+ insr_exec++;
+ }
+
+ /* Detect deadlock */
+ if (channel->state == pl330_chan_executing) {
+ pl330_fault(channel, PL330_FAULT_LOCKUP_ER);
+ }
+ /* Situation when one of the queues has deadlocked but all channels
+ has finished their programs should be impossible. */
+
+ return insr_exec;
+}
+
+
+static inline void pl330_exec(pl330_state *s)
+{
+ int i, insr_exec;
+ do {
+ insr_exec = pl330_exec_channel(&s->manager);
+
+ for (i = 0; i < s->chan_num; i++) {
+ insr_exec += pl330_exec_channel(&s->chan[i]);
+ }
+ } while (insr_exec);
+}
+
+static void pl330_exec_cycle_timer(void *opaque)
+{
+ struct pl330_state *s = (struct pl330_state *)opaque;
+ pl330_exec(s);
+}
+
+/* Stop or restore dma operations */
+static void pl330_dma_stop_irq(void *opaque, int irq, int level)
+{
+ struct pl330_state *s = (struct pl330_state *)opaque;
+
+ if (s->periph_busy[irq] != level) {
+ s->periph_busy[irq] = level;
+ qemu_mod_timer(s->timer, qemu_get_clock(vm_clock));
+ }
+}
+
+static void pl330_debug_exec(pl330_state *s)
+{
+ uint8_t args[5];
+ uint8_t opcode;
+ uint8_t chan_id;
+ int i;
+ pl330_chan_state *ch;
+ const pl330_insn_desc *insn;
+
+ s->debug_status = 1;
+ chan_id = (s->dbg[0] >> 8) & 0x07;
+ opcode = (s->dbg[0] >> 16) & 0xff;
+ args[0] = (s->dbg[0] >> 24) & 0xff;
+ args[1] = (s->dbg[1] >> 0) & 0xff;
+ args[2] = (s->dbg[1] >> 8) & 0xff;
+ args[3] = (s->dbg[1] >> 16) & 0xff;
+ args[4] = (s->dbg[1] >> 24) & 0xff;
+ if (s->dbg[0] & 1) {
+ ch = &s->chan[chan_id];
+ } else {
+ ch = &s->manager;
+ }
+ insn = NULL;
+ for (i = 0; debug_insn_desc[i].size; i++)
+ if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode)
+ insn = &debug_insn_desc[i];
+ if (!insn) {
+ pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+ return ;
+ }
+ ch->stall = 0;
+ insn->exec(ch, opcode, args, insn->size - 1);
+ if (ch->stall) {
+ hw_error("pl330: stall of debug instruction not implemented\n");
+ }
+ s->debug_status = 0;
+}
+
+
+/* IOMEM mapped registers */
+
+static void pl330_iomem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl330_state *s = (pl330_state *) opaque;
+ uint32_t i;
+
+ if (offset & 3) {
+ hw_error("pl330: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+ switch (offset) {
+ case PL330_REG_INTEN:
+ s->inten = value;
+ break;
+ case PL330_REG_INTCLR:
+ for (i = 0; i < s->event_num; i++) {
+ if (s->int_status & s->inten & value & (1 << i)) {
+ qemu_irq_lower(s->irq[i]);
+ }
+ }
+ s->int_status &= ~value;
+ break;
+ case PL330_REG_DBGCMD:
+ if ((value & 3) == 0) {
+ pl330_debug_exec(s);
+ pl330_exec(s);
+ } else {
+ hw_error("pl330: write of illegal value %u for offset "
+ TARGET_FMT_plx "\n", value, offset);
+ }
+ break;
+ case PL330_REG_DBGINST0:
+ s->dbg[0] = value;
+ break;
+ case PL330_REG_DBGINST1:
+ s->dbg[1] = value;
+ break;
+ default:
+ hw_error("pl330: bad write offset " TARGET_FMT_plx "\n", offset);
+ break;
+ }
+}
+
+static uint32_t pl330_iomem_read(void *opaque, target_phys_addr_t offset)
+{
+ pl330_state *s = (pl330_state *) opaque;
+ int chan_id;
+ int i;
+ uint32_t res;
+
+ if (offset & 3) {
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ if (offset >= PL330_REG_ID && offset < PL330_REG_ID + 32) {
+ return pl330_id[(offset - PL330_REG_ID) >> 2];
+ }
+ if (offset >= PL330_REG_CONFIG && offset < PL330_REG_CONFIG + 24) {
+ return s->cfg[(offset - PL330_REG_CONFIG) >> 2];
+ }
+ if (offset >= PL330_REG_CHANCTRL && offset < 0xD00) {
+ offset -= PL330_REG_CHANCTRL;
+ chan_id = offset >> 5;
+ if (chan_id >= s->chan_num) {
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+ switch (offset & 0x1f) {
+ case 0x00:
+ return s->chan[chan_id].src;
+ case 0x04:
+ return s->chan[chan_id].dst;
+ case 0x08:
+ return s->chan[chan_id].control;
+ case 0x0C:
+ return s->chan[chan_id].lc[0];
+ case 0x10:
+ return s->chan[chan_id].lc[1];
+ default:
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+ }
+ if (offset >= PL330_REG_CS_BASE && offset < 0x400) {
+ offset -= PL330_REG_CS_BASE;
+ chan_id = offset >> 3;
+ if (chan_id >= s->chan_num) {
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+ switch ((offset >> 2) & 1) {
+ case 0x0:
+ return (s->chan[chan_id].ns << 20) | (s->chan[chan_id].wakeup << 4) |
+ (s->chan[chan_id].state & 0xf) |
+ (s->chan[chan_id].wfp_sbp << 13);
+ case 0x1:
+ return s->chan[chan_id].pc;
+ default:
+ hw_error("pl330: read error\n");
+ }
+ }
+ if (offset >= PL330_REG_FTC_BASE && offset < 0x100) {
+ offset -= PL330_REG_FTC_BASE;
+ chan_id = offset >> 2;
+ if (chan_id >= s->chan_num) {
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+ return s->chan[chan_id].fault_type;
+ }
+ switch (offset) {
+ case PL330_REG_DS:
+ return (s->manager.ns << 8) | (s->manager.wakeup << 4) |
+ (s->manager.state & 0xf);
+ case PL330_REG_DPC:
+ return s->manager.pc;
+ case PL330_REG_INTEN:
+ return s->inten;
+ case PL330_REG_ES:
+ return s->ev_status;
+ case PL330_REG_INTSTATUS:
+ return s->int_status;
+ case PL330_REG_INTCLR:
+ /* Documentation says that we can't read this register
+ * but linux kernel does it */
+ return 0;
+ case PL330_REG_FSM:
+ return s->manager.fault_type ? 1 : 0;
+ case PL330_REG_FSC:
+ res = 0;
+ for (i = 0; i < s->chan_num; i++) {
+ if (s->chan[i].state == pl330_chan_fault ||
+ s->chan[i].state == pl330_chan_fault_completing) {
+ res |= 1 << i;
+ }
+ }
+ return res;
+ case PL330_REG_FTM:
+ return s->manager.fault_type;
+ case PL330_REG_DBGSTATUS:
+ return s->debug_status;
+ default:
+ hw_error("pl330: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pl330_readfn[] = {
+ pl330_iomem_read,
+ pl330_iomem_read,
+ pl330_iomem_read
+};
+
+static CPUWriteMemoryFunc * const pl330_writefn[] = {
+ pl330_iomem_write,
+ pl330_iomem_write,
+ pl330_iomem_write
+};
+
+static void pl330_save(QEMUFile *f, void *opaque)
+{
+ pl330_state *s = (pl330_state *) opaque;
+ int i;
+ /* manager */
+ qemu_put_be32s(f, &s->manager.src);
+ qemu_put_be32s(f, &s->manager.dst);
+ qemu_put_be32s(f, &s->manager.pc);
+ qemu_put_be32s(f, &s->manager.control);
+ qemu_put_be32s(f, &s->manager.status);
+ qemu_put_be32s(f, &s->manager.lc[0]);
+ qemu_put_be32s(f, &s->manager.lc[1]);
+ qemu_put_be32s(f, &s->manager.fault_type);
+ qemu_put_8s (f, &s->manager.request_flag);
+ qemu_put_8s (f, &s->manager.wakeup);
+ qemu_put_8s (f, &s->manager.wfp_sbp);
+ qemu_put_byte (f, (uint8_t) s->manager.state);
+ qemu_put_8s (f, &s->manager.stall);
+ qemu_put_be32s(f, &s->manager.watchdog_timer);
+ /* chan[chan_num] */
+ for (i = 0; i < s->chan_num; i++) {
+ qemu_put_be32s(f, &s->chan[i].src);
+ qemu_put_be32s(f, &s->chan[i].dst);
+ qemu_put_be32s(f, &s->chan[i].pc);
+ qemu_put_be32s(f, &s->chan[i].control);
+ qemu_put_be32s(f, &s->chan[i].status);
+ qemu_put_be32s(f, &s->chan[i].lc[0]);
+ qemu_put_be32s(f, &s->chan[i].lc[1]);
+ qemu_put_be32s(f, &s->chan[i].fault_type);
+ qemu_put_8s (f, &s->chan[i].ns);
+ qemu_put_8s (f, &s->chan[i].request_flag);
+ qemu_put_8s (f, &s->chan[i].wakeup);
+ qemu_put_8s (f, &s->chan[i].wfp_sbp);
+ qemu_put_byte (f, (uint8_t) s->chan[i].state);
+ qemu_put_8s (f, &s->chan[i].stall);
+ qemu_put_be32s(f, &s->chan[i].watchdog_timer);
+ }
+ /* fifo */
+ qemu_put_buffer(f, s->fifo.buf, s->fifo.buf_size);
+ qemu_put_buffer(f, s->fifo.tag, s->fifo.buf_size);
+ qemu_put_sbe32s(f, &s->fifo.s);
+ qemu_put_sbe32s(f, &s->fifo.t);
+ /* read_queue */
+ for (i = 0; i < s->read_queue.queue_size; i++) {
+ qemu_put_be32s (f, &s->read_queue.queue->addr);
+ qemu_put_sbe32s(f, &s->read_queue.queue->len);
+ qemu_put_sbe32s(f, &s->read_queue.queue->n);
+ qemu_put_sbe32s(f, &s->read_queue.queue->inc);
+ qemu_put_sbe32s(f, &s->read_queue.queue->z);
+ qemu_put_8s (f, &s->read_queue.queue->tag);
+ qemu_put_8s (f, &s->read_queue.queue->seqn);
+ }
+ qemu_put_buffer(f, s->read_queue.lo_seqn, s->chan_num);
+ qemu_put_buffer(f, s->read_queue.hi_seqn, s->chan_num);
+ /* write_queue */
+ for (i = 0; i < s->write_queue.queue_size; i++) {
+ qemu_put_be32s (f, &s->write_queue.queue->addr);
+ qemu_put_sbe32s(f, &s->write_queue.queue->len);
+ qemu_put_sbe32s(f, &s->write_queue.queue->n);
+ qemu_put_sbe32s(f, &s->write_queue.queue->inc);
+ qemu_put_sbe32s(f, &s->write_queue.queue->z);
+ qemu_put_8s (f, &s->write_queue.queue->tag);
+ qemu_put_8s (f, &s->write_queue.queue->seqn);
+ }
+ qemu_put_buffer(f, s->write_queue.lo_seqn, s->chan_num);
+ qemu_put_buffer(f, s->write_queue.hi_seqn, s->chan_num);
+ /* config registers */
+ qemu_put_be32s(f, &s->inten);
+ qemu_put_be32s(f, &s->int_status);
+ qemu_put_be32s(f, &s->ev_status);
+ for (i = 0; i < 6; i++) {
+ qemu_put_be32s(f, s->cfg + i);
+ if (i > 1)
+ continue;
+ qemu_put_be32s(f, s->dbg + i);
+ }
+ qemu_put_8s (f, &s->debug_status);
+ /* other */
+ qemu_put_8s (f, &s->num_faulting);
+ qemu_put_sbuffer(f, s->periph_busy, PL330_PERIPH_NUM);
+ qemu_put_timer (f, s->timer);
+}
+
+static int pl330_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pl330_state *s = (pl330_state *) opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ /* manager */
+ qemu_get_be32s(f, &s->manager.src);
+ qemu_get_be32s(f, &s->manager.dst);
+ qemu_get_be32s(f, &s->manager.pc);
+ qemu_get_be32s(f, &s->manager.control);
+ qemu_get_be32s(f, &s->manager.status);
+ qemu_get_be32s(f, &s->manager.lc[0]);
+ qemu_get_be32s(f, &s->manager.lc[1]);
+ qemu_get_be32s(f, &s->manager.fault_type);
+ qemu_get_8s (f, &s->manager.request_flag);
+ qemu_get_8s (f, &s->manager.wakeup);
+ qemu_get_8s (f, &s->manager.wfp_sbp);
+ s->manager.state = qemu_get_byte(f);
+ qemu_get_8s (f, &s->manager.stall);
+ qemu_get_be32s(f, &s->manager.watchdog_timer);
+ /* chan[chan_num] */
+ for (i = 0; i < s->chan_num; i++) {
+ qemu_get_be32s(f, &s->chan[i].src);
+ qemu_get_be32s(f, &s->chan[i].dst);
+ qemu_get_be32s(f, &s->chan[i].pc);
+ qemu_get_be32s(f, &s->chan[i].control);
+ qemu_get_be32s(f, &s->chan[i].status);
+ qemu_get_be32s(f, &s->chan[i].lc[0]);
+ qemu_get_be32s(f, &s->chan[i].lc[1]);
+ qemu_get_be32s(f, &s->chan[i].fault_type);
+ qemu_get_8s (f, &s->chan[i].ns);
+ qemu_get_8s (f, &s->chan[i].request_flag);
+ qemu_get_8s (f, &s->chan[i].wakeup);
+ qemu_get_8s (f, &s->chan[i].wfp_sbp);
+ s->chan[i].state = qemu_get_byte(f);
+ qemu_get_8s (f, &s->chan[i].stall);
+ qemu_get_be32s(f, &s->chan[i].watchdog_timer);
+ }
+ /* fifo */
+ qemu_get_buffer(f, s->fifo.buf, s->fifo.buf_size);
+ qemu_get_buffer(f, s->fifo.tag, s->fifo.buf_size);
+ qemu_get_sbe32s(f, &s->fifo.s);
+ qemu_get_sbe32s(f, &s->fifo.t);
+ /* read_queue */
+ for (i = 0; i < s->read_queue.queue_size; i++) {
+ qemu_get_be32s (f, &s->read_queue.queue->addr);
+ qemu_get_sbe32s(f, &s->read_queue.queue->len);
+ qemu_get_sbe32s(f, &s->read_queue.queue->n);
+ qemu_get_sbe32s(f, &s->read_queue.queue->inc);
+ qemu_get_sbe32s(f, &s->read_queue.queue->z);
+ qemu_get_8s (f, &s->read_queue.queue->tag);
+ qemu_get_8s (f, &s->read_queue.queue->seqn);
+ }
+ qemu_get_buffer(f, s->read_queue.lo_seqn, s->chan_num);
+ qemu_get_buffer(f, s->read_queue.hi_seqn, s->chan_num);
+ /* write_queue */
+ for (i = 0; i < s->write_queue.queue_size; i++) {
+ qemu_get_be32s (f, &s->write_queue.queue->addr);
+ qemu_get_sbe32s(f, &s->write_queue.queue->len);
+ qemu_get_sbe32s(f, &s->write_queue.queue->n);
+ qemu_get_sbe32s(f, &s->write_queue.queue->inc);
+ qemu_get_sbe32s(f, &s->write_queue.queue->z);
+ qemu_get_8s (f, &s->write_queue.queue->tag);
+ qemu_get_8s (f, &s->write_queue.queue->seqn);
+ }
+ qemu_get_buffer(f, s->write_queue.lo_seqn, s->chan_num);
+ qemu_get_buffer(f, s->write_queue.hi_seqn, s->chan_num);
+ /* config registers */
+ qemu_get_be32s(f, &s->inten);
+ qemu_get_be32s(f, &s->int_status);
+ qemu_get_be32s(f, &s->ev_status);
+ for (i = 0; i < 6; i++) {
+ qemu_get_be32s(f, &s->cfg[i]);
+ if (i > 1)
+ continue;
+ qemu_get_be32s(f, &s->dbg[i]);
+ }
+ qemu_get_8s (f, &s->debug_status);
+ /* other */
+ qemu_get_8s (f, &s->num_faulting);
+ qemu_get_sbuffer(f, s->periph_busy, PL330_PERIPH_NUM);
+ qemu_get_timer (f, s->timer);
+
+ return 0;
+}
+
+/* Controller logic and initialization */
+
+static void pl330_chan_reset(pl330_chan_state *ch)
+{
+ ch->src = 0;
+ ch->dst = 0;
+ ch->pc = 0;
+ ch->state = pl330_chan_stopped;
+ ch->watchdog_timer = 0;
+ ch->stall = 0;
+ ch->control = 0;
+ ch->status = 0;
+ ch->fault_type = 0;
+}
+
+static void pl330_reset(DeviceState *d)
+{
+ int i;
+ pl330_state *s = FROM_SYSBUS(pl330_state, sysbus_from_qdev(d));
+
+ s->inten = 0;
+ s->int_status = 0;
+ s->ev_status = 0;
+ s->debug_status = 0;
+ s->num_faulting = 0;
+ pl330_fifo_reset(&s->fifo);
+ pl330_queue_reset(&s->read_queue);
+ pl330_queue_reset(&s->write_queue);
+
+ for (i = 0; i < s->chan_num; i++) {
+ pl330_chan_reset(&s->chan[i]);
+ }
+ for (i = 0; i < s->periph_num; i++) {
+ s->periph_busy[i] = 0;
+ }
+}
+
+/* PrimeCell PL330 Initialization. CFG is a 6-element array of 32-bit integers.
+ CFG[0] is Config Register 0, CFG[1] - Config Register 1, ..., CFG[4] -
+ Config Register 4 and CFG[5] - Config Register Dn. IRQS is an array of
+ interrupts. Required number of elements in this array is discribed by
+ bits [21:17] of CFG[0]. */
+DeviceState *pl330_init(target_phys_addr_t base,
+ uint32_t instance, const uint32_t *cfg,
+ qemu_irq *irqs, qemu_irq irq_abort)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ int i;
+
+ dev = qdev_create(NULL, "pl330");
+ qdev_prop_set_uint32(dev, "instance", instance);
+ qdev_prop_set_uint32(dev, "cfg0", cfg[0]);
+ qdev_prop_set_uint32(dev, "cfg1", cfg[1]);
+ qdev_prop_set_uint32(dev, "cfg2", cfg[2]);
+ qdev_prop_set_uint32(dev, "cfg3", cfg[3]);
+ qdev_prop_set_uint32(dev, "cfg4", cfg[4]);
+ qdev_prop_set_uint32(dev, "cfg5", cfg[5]);
+
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq_abort);
+ for (i = 0; i < ((cfg[0] >> 17) & 0x1f) + 1; i++) {
+ sysbus_connect_irq(s, i + 1, irqs[i]);
+ }
+ sysbus_mmio_map(s, 0, base);
+
+ return dev;
+}
+
+static int pl330_init1(SysBusDevice *dev)
+{
+ int i;
+ int iomem;
+ pl330_state *s = FROM_SYSBUS(pl330_state, dev);
+
+ sysbus_init_irq(dev, &s->irq_abort);
+ iomem = cpu_register_io_memory(pl330_readfn, pl330_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, PL330_IOMEM_SIZE, iomem);
+ s->timer = qemu_new_timer(vm_clock, pl330_exec_cycle_timer, s);
+
+ s->chan_num = ((s->cfg[0] >> 4) & 7) + 1;
+ s->chan = qemu_mallocz(sizeof(pl330_chan_state) * s->chan_num);
+ for (i = 0; i < s->chan_num; i++) {
+ s->chan[i].parent = s;
+ s->chan[i].tag = (uint8_t)i;
+ }
+ s->manager.parent = s;
+ s->manager.tag = s->chan_num;
+ s->manager.ns = (s->cfg[0] >> 2) & 1;
+ s->manager.is_manager = 1;
+ if (s->cfg[0] & 1) {
+ s->periph_num = ((s->cfg[0] >> 12) & 0x1f) + 1;
+ } else {
+ s->periph_num = 0;
+ }
+ s->event_num = ((s->cfg[0] >> 17) & 0x1f) + 1;
+
+ s->irq = qemu_mallocz(sizeof(qemu_irq) * s->event_num);
+ for (i = 0; i < s->event_num; i++) {
+ sysbus_init_irq(dev, &s->irq[i]);
+ }
+
+ qdev_init_gpio_in(&dev->qdev, pl330_dma_stop_irq, PL330_PERIPH_NUM);
+
+ pl330_queue_init(&s->read_queue, ((s->cfg[5] >> 16) & 0xf) + 1, s->chan_num);
+ pl330_queue_init(&s->write_queue, ((s->cfg[5] >> 8) & 0xf) + 1, s->chan_num);
+ pl330_fifo_init(&s->fifo, ((s->cfg[5] >> 20) & 0x1ff) + 1);
+ pl330_reset(&s->busdev.qdev);
+
+ register_savevm(&dev->qdev, "pl330", s->instance, 1,
+ pl330_save, pl330_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo pl330_info = {
+ .init = pl330_init1,
+ .qdev.name = "pl330",
+ .qdev.size = sizeof(pl330_state),
+ .qdev.reset = pl330_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("instance", pl330_state, instance, 0),
+ DEFINE_PROP_HEX32 ("cfg0", pl330_state, cfg[0], 0),
+ DEFINE_PROP_HEX32 ("cfg1", pl330_state, cfg[1], 0),
+ DEFINE_PROP_HEX32 ("cfg2", pl330_state, cfg[2], 0),
+ DEFINE_PROP_HEX32 ("cfg3", pl330_state, cfg[3], 0),
+ DEFINE_PROP_HEX32 ("cfg4", pl330_state, cfg[4], 0),
+ DEFINE_PROP_HEX32 ("cfg5", pl330_state, cfg[5], 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pl330_register_devices(void)
+{
+ sysbus_register_withprop(&pl330_info);
+}
+
+device_init(pl330_register_devices)
/* 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);
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;
keycode = ps2_raw_keycode[keycode & 0x7f];
}
ps2_queue(&s->common, keycode);
+ return 0;
}
uint32_t ps2_read_data(void *opaque)
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;
}
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)
row = kp->map[keycode].row;
col = kp->map[keycode].column;
if(row == -1 || col == -1)
- return;
+ return 0;
switch (col) {
case 0:
case 1:
} /* 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)
}
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");
}
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;
}
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)
{
#define QDEV_H
#include "hw.h"
+#include "blockdev.h"
#include "qemu-queue.h"
#include "qemu-char.h"
#include "qemu-option.h"
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. ***/
--- /dev/null
+/*
+ * AT42QT602240 Touchscreen
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vmonakhov@ispras.ru>
+ *
+ * NB: Only features used in the kernel driver is implemented currently.
+ */
+
+#include "console.h"
+#include "i2c-addressable.h"
+
+/* Object types */
+#define QT602240_DEBUG_DELTAS 2
+#define QT602240_DEBUG_REFERENCES 3
+#define QT602240_DEBUG_CTERANGE 26
+#define QT602240_GEN_MESSAGE 5
+#define QT602240_GEN_COMMAND 6
+#define QT602240_GEN_POWER 7
+#define QT602240_GEN_ACQUIRE 8
+#define QT602240_TOUCH_MULTI 9
+#define QT602240_TOUCH_KEYARRAY 15
+#define QT602240_PROCI_GRIPFACE 20
+#define QT602240_PROCG_NOISE 22
+#define QT602240_PROCI_ONETOUCH 24
+#define QT602240_PROCI_TWOTOUCH 27
+#define QT602240_SPT_GPIOPWM 19
+#define QT602240_SPT_SELFTEST 25
+#define QT602240_SPT_CTECONFIG 28
+
+/* Orient */
+#define QT602240_NORMAL 0x0
+#define QT602240_DIAGONAL 0x1
+#define QT602240_HORIZONTAL_FLIP 0x2
+#define QT602240_ROTATED_90_COUNTER 0x3
+#define QT602240_VERTICAL_FLIP 0x4
+#define QT602240_ROTATED_90 0x5
+#define QT602240_ROTATED_180 0x6
+#define QT602240_DIAGONAL_COUNTER 0x7
+
+/* Touch status */
+#define QT602240_SUPPRESS (1 << 1)
+#define QT602240_AMP (1 << 2)
+#define QT602240_VECTOR (1 << 3)
+#define QT602240_MOVE (1 << 4)
+#define QT602240_RELEASE (1 << 5)
+#define QT602240_PRESS (1 << 6)
+#define QT602240_DETECT (1 << 7)
+
+/* Message */
+#define QT602240_REPORTID 0
+#define QT602240_MSG_STATUS 1
+#define QT602240_MSG_XPOSMSB 2
+#define QT602240_MSG_YPOSMSB 3
+#define QT602240_MSG_XYPOSLSB 4
+#define QT602240_MSG_TCHAREA 5
+#define QT602240_MSG_TCHAMPLITUDE 6
+#define QT602240_MSG_TCHVECTOR 7
+#define QT602240_CHECKSUM 8
+
+/* Message format */
+#define OBJECT_TABLE_MAX_SIZE 16
+
+#define OBJ_ADDR_TYPE 0
+#define OBJ_ADDR_START 1
+#define OBJ_ADDR_SIZE 3
+#define OBJ_ADDR_INSTANCES 4
+#define OBJ_ADDR_REPORT_IDS 5
+#define OBJ_SIZE 6
+
+/* Size of message queue */
+#define QT602240_MAX_MESSAGE 10
+
+#define QEMUMAXX 0x7FFF
+#define QEMUMAXY 0x7FFF
+
+#define FAMILY_ID_SIZE 1
+#define VARIANT_ID_SIZE 1
+#define VERSION_SIZE 1
+#define BUILD_SIZE 1
+#define MATRIX_X_SIZE_SIZE 1
+#define MATRIX_Y_SIZE_SIZE 1
+#define OBJECTS_NUM_SIZE 1
+#define OBJECT_TABLE_SIZE (OBJECT_TABLE_MAX_SIZE * OBJ_SIZE)
+#define CHECKSUM_SIZE 1
+#define MESSAGE_SIZE 9
+#define MULTITOUCH_SIZE 30
+#define GENCOMMAND_SIZE 5
+#define SPT_CTECONFIG_SIZE 5
+
+#define FAMILY_ID 0
+#define VARIANT_ID (FAMILY_ID + FAMILY_ID_SIZE)
+#define VERSION (VARIANT_ID + VARIANT_ID_SIZE)
+#define BUILD (VERSION + VERSION_SIZE)
+#define MATRIX_X_SIZE (BUILD + BUILD_SIZE)
+#define MATRIX_Y_SIZE (MATRIX_X_SIZE + MATRIX_X_SIZE_SIZE)
+#define OBJECTS_NUM (MATRIX_Y_SIZE + MATRIX_Y_SIZE_SIZE)
+#define OBJECT_TABLE (OBJECTS_NUM + OBJECTS_NUM_SIZE)
+#define CHECKSUM (OBJECT_TABLE + OBJECT_TABLE_SIZE)
+#define MESSAGE (CHECKSUM + CHECKSUM_SIZE)
+#define MULTITOUCH (MESSAGE + MESSAGE_SIZE)
+#define GENCOMMAND (MULTITOUCH + MULTITOUCH_SIZE)
+#define SPT_CTECONFIG (GENCOMMAND + GENCOMMAND_SIZE)
+#define TOTAL_SIZE (SPT_CTECONFIG + SPT_CTECONFIG_SIZE)
+
+#define MULTITOUCH_CTRL 0
+#define MULTITOUCH_ORIENT 9
+#define MULTITOUCH_XRANGE_LSB 18
+#define MULTITOUCH_XRANGE_MSB 19
+#define MULTITOUCH_YRANGE_LSB 20
+#define MULTITOUCH_YRANGE_MSB 21
+
+/* This structure closely correspond to the memory map of the real device.
+ * We use this property in read\write functions by directly reading\writing
+ * data at the offsets provided by the driver. It is possible due to proper
+ * filling of ADDR_START field of each object with this object's structure
+ * offset in the next structure and byte-to-byte equivalence of each object
+ * structure to that of the real device. */
+typedef struct QT602240State {
+
+ I2CAddressableState i2c_addressable;
+
+ uint8_t regs[TOTAL_SIZE];
+
+ /* Supported objects are MESSAGE, MULTITOUCH, SPT_CTECONFIG, GENCOMMAND. */
+
+ int32_t prev_x, prev_y;
+ int32_t pressure;
+ qemu_irq irq;
+
+ /* Messages are stored in a cyclic buffer */
+ int32_t queue_start, queue_end;
+ uint8_t queue[QT602240_MAX_MESSAGE][MESSAGE_SIZE];
+
+ /* Boundary reported coordinates */
+ uint32_t minx, maxx, miny, maxy, orient;
+} QT602240State;
+
+typedef struct QT602240MultitouchMessage {
+ uint8_t status;
+ uint8_t xposmsb;
+ uint8_t yposmsb;
+ uint8_t xyposlsb;
+ uint8_t tcharea;
+ uint8_t tchamplitude;
+ uint8_t tchvector;
+} QT602240MultitouchMessage;
+
+
+/* Add one object to the table */
+static void qt602240_add_object(QT602240State *s, uint16_t offset,
+ uint8_t size, int type, uint8_t report_ids)
+{
+ int i;
+
+ for (i = 0; i < OBJECT_TABLE_MAX_SIZE; i++) {
+ if (s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] == 0) {
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_TYPE] = type;
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START] =
+ offset & 0xFF;
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_START + 1] =
+ (offset >> 8) & 0xFF;
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_SIZE] = size - 1;
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_INSTANCES] = 0;
+
+ s->regs[OBJECT_TABLE + i * OBJ_SIZE + OBJ_ADDR_REPORT_IDS] = report_ids;
+
+ s->regs[OBJECTS_NUM]++;
+ break;
+ }
+ }
+}
+
+/* Reset to default values */
+static void qt602240_reset(DeviceState *d)
+{
+ QT602240State *s =
+ FROM_I2CADDR_SLAVE(QT602240State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+ s->regs[FAMILY_ID] = 0x80;
+ s->regs[VARIANT_ID] = 0x00;
+ s->regs[VERSION] = 20;
+ s->regs[BUILD] = 0x00;
+ s->regs[MATRIX_X_SIZE] = 16;
+ s->regs[MATRIX_Y_SIZE] = 14;
+ s->regs[OBJECTS_NUM] = 0;
+
+ s->minx = 0;
+ s->maxx = 1;
+ s->miny = 0;
+ s->maxy = 1;
+
+ qt602240_add_object(s, MESSAGE, MESSAGE_SIZE,
+ QT602240_GEN_MESSAGE, 0);
+ qt602240_add_object(s, MULTITOUCH, MULTITOUCH_SIZE,
+ QT602240_TOUCH_MULTI, 10);
+ qt602240_add_object(s, SPT_CTECONFIG, SPT_CTECONFIG_SIZE,
+ QT602240_SPT_CTECONFIG, 0);
+ qt602240_add_object(s, GENCOMMAND, GENCOMMAND_SIZE,
+ QT602240_GEN_COMMAND, 0);
+
+ s->regs[MESSAGE + QT602240_REPORTID] = 0xFF;
+
+ s->queue_start = 0;
+ s->queue_end = 0;
+}
+
+#define OFFESETOF_MEM(s, mem) ((void *)(&(mem)) - (void *)(s))
+
+static uint8_t qt602240_read(void *opaque, uint32_t address, uint8_t offset)
+{
+ QT602240State *s = (QT602240State *)opaque;
+ uint8_t retval;
+ uint32_t reg = address + offset;
+
+ if (reg > TOTAL_SIZE) {
+ hw_error("qt602240: bad read offset 0x%x\n", reg);
+ }
+
+ retval = s->regs[reg];
+ if (reg >= MESSAGE + QT602240_REPORTID &&
+ reg <= MESSAGE + QT602240_CHECKSUM) {
+ /* Get message from the queue */
+ if (s->queue_start == s->queue_end) {
+ /* No messages */
+ return 0xFF;
+ }
+ retval = s->queue[s->queue_start][reg - MESSAGE];
+ /* Here is an assumption that message is read till the end */
+ if (reg == MESSAGE + QT602240_CHECKSUM) {
+ /* Move to the next message from the queue */
+ s->queue_start = (s->queue_start + 1) % QT602240_MAX_MESSAGE;
+ if (s->queue_start != s->queue_end) {
+ qemu_irq_raise(s->irq); // GRS : # of interrupt & # of message does not match
+ }
+ }
+ }
+
+ if(reg == 127){
+ return 3; // GRS : Temporary code. Unimplemented reg
+ }
+ return retval;
+}
+
+static void qt602240_write(void *opaque, uint32_t address, uint8_t offset,
+ uint8_t val)
+{
+ QT602240State *s = (QT602240State *)opaque;
+ uint32_t reg = address + offset;
+
+ if (reg >= MULTITOUCH &&
+ reg < MULTITOUCH + MULTITOUCH_SIZE) {
+ s->regs[reg] = val;
+
+ if (reg == MULTITOUCH + MULTITOUCH_ORIENT) {
+ s->orient = s->regs[reg] & 1;
+ }
+
+ if (reg == MULTITOUCH + MULTITOUCH_XRANGE_LSB ||
+ reg == MULTITOUCH + MULTITOUCH_XRANGE_MSB) {
+ int res = s->regs[MULTITOUCH + MULTITOUCH_XRANGE_LSB] +
+ (s->regs[MULTITOUCH + MULTITOUCH_XRANGE_MSB] << 8) + 1;
+
+ if (s->orient == 0) {
+ s->maxx = res;
+ } else {
+ s->maxy = res;
+ }
+ }
+
+ if (reg == MULTITOUCH + MULTITOUCH_YRANGE_LSB ||
+ reg == MULTITOUCH + MULTITOUCH_YRANGE_MSB) {
+ int res = s->regs[MULTITOUCH + MULTITOUCH_YRANGE_LSB] +
+ (s->regs[MULTITOUCH + MULTITOUCH_YRANGE_MSB] << 8) + 1;
+
+ if (s->orient == 0) {
+ s->maxy = res;
+ } else {
+ s->maxx = res;
+ }
+ }
+
+ } else if (reg >= GENCOMMAND &&
+ reg < GENCOMMAND + GENCOMMAND_SIZE) {
+ s->regs[reg] = val;
+ } else if (reg >= SPT_CTECONFIG &&
+ reg < SPT_CTECONFIG + SPT_CTECONFIG_SIZE) {
+ s->regs[reg] = val;
+ } else {
+ hw_error("qt602240: bad write offset 0x%x\n", reg);
+ }
+}
+
+static int qt602240_enabled(QT602240State *s)
+{
+ /* Check for ENABLE and RPTEN bits */
+ return ((s->regs[MULTITOUCH + MULTITOUCH_CTRL] & 0x3) == 0x3);
+}
+
+/* Modify the message read by the driver */
+static void qt602240_msg(QT602240State *s, int x, int y, int status)
+{
+ /* Check if queue is full */
+ if ((s->queue_end + 1) % QT602240_MAX_MESSAGE == s->queue_start) {
+ if(status != QT602240_RELEASE){
+ return; // GRS : Heuristic - Drag does not finish, by dropping release msg
+ }
+ }
+
+ memset(s->queue[s->queue_end], 0, MESSAGE_SIZE);
+ s->queue[s->queue_end][QT602240_REPORTID] = 2;
+ s->queue[s->queue_end][QT602240_MSG_XPOSMSB] = x >> 2;
+ s->queue[s->queue_end][QT602240_MSG_YPOSMSB] = y >> 2;
+ s->queue[s->queue_end][QT602240_MSG_XYPOSLSB] =
+ ((x & 3) << 6) | ((y & 3) << 2);
+ s->queue[s->queue_end][QT602240_MSG_STATUS] = status;
+ s->queue[s->queue_end][QT602240_MSG_TCHAREA] = 1;
+
+ if ((s->queue_end + 1) % QT602240_MAX_MESSAGE != s->queue_start) {
+ s->queue_end = (s->queue_end + 1) % QT602240_MAX_MESSAGE;
+ // GRS : In case of queue full & release msg - drop the last msg instead of release
+ }
+
+}
+
+static void qt602240_ts_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ QT602240State *s = (QT602240State *)opaque;
+
+ if (!qt602240_enabled(s)) {
+ return;
+ }
+
+ /* Convert QEMU mouse coordinates to the touchscreen */
+ /* FIXME: should we use configuration data provided by the driver? */
+ y = (s->miny + y * (s->maxy - s->miny) / QEMUMAXY);
+ x = (s->minx + x * (s->maxx - s->minx) / QEMUMAXX);
+
+ if (s->pressure == !buttons_state) {
+ if (buttons_state) {
+ qt602240_msg(s, x, y, QT602240_PRESS | QT602240_DETECT);
+ } else {
+ qt602240_msg(s, x, y, QT602240_RELEASE);
+ }
+ qemu_irq_raise(s->irq);
+ } else if (s->pressure && (x != s->prev_x || y != s->prev_y)) {
+ static int drop_move = 1;
+ if(drop_move){
+ qt602240_msg(s, x, y, QT602240_MOVE | QT602240_DETECT);
+ qemu_irq_raise(s->irq);
+ drop_move = 0;
+ }else{
+ drop_move = 1;
+ }
+ }
+
+ s->pressure = !!buttons_state;
+ s->prev_x = x;
+ s->prev_y = y;
+}
+
+static void qt602240_save(QEMUFile *f, void *opaque)
+{
+ QT602240State *s = (QT602240State *)opaque;
+ int i;
+
+ qemu_put_buffer(f, s->regs, TOTAL_SIZE);
+ qemu_put_sbe32s(f, &s->prev_x);
+ qemu_put_sbe32s(f, &s->prev_y);
+ qemu_put_sbe32s(f, &s->pressure);
+ qemu_put_sbe32s(f, &s->queue_start);
+ qemu_put_sbe32s(f, &s->queue_end);
+
+ for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
+ qemu_put_buffer(f, s->queue[i], MESSAGE_SIZE);
+ }
+
+ qemu_put_be32s(f, &s->minx);
+ qemu_put_be32s(f, &s->miny);
+ qemu_put_be32s(f, &s->maxx);
+ qemu_put_be32s(f, &s->maxy);
+ qemu_put_be32s(f, &s->orient);
+}
+
+static int qt602240_load(QEMUFile *f, void *opaque, int version_id)
+{
+ QT602240State *s = (QT602240State *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_buffer(f, s->regs, TOTAL_SIZE);
+ qemu_get_sbe32s(f, &s->prev_x);
+ qemu_get_sbe32s(f, &s->prev_y);
+ qemu_get_sbe32s(f, &s->pressure);
+ qemu_get_sbe32s(f, &s->queue_start);
+ qemu_get_sbe32s(f, &s->queue_end);
+
+ for (i = 0; i < QT602240_MAX_MESSAGE; i++) {
+ qemu_get_buffer(f, s->queue[i], MESSAGE_SIZE);
+ }
+
+ qemu_get_be32s(f, &s->minx);
+ qemu_get_be32s(f, &s->miny);
+ qemu_get_be32s(f, &s->maxx);
+ qemu_get_be32s(f, &s->maxy);
+ qemu_get_be32s(f, &s->orient);
+
+ return 0;
+}
+
+static int qt602240_init(I2CAddressableState *s)
+{
+ QT602240State *t = FROM_I2CADDR_SLAVE(QT602240State, s);
+
+ qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
+
+ qemu_add_mouse_event_handler(qt602240_ts_event, t, 1,
+ "AT42QT602240 Touchscreen");
+ qt602240_reset(&s->i2c.qdev);
+
+ register_savevm(&s->i2c.qdev, "qt602240", -1, 1,
+ qt602240_save, qt602240_load, s);
+
+ return 0;
+}
+
+static I2CAddressableDeviceInfo qt602240_info = {
+ .i2c.qdev.name = "qt602240",
+ .i2c.qdev.size = sizeof(QT602240State),
+ .i2c.qdev.reset = qt602240_reset,
+ .init = qt602240_init,
+ .read = qt602240_read,
+ .write = qt602240_write,
+ .size = 2,
+ .rev = 0
+};
+
+static void qt602240_register_devices(void)
+{
+ i2c_addressable_register_device(&qt602240_info);
+}
+
+device_init(qt602240_register_devices)
--- /dev/null
+/*
+ * Samsung S5PC1XX-based board emulation.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ * Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ * Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+
+/* turn off some memory-hungry but useless (yet) devices */
+#define LOWMEMORY
+
+
+#include "s5pc1xx.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "primecell.h"
+#include "qemu-timer.h"
+#include "net.h"
+#include "i2c.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysemu.h"
+#include "ide.h"
+#include "ak8973.h"
+
+
+#ifdef CONFIG_GLES2
+#include "gles2.h"
+#endif
+
+
+/* Memory map */
+#define S5PC1XX_BOOT_BASE 0x00000000
+#define S5PC1XX_DRAM0_BASE 0x20000000
+#define S5PC1XX_ONEDRAM_BASE 0x30000000
+#define S5PC1XX_DRAM1_BASE 0x40000000
+#define S5PC1XX_SROMC_B0_BASE 0x80000000
+#define S5PC1XX_SROMC_B1_BASE 0x88000000
+#define S5PC1XX_SROMC_B2_BASE 0x90000000
+#define S5PC1XX_SROMC_B3_BASE 0x98000000
+#define S5PC1XX_SROMC_B4_BASE 0xA0000000
+#define S5PC1XX_SROMC_B5_BASE 0xA8000000
+#define S5PC1XX_ONENAND_BASE 0xB0000000
+#define S5PC1XX_NAND_BASE 0xB0E00000
+#define S5PC1XX_MP3_SRAM_BASE 0xC0000000
+#define S5PC1XX_IROM_BASE 0xD0000000 /* Internal SROM */
+#define S5PC1XX_IRAM_BASE 0xD0020000 /* Internal SRAM */
+#define S5PC1XX_DMZ_ROM 0xD8000000
+#define S5PC1XX_SFRS 0xE0000000
+
+#define S5PC1XX_JPEG_BASE 0xFB600000
+#define S5PC1XX_USB_OTG_BASE 0xEC000000
+
+/* Memory size */
+#define S5PC1XX_BOOT_SIZE (1 << 29) /* 512 MB */
+#define S5PC1XX_ISROM_SIZE (1 << 16) /* 64 KB */
+#define S5PC1XX_ISRAM_SIZE (1 << 17) /* 128 KB */
+#define S5PC1XX_DRAM_MAX_SIZE (1 << 29) /* 512 MB */
+
+#ifndef LOWMEMORY
+#define S5PC1XX_SFRS_SIZE (1 << 29) /* 512 MB */
+#else
+#define S5PC1XX_SFRS_SIZE (1 << 27) /* 128 MB */
+#endif
+
+/* Interrputs Handling */
+
+/* Number of interrupts */
+#define S5PC1XX_IRQ_COUNT 128
+/* Number of Vector Interrupt Controllers */
+#define S5PC1XX_VIC_N 4
+/* Number of vectors in each Vector Interrupt Controller */
+#define S5PC1XX_VIC_SIZE 32
+#define S5PC1XX_VIC_BASE 0xF2000000
+#define S5PC1XX_VIC_SHIFT 0x00100000
+
+#define S5PC1XX_LCD_BASE 0xF8000000
+#define S5PC1XX_AC97_BASE 0xE2200000
+
+#define S5PC1XX_SPI_BASE 0xE1300000
+#define S5PC1XX_SPI_SHIFT 0x00100000
+
+#define S5PC1XX_IRQ_EXTEND 16
+#define S5PC1XX_IRQ_DMAMEM 18
+#define S5PC1XX_IRQ_DMA0 19
+#define S5PC1XX_IRQ_DMA1 20
+#define S5PC1XX_IRQ_TIMER0 21
+#define S5PC1XX_IRQ_TIMER1 22
+#define S5PC1XX_IRQ_TIMER2 23
+#define S5PC1XX_IRQ_TIMER3 24
+#define S5PC1XX_IRQ_TIMER4 25
+#define S5PC1XX_IRQ_SYS_TIMER 26
+#define S5PC1XX_IRQ_WDT 27
+#define S5PC1XX_IRQ_RTC_ALARM 28
+#define S5PC1XX_IRQ_RTC_TICK 29
+#define S5PC1XX_IRQ_GPIOINT 30
+
+#define S5PC1XX_IRQ_CFCON 41
+#define S5PC1XX_IRQ_UART0 42
+#define S5PC1XX_IRQ_UART1 43
+#define S5PC1XX_IRQ_UART2 44
+#define S5PC1XX_IRQ_UART3 45
+#define S5PC1XX_IRQ_SPI0 47
+#define S5PC1XX_IRQ_SPI1 48
+#define S5PC1XX_IRQ_SPI2 49
+
+#define S5PC1XX_IRQ_UHOST 55
+#define S5PC1XX_IRQ_OTG 56
+
+#define S5PC1XX_IRQ_MMC0 58
+#define S5PC1XX_IRQ_MMC1 59
+#define S5PC1XX_IRQ_MMC2 60
+#define S5PC1XX_IRQ_MMC3 98
+
+#define S5PC1XX_IRQ_I2C_0 46
+#define S5PC1XX_IRQ_I2C_2 51
+#define S5PC1XX_IRQ_I2C_PHY 52
+#define S5PC1XX_IRQ_I2C_DDC 77
+
+#define S5PC1XX_IRQ_JPEG 72
+
+#define S5PC1XX_IRQ_I2S0 80
+#define S5PC1XX_IRQ_I2S1 81
+#define S5PC1XX_IRQ_I2S_V5 S5PC1XX_IRQ_I2S0
+
+#define S5PC1XX_IRQ_AC97 83
+#define S5PC1XX_IRQ_PCM0 84
+#define S5PC1XX_IRQ_PCM1 85
+#define S5PC1XX_IRQ_SPDIF 86
+#define S5PC1XX_IRQ_ADC0 87
+#define S5PC1XX_IRQ_PENDN0 88
+#define S5PC1XX_IRQ_KEYPAD 89
+#define S5PC1XX_IRQ_PCM2 93
+
+#define S5PC1XX_IRQ_LCD0 64
+#define S5PC1XX_IRQ_LCD1 65
+#define S5PC1XX_IRQ_LCD2 66
+
+#define S5PC1XX_IRQ_ADC1 105
+#define S5PC1XX_IRQ_PENDN1 106
+
+#define S5PC1XX_DMAMEM_BASE 0xFA200000
+#define S5PC1XX_DMA0_BASE 0xE0900000
+#define S5PC1XX_DMA1_BASE 0xE0A00000
+
+#define S5PC1XX_GPIO_BASE 0xE0200000
+#define S5PC1XX_PWM_BASE 0xE2500000
+#define S5PC1XX_ST_BASE 0xE2600000
+#define S5PC1XX_WDT_BASE 0xE2700000
+#define S5PC1XX_RTC_BASE 0xE2800000
+
+#define S5PC1XX_UART_BASE 0xE2900000
+#define S5PC1XX_UART_SHIFT 0x00000400
+
+#define S5PC1XX_PCM0_BASE 0xE2300000
+#define S5PC1XX_PCM1_BASE 0xE1200000
+#define S5PC1XX_PCM2_BASE 0xE2B00000
+
+#define S5PC1XX_SPDIF_BASE 0xE1100000
+
+#define S5PC1XX_SROMC_BASE 0xE8000000
+
+#define S5PC1XX_I2S1_BASE 0xE2100000
+#define S5PC1XX_I2S2_BASE 0xE2A00000
+
+#define S5PC1XX_I2C_0_BASE 0xE1800000
+#define S5PC1XX_I2C_2_BASE 0xE1A00000
+#define S5PC1XX_I2C_PHY_BASE 0xFA900000
+#define S5PC1XX_I2C_DDC_BASE 0xFAB00000
+
+#define S5PC1XX_USB_EHCI_BASE 0xEC200000
+#define S5PC1XX_USB_OHCI_BASE 0xEC300000
+
+#define S5PC1XX_SROMC_MAX_BANK_SIZE 0x08000000
+
+#define S5PC1XX_SWRST_BASE 0xE0102000
+#define S5PC1XX_PMU_BASE 0xE0108000
+#define S5PC1XX_CMU_BASE 0xE0100000
+
+#define S5PC1XX_KEYPAD_BASE 0xE1600000
+#define S5PC1XX_TSADC0_BASE 0xE1700000
+#define S5PC1XX_TSADC1_BASE 0xE1701000
+
+#define S5PC1XX_I2S2_V5_BASE 0xEEE30000
+
+#define S5PC1XX_CFCON_ATAPI1 0xE8200054
+#define S5PC1XX_CFCON_ATAPI2 0xE8200074
+
+#define S5PC1XX_HSMMC0_BASE 0xEB000000
+#define S5PC1XX_HSMMC1_BASE 0xEB100000
+#define S5PC1XX_HSMMC2_BASE 0xEB200000
+#define S5PC1XX_HSMMC3_BASE 0xEB300000
+
+#define S5PC1XX_IRQ_ONEDRAM_INT_AP 11
+
+#define S5PC1XX_USB_NUM_PORTS 1
+#define S5PC1XX_SROMC_NUM_BANKS 6
+
+#define S5PC1XX_QT602240_ADDR 0x4A
+#define S5PC1XX_QT602240_IRQ GPIOINT_IRQ(GPJ0, 5)
+
+#define S5PC1XX_WM8994_ADDR 0x1A
+
+#define S5PC1XX_MCS5000_ADDR 0x20
+#define S5PC1XX_MCS5000_IRQ GPIOINT_IRQ(GPJ2, 7)
+
+#define S5PC1XX_MAX17040_ADDR 0x36
+#define S5PC1XX_MAX8998_ADDR 0x66
+#define S5PC1XX_MAX8998_RTC_ADDR 0x06
+
+#define S5PC1XX_AK8973_ADDR 0x1C
+#define S5PC1XX_AK8973_IRQ GPIOEXT_IRQ(29)
+
+/* pl330 peripheral numbers */
+#define PL330_PERIPH_NUM_I2S1 10
+#define PL330_PERIPH_NUM_I2S2 11
+
+
+static const uint32_t dmamem_cfg[] =
+{ 0x003E1071, 0x00000075, 0x0, 0xFFFFFFFF, 0x00000003, 0x01F73733 };
+
+static const uint32_t dma0_cfg[] =
+{ 0x003FF075, 0x00000074, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00773732 };
+
+static const uint32_t dma1_cfg[] =
+{ 0x003FF075, 0x00000074, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0x00773732 };
+
+static const uint8_t chipid_and_omr[] =
+{ 0x00, 0x02, 0x11, 0x43, 0x09, 0x00, 0x00, 0x00 }; /* Little-endian */
+
+/* FIXME: this should be considered a hack, but is there any other way?
+ now at least this may be easily changed if another compass device
+ is used */
+static DeviceState *compass_device = NULL;
+static DeviceState *sensor_device = NULL;
+
+
+int compass_update(int8_t x, int8_t y, int8_t z, int8_t temp)
+{
+ if (compass_device) {
+ ak8973_update(compass_device, x, y, z, temp);
+ }
+ return 0;
+}
+
+#if 0
+/* smb380 module has this function */
+int sensor_update(int16_t x, int16_t y, int16_t z)
+{
+ if (sensor_device) {
+ /* TODO: implement proper emulation for SMB380 */
+ /* smb380_update(sensor_device, x, y, z); */
+ }
+ return 0;
+}
+#endif
+
+/* Find IRQ by it's number */
+static inline qemu_irq s5pc1xx_get_irq(struct s5pc1xx_state_s *s, int n)
+{
+ return s->irq[n / S5PC1XX_VIC_SIZE][n % S5PC1XX_VIC_SIZE];
+}
+
+//#include "s5pc1xx_debug.c"
+
+struct i2c
+{
+ i2c_bus *intrf0;
+ i2c_bus *intrf2;
+ i2c_bus *intrfDDC;
+ i2c_bus *intrfPHY;
+
+ i2c_bus *gpio3;
+ i2c_bus *gpio4;
+ i2c_bus *gpio5;
+ i2c_bus *gpio10;
+};
+
+/* Helper structure and two functions. Allow you to connect several irq
+ sources to one irq input using logical OR. */
+struct irq_multiplexer_s {
+ qemu_irq parent;
+ int count;
+ uint8_t mask[];
+};
+
+static void irq_mult_handler(void *opaque, int irq, int level)
+{
+ uint8_t old_level;
+ struct irq_multiplexer_s *s = (struct irq_multiplexer_s *)opaque;
+
+ old_level = (s->mask[irq >> 3] >> (irq & 7)) & 1;
+ if (!old_level && level) {
+ s->count++;
+ s->mask[irq >> 3] |= (1 << (irq & 7));
+ if (s->count == 1) {
+ qemu_irq_raise(s->parent);
+ }
+ }
+ if (old_level && !level) {
+ s->count--;
+ s->mask[irq >> 3] &= ~(1 << (irq & 7));
+ if (s->count == 0) {
+ qemu_irq_lower(s->parent);
+ }
+ }
+}
+
+static qemu_irq *irq_multiplexer(qemu_irq irq, int n)
+{
+ struct irq_multiplexer_s *s =
+ qemu_mallocz(sizeof(struct irq_multiplexer_s) +
+ ((n + 7) << 3) * sizeof(uint8_t));
+ s->parent = irq;
+ return qemu_allocate_irqs(irq_mult_handler, s, n);
+}
+
+static void s5pc110_rst_mm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ if (val & 1) {
+ qemu_system_reset_request();
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc110_rst_readfn[] = {
+ NULL,
+ NULL,
+ NULL
+};
+
+static CPUWriteMemoryFunc * const s5pc110_rst_writefn[] = {
+ s5pc110_rst_mm_write,
+ s5pc110_rst_mm_write,
+ s5pc110_rst_mm_write
+};
+
+static struct arm_boot_info s5pc110x_binfo = {
+ .loader_start = 0x0,
+ //.board_id = 0xA74, /* Aquila */
+ .board_id = 0xb2e, /* Aquila */
+ .revision = 0x803, /* Aquila LiMo Universal */
+};
+
+static void s5pc110_reset(void *opaque)
+{
+ struct s5pc1xx_state_s *s = (struct s5pc1xx_state_s *)opaque;
+
+ s->env->regs[15] = 0x30000000;
+}
+
+/* Initialize and start system */
+static void s5pc110_init(ram_addr_t ram_size, const char *boot_device,
+ const char *kernel_fname, const char *kernel_cmdline,
+ const char *initrd_name, const char *cpu_model)
+{
+ ram_addr_t sdram_off, isrom_off, isram_off, chipid_off;
+ qemu_irq *cpu_irq;
+ DeviceState *dev, *dev_prev, *gpio, *dmamem, *dma0, *dma1;
+ CharDriverState *chr2, *chr3;
+ qemu_irq *dma_irqs, dma_stop_irq1, dma_stop_irq2;
+ struct i2c *i2c = (struct i2c *)qemu_mallocz(sizeof(*i2c));
+ struct s5pc1xx_state_s *s =
+ (struct s5pc1xx_state_s *)qemu_mallocz(sizeof(*s));
+ int i, j, iomemtype;
+ DriveInfo *dinfo;
+
+ /* Core */
+ s->mpu_model = s5pc110;
+ s->env = cpu_init("cortex-a8");
+ if (!s->env)
+ hw_error("Unable to find CPU definition (%s)\n", "cortex-a8");
+
+ /* Chip-ID and OMR */
+ chipid_off = qemu_ram_alloc(NULL, "s5pc100.chipid", sizeof(chipid_and_omr));
+ cpu_register_physical_memory(S5PC1XX_SFRS, sizeof(chipid_and_omr),
+ chipid_off | IO_MEM_ROM);
+ cpu_physical_memory_write_rom(S5PC1XX_SFRS, chipid_and_omr,
+ sizeof(chipid_and_omr));
+
+ /* Software reset */
+ iomemtype =
+ cpu_register_io_memory(s5pc110_rst_readfn, s5pc110_rst_writefn, NULL, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(S5PC1XX_SWRST_BASE, 0x4, iomemtype);
+
+ /* Memory */
+ /* Main RAM */
+ if (ram_size > S5PC1XX_DRAM_MAX_SIZE) {
+ hw_error("Too much memory was requested for this board "
+ "(requested: %lu MB, max: %u MB)\n",
+ ram_size / (1024 * 1024),
+ S5PC1XX_DRAM_MAX_SIZE / (1024 * 1024));
+ }
+ s->sdram_size = ram_size;
+ sdram_off = qemu_ram_alloc(NULL, "s5pc100.ram", ram_size);
+ cpu_register_physical_memory(S5PC1XX_DRAM1_BASE, ram_size,
+ sdram_off | IO_MEM_RAM);
+ /* Also map the same memory to boot area. */
+ cpu_register_physical_memory(S5PC1XX_BOOT_BASE, ram_size,
+ sdram_off | IO_MEM_RAM);
+
+ /* Internal SROM */
+ s->isrom_size = S5PC1XX_ISROM_SIZE;
+ isrom_off = qemu_ram_alloc(NULL, "s5pc100.srom", s->isrom_size);
+ cpu_register_physical_memory(S5PC1XX_IROM_BASE, s->isrom_size,
+ isrom_off | IO_MEM_ROM);
+
+ /* Internal SRAM */
+ s->isram_size = S5PC1XX_ISRAM_SIZE;
+ isram_off = qemu_ram_alloc(NULL, "s5pc100.sram", s->isram_size);
+ cpu_register_physical_memory(S5PC1XX_IRAM_BASE, s->isram_size,
+ isram_off | IO_MEM_RAM);
+
+#ifndef LOWMEMORY
+ /* SROMC banks */
+ ram_addr_t srom_bank_off = qemu_ram_alloc(NULL, "s5pc100.srombanks",
+ S5PC1XX_SROMC_MAX_BANK_SIZE);
+ cpu_register_physical_memory(S5PC1XX_SROMC_B0_BASE,
+ S5PC1XX_SROMC_NUM_BANKS * S5PC1XX_SROMC_MAX_BANK_SIZE,
+ srom_bank_off | IO_MEM_ROM);
+#endif
+
+ /* System devices */
+ /* Interrupts */
+ cpu_irq = arm_pic_init_cpu(s->env);
+ s->irq =
+ qemu_mallocz(S5PC1XX_VIC_N * sizeof(qemu_irq *));
+ dev = pl192_init(S5PC1XX_VIC_BASE, 0,
+ cpu_irq[ARM_PIC_CPU_IRQ],
+ cpu_irq[ARM_PIC_CPU_FIQ], NULL);
+ s->irq[0] = qemu_mallocz(S5PC1XX_VIC_SIZE * sizeof(qemu_irq));
+ for (i = 0; i < S5PC1XX_VIC_SIZE; i++)
+ s->irq[0][i] = qdev_get_gpio_in(dev, i);
+ for (j = 1; j < S5PC1XX_VIC_N; j++) {
+ dev_prev = dev;
+ dev =
+ pl192_init(S5PC1XX_VIC_BASE + S5PC1XX_VIC_SHIFT * j, j, NULL);
+
+ s->irq[j] = qemu_mallocz(S5PC1XX_VIC_SIZE * sizeof(qemu_irq));
+ for (i = 0; i < S5PC1XX_VIC_SIZE; i++)
+ s->irq[j][i] = qdev_get_gpio_in(dev, i);
+ pl192_chain(sysbus_from_qdev(dev_prev), sysbus_from_qdev(dev));
+ }
+
+ /* GPIO */
+ gpio = sysbus_create_varargs("s5pc1xx.gpio", S5PC1XX_GPIO_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_GPIOINT),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_EXTEND),
+ NULL);
+
+ /* DMA Controller */
+ dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMAMEM),
+ ((dmamem_cfg[0] >> 17) & 0x1f) + 2);
+ dmamem =
+ pl330_init(S5PC1XX_DMAMEM_BASE, 0, dmamem_cfg, dma_irqs + 1, *dma_irqs);
+ dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMA0),
+ ((dma0_cfg[0] >> 17) & 0x1f) + 2);
+ dma0 = pl330_init(S5PC1XX_DMA0_BASE, 1, dma0_cfg, dma_irqs + 1, *dma_irqs);
+ dma_irqs = irq_multiplexer(s5pc1xx_get_irq(s, S5PC1XX_IRQ_DMA1),
+ ((dma1_cfg[0] >> 17) & 0x1f) + 2);
+ dma1 = pl330_init(S5PC1XX_DMA1_BASE, 2, dma1_cfg, dma_irqs + 1, *dma_irqs);
+
+ /* I2C */
+ dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_0_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_0));
+ i2c->intrf0 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_2_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_2));
+ i2c->intrf2 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_PHY_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_PHY));
+ i2c->intrfPHY = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = sysbus_create_simple("s5pc1xx.i2c", S5PC1XX_I2C_DDC_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2C_DDC));
+ i2c->intrfDDC = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+
+ /* GPIO I2C */
+ dev = s5pc1xx_i2c_gpio_init(3);
+ i2c->gpio3 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = s5pc1xx_i2c_gpio_init(4);
+ i2c->gpio4 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = s5pc1xx_i2c_gpio_init(5);
+ i2c->gpio5 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ dev = s5pc1xx_i2c_gpio_init(10);
+ i2c->gpio10 = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+
+ /* SPI */
+ sysbus_create_simple("s5pc1xx.spi",
+ S5PC1XX_SPI_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI0));
+ sysbus_create_simple("s5pc1xx.spi",
+ S5PC1XX_SPI_BASE + S5PC1XX_SPI_SHIFT,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI1));
+ sysbus_create_simple("s5pc1xx.spi",
+ S5PC1XX_SPI_BASE + S5PC1XX_SPI_SHIFT * 2,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPI2));
+
+ /* PMU */
+ sysbus_create_simple("s5pc1xx.pmu", S5PC1XX_PMU_BASE, NULL);
+
+ /* MAX17040 */
+ max17040_init(i2c->gpio3, S5PC1XX_MAX17040_ADDR);
+
+ /* MAX8998 */
+ max8998_init(i2c->gpio4, S5PC1XX_MAX8998_ADDR);
+
+ /* MAX8998 RTC */
+ max8998_rtc_init(i2c->gpio4, S5PC1XX_MAX8998_RTC_ADDR);
+
+ /* USB */
+ if (usb_enabled) {
+ sysbus_create_simple("sysbus-ohci", S5PC1XX_USB_OHCI_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UHOST));
+ sysbus_create_simple("usb-ehci", S5PC1XX_USB_EHCI_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UHOST));
+ }
+
+ /* Clock devices */
+ /* CMU */
+ sysbus_create_simple("s5pc1xx.clk", S5PC1XX_CMU_BASE, NULL);
+
+ /* RTC */
+ sysbus_create_varargs("s5pc1xx.rtc", S5PC1XX_RTC_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_RTC_ALARM),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_RTC_TICK), NULL);
+
+ /* PWM */
+ sysbus_create_varargs("s5pc1xx.pwm", S5PC1XX_PWM_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER0),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER1),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER2),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER3),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_TIMER4), NULL);
+
+ /* WDT */
+ sysbus_create_simple("s5pc1xx.wdt", S5PC1XX_WDT_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_WDT));
+
+ /* ST */
+ sysbus_create_simple("s5pc1xx.st", S5PC1XX_ST_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_SYS_TIMER));
+
+ /* Storage devices */
+ /* NAND */
+ sysbus_create_simple("s5pc1xx.nand", S5PC1XX_NAND_BASE, NULL);
+
+ /* OneNAND */
+ s5pc1xx_onenand_init(S5PC1XX_ONENAND_BASE);
+
+ /* SROMC */
+ s5pc1xx_srom_init(S5PC1XX_SROMC_BASE, S5PC1XX_SROMC_NUM_BANKS);
+
+ /* SD/MMC */
+ sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC0_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC0));
+
+ sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC1_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC1));
+
+ sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC2_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC2));
+
+ sysbus_create_simple("s5pc1xx.mmc", S5PC1XX_HSMMC3_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_MMC3));
+
+ dinfo = drive_get(IF_IDE, 0, 0);
+ mmio_ide_init(S5PC1XX_CFCON_ATAPI1, S5PC1XX_CFCON_ATAPI2,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_CFCON), 2, dinfo, NULL);
+
+ /* Input/Output devices */
+ /* UART */
+ if (serial_hds[0]) {
+ chr2 = serial_hds[0];
+ } else {
+ chr2 = qemu_chr_open("s5pc1xx.uart", "vc:800x600", NULL);
+ }
+ chr3 =
+ qemu_chr_open("AT_socket", "tcp:localhost:7776,server,nowait", NULL);
+ s5pc1xx_uart_init(S5PC1XX_UART_BASE, 0, 256,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART0), NULL);
+ s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT, 1, 64,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART1), NULL);
+ s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT * 2, 2, 16,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART2), chr2);
+ s5pc1xx_uart_init(S5PC1XX_UART_BASE + S5PC1XX_UART_SHIFT * 3, 3, 16,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_UART3), chr3);
+
+ /* QT602240 Touchscreen */
+ dev = i2c_create_slave(i2c->intrf2, "qt602240", S5PC1XX_QT602240_ADDR);
+ qdev_connect_gpio_out(dev, 0,
+ s5pc1xx_gpoint_irq(gpio, S5PC1XX_QT602240_IRQ));
+
+ /* S3C Touchscreen */
+ s5pc1xx_tsadc_init(S5PC1XX_TSADC0_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_ADC0),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_PENDN0),
+ 1, 12, 0, 120, 0, 200);
+ s5pc1xx_tsadc_init(S5PC1XX_TSADC1_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_ADC1),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_PENDN1),
+ 1, 12, 0, 120, 0, 200);
+
+ /* Keypad */
+ s5pc1xx_keyif_init(S5PC1XX_KEYPAD_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_KEYPAD),
+ "aquila", 8);
+
+ /* MSC5000 Touchkeys */
+ dev = i2c_create_slave(i2c->gpio10, "mcs5000.universal",
+ S5PC1XX_MCS5000_ADDR);
+ qdev_connect_gpio_out(dev, 0,
+ s5pc1xx_gpoint_irq(gpio, S5PC1XX_MCS5000_IRQ));
+
+ /* USB-OTG */
+ s5pc1xx_usb_otg_init(&nd_table[0],
+ S5PC1XX_USB_OTG_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_OTG));
+
+ /* OneDRAM */
+ s5pc1xx_onedram_init("s5pc1xx.onedram.aquila.xmm", S5PC1XX_ONEDRAM_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_ONEDRAM_INT_AP));
+
+ /* AK8973 Compass Emulation */
+ dev = i2c_create_slave(i2c->intrfDDC, "ak8973", S5PC1XX_AK8973_ADDR);
+ compass_device = dev;
+ qdev_connect_gpio_out(dev, 0, s5pc1xx_gpoint_irq(gpio, S5PC1XX_AK8973_IRQ));
+
+ /* Audio devices and interfaces */
+ /* SPDIF */
+ sysbus_create_simple("s5pc1xx.spdif", S5PC1XX_SPDIF_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_SPDIF));
+
+ /* AC97 */
+ sysbus_create_simple("s5pc1xx.ac97", S5PC1XX_AC97_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_AC97));
+
+ /* PCM */
+ s5pc1xx_pcm_init(S5PC1XX_PCM0_BASE, 0,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM0));
+ s5pc1xx_pcm_init(S5PC1XX_PCM1_BASE, 1,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM1));
+ s5pc1xx_pcm_init(S5PC1XX_PCM2_BASE, 2,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_PCM2));
+
+ /* WM8994 */
+ dev = i2c_create_slave(i2c->gpio5, "wm8994", S5PC1XX_WM8994_ADDR);
+
+ /* I2S */
+ dma_stop_irq1 = qdev_get_gpio_in(dma0, PL330_PERIPH_NUM_I2S1);
+ dma_stop_irq2 = qdev_get_gpio_in(dma1, PL330_PERIPH_NUM_I2S2);
+ s5pc1xx_i2s_init(S5PC1XX_I2S2_V5_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_I2S_V5), dev /* WM8994 */,
+ dma_stop_irq1, dma_stop_irq2);
+
+ /* Video devices */
+ /* LCD */
+ sysbus_create_varargs("s5pc1xx.lcd", S5PC1XX_LCD_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD0),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD1),
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_LCD2), NULL);
+
+ /* JPEG */
+ sysbus_create_simple("s5pc1xx.jpeg", S5PC1XX_JPEG_BASE,
+ s5pc1xx_get_irq(s, S5PC1XX_IRQ_JPEG));
+
+#ifdef CONFIG_GLES2
+ gles2_init(s->env);
+#endif
+
+ /* Load the kernel */
+ s5pc110x_binfo.ram_size = ram_size;
+ s5pc110x_binfo.kernel_filename = kernel_fname;
+ s5pc110x_binfo.initrd_filename = initrd_name;
+ s5pc110x_binfo.loader_start = 0x30000000;
+ if (kernel_cmdline) {
+ if (strstr(kernel_cmdline,"duallcd")) {
+ /* Board is 'media' for enabling duallcd */
+ s5pc110x_binfo.revision = 0x1003;
+ }
+ s5pc110x_binfo.kernel_cmdline = kernel_cmdline;
+ } else {
+ s5pc110x_binfo.kernel_cmdline =
+ "root=/dev/mtdblock2 rootfstype=cramfs init=/linuxrc "
+ "console=ttySAC2,115200 mem=128M debug";
+ }
+
+ arm_load_kernel(s->env, &s5pc110x_binfo);
+
+ qemu_register_reset(s5pc110_reset, s);
+ s5pc110_reset(s);
+
+ /* Don't hide cursor since we are using touchscreen */
+ cursor_hide = 0;
+}
+
+static QEMUMachine s5pc110_machine = {
+ .name = "s5pc110",
+ .desc = "Samsung S5PC110-base board",
+ .init = s5pc110_init,
+};
+
+static void s5pc1xx_machine_init(void)
+{
+ qemu_register_machine(&s5pc110_machine);
+}
+
+machine_init(s5pc1xx_machine_init);
--- /dev/null
+/*
+ * Samsung S5PC1XX-based board emulation.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ * Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ * Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#ifndef hw_s5pc1xx_h
+#define hw_s5pc1xx_h "s5pc1xx.h"
+
+#include "qemu-common.h"
+
+
+typedef struct Clk *S5pc1xxClk;
+typedef void GPIOWriteMemoryFunc(void *opaque, int io_index, uint32_t value);
+typedef uint32_t GPIOReadMemoryFunc(void *opaque, int io_index);
+
+struct s5pc1xx_state_s {
+ /* Model */
+ enum s5pc1xx_model {
+ s5pc110
+ } mpu_model;
+
+ /* CPU Core */
+ CPUState *env;
+
+ /* Interrupts */
+ qemu_irq **irq;
+
+ /* Amount of different memory types */
+ ram_addr_t sdram_size;
+ ram_addr_t isrom_size;
+ ram_addr_t isram_size;
+};
+
+
+int compass_update(int8_t x, int8_t y, int8_t z, int8_t temp);
+
+/* s5pc1xx_gpio.c */
+void s5pc1xx_gpio_register_io_memory(int io_index, int instance,
+ GPIOReadMemoryFunc *mem_read,
+ GPIOWriteMemoryFunc *mem_write,
+ GPIOWriteMemoryFunc *mem_conf,
+ void *opaque);
+
+uint32_t s5pc1xx_empty_gpio_read(void *opaque, int io_index);
+void s5pc1xx_empty_gpio_write(void *opaque, int io_index, uint32_t value);
+
+/* s5pc1xx_i2c_gpio.c */
+DeviceState *s5pc1xx_i2c_gpio_init(int instance);
+
+/* s5pc1xx_clk.c */
+S5pc1xxClk s5pc1xx_findclk(const char *name);
+int64_t s5pc1xx_clk_getrate(S5pc1xxClk clk);
+
+/* s5pc1xx_pmu.c */
+DeviceState *max17040_init(i2c_bus *bus, int addr);
+DeviceState *max8998_init(i2c_bus *bus, int addr);
+DeviceState *max8998_rtc_init(i2c_bus *bus, int addr);
+
+/* s5pc1xx_srom.c */
+DeviceState *s5pc1xx_srom_init(target_phys_addr_t base, int num_banks);
+
+/* s5pc1xx_gpio.c */
+qemu_irq s5pc1xx_gpoint_irq(DeviceState *d, int irq_num);
+
+/* s5pc1xx_uart.c */
+DeviceState *s5pc1xx_uart_init(target_phys_addr_t base, int instance,
+ int queue_size, qemu_irq irq,
+ CharDriverState *chr);
+
+/* s5pc1xx_onenand.c */
+DeviceState *s5pc1xx_onenand_init(target_phys_addr_t base);
+
+/* s5pc1xx_tsadc.c */
+DeviceState *s5pc1xx_tsadc_init(target_phys_addr_t base, qemu_irq irq_adc,
+ qemu_irq irq_pennd, int new, int resolution,
+ int minx, int maxx, int miny, int maxy);
+
+/* s5pc1xx_keyif.c */
+DeviceState *s5pc1xx_keyif_init(target_phys_addr_t base, qemu_irq irq,
+ const char *keymapname, uint32_t shift);
+
+/* s5pc1xx_i2s.c */
+DeviceState *s5pc1xx_i2s_init(target_phys_addr_t base, qemu_irq irq,
+ DeviceState *wm8994_dev, qemu_irq dma_irq1,
+ qemu_irq dma_irq2);
+
+/* s5pc1xx_pcm.c */
+DeviceState *s5pc1xx_pcm_init(target_phys_addr_t base,
+ int instance,
+ qemu_irq irq);
+
+/* s5pc1xx_onedram.c */
+DeviceState *s5pc1xx_onedram_init(const char *name, target_phys_addr_t base,
+ qemu_irq irq_ap);
+
+/* usb-ohci.c */
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+ qemu_irq irq);
+
+void s5pc1xx_usb_otg_init(NICInfo *nd, target_phys_addr_t base, qemu_irq irq);
+
+#endif /* hw_s5pc1xx_h */
--- /dev/null
+/*
+ * AC97 controller for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+#define ALL_BITS(b,a) (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the AC97 Global Control Register 0x00000000 */
+#define AC_GLBCTRL 0x00
+ #define ALL_CLEAR ALL_BITS(30, 24)
+ #define INT_EN(stat) (stat)
+ #define TRANSFER_EN (1 << 3)
+ #define AC_LINK_ON (1 << 2)
+ #define WARM_RESET (1 << 1)
+ #define COLD_RESET (1 << 0)
+
+/* R Specifies the AC97 Global Status Register 0x00000001 */
+#define AC_GLBSTAT 0x04
+ #define CODEC_READY_INT (1 << 22)
+ #define PCM_OUT_UNDER_INT (1 << 21)
+ #define PCM_IN_OVER_INT (1 << 20)
+ #define MIC_IN_OVER_INT (1 << 19)
+ #define PCM_OUT_TH_INT (1 << 18)
+ #define PCM_IN_TH_INT (1 << 17)
+ #define MIC_IN_TH_INT (1 << 16)
+ #define ALL_STAT ALL_BITS(22, 16)
+
+/* R/W Specifies the AC97 Codec Command Register 0x00000000 */
+#define AC_CODEC_CMD 0x08
+/* R Specifies the AC97 Codec Status Register 0x00000000 */
+#define AC_CODEC_STAT 0x0C
+/* R Specifies the AC97 PCM Out/In Channel FIFO Address Register 0x00000000 */
+#define AC_PCMADDR 0x10
+/* R Specifies the AC97 MIC In Channel FIFO Address Register 0x00000000 */
+#define AC_MICADDR 0x14
+/* R/W Specifies the AC97 PCM Out/In Channel FIFO Data Register 0x00000000 */
+#define AC_PCMDATA 0x18
+/* R/W Specifies the AC97 MIC In Channel FIFO Data Register 0x00000000 */
+#define AC_MICDATA 0x1C
+
+#define S5PC1XX_AC97_REG_MEM_SIZE 0x20
+
+
+typedef struct S5pc1xxAC97State {
+ SysBusDevice busdev;
+
+ uint32_t glbctrl;
+ uint32_t glbstat;
+ uint32_t codec_cmd;
+ uint32_t codec_stat;
+ uint32_t pcmaddr;
+ uint32_t micaddr;
+ uint32_t pcmdata;
+ uint32_t micdata;
+
+ struct FrameIn {
+ uint16_t pcm_left_fifo[16];
+ uint16_t pcm_right_fifo[16];
+
+ uint64_t pcm_write_idx;
+ uint64_t pcm_read_idx;
+
+ uint16_t mic_fifo[16];
+
+ uint64_t mic_write_idx;
+ uint64_t mic_read_idx;
+
+ uint16_t tag_phase;
+ uint32_t data_phase[12];
+ } in;
+
+ struct FrameOut {
+ uint16_t pcm_left_fifo[16];
+ uint16_t pcm_right_fifo[16];
+
+ uint64_t pcm_write_idx;
+ uint64_t pcm_read_idx;
+
+ uint16_t tag_phase;
+ uint32_t data_phase[12];
+ } out;
+
+ uint8_t delay;
+ uint8_t cur_pos;
+ uint8_t stream;
+ uint8_t sync_en;
+ uint8_t reset;
+
+ qemu_irq irq;
+ QEMUTimer *ac97_timer;
+ uint64_t last_ac97_time;
+} S5pc1xxAC97State;
+
+
+/* Function for initialization and cold reset */
+static void s5pc1xx_ac97_reset(void *opaque)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+ int i;
+
+ s->delay = 2;
+ s->stream = 0;
+ s->sync_en = 0;
+
+ s->glbctrl = 0;
+ s->glbstat = 0;
+ s->codec_cmd = 0;
+ s->codec_stat = 0;
+ s->pcmaddr = 0;
+ s->micaddr = 0;
+ s->pcmdata = 0;
+ s->micdata = 0;
+
+ s->in.tag_phase = 0;
+ s->out.tag_phase = 0;
+
+ for (i = 0; i < 12; i++) {
+ s->in.data_phase[i] = 0;
+ s->out.data_phase[i] = 0;
+ }
+}
+
+/* Interrupts handler */
+static void s5pc1xx_ac97_irq(S5pc1xxAC97State *s,
+ uint32_t stat, uint32_t clear)
+{
+ if (stat) {
+ s->glbstat |= stat;
+ if (s->glbctrl & INT_EN(stat)) /* if enabled */
+ qemu_irq_raise(s->irq);
+ }
+ if (clear) {
+ s->glbstat &= ~(clear >> 8);
+ if (!(s->glbstat & ALL_STAT)) /* if all clear */
+ qemu_irq_lower(s->irq);
+ }
+}
+
+/* Controls input fifo stage */
+static void s5pc1xx_ac97_infifo_control(S5pc1xxAC97State *s)
+{
+ uint8_t pcm_depth, mic_depth;
+
+ pcm_depth = (s->in.pcm_write_idx - s->in.pcm_read_idx) % 17;
+ mic_depth = (s->in.mic_write_idx - s->in.mic_read_idx) % 17;
+
+ if (pcm_depth == 16)
+ s5pc1xx_ac97_irq(s, PCM_IN_OVER_INT, 0);
+
+ if (pcm_depth > 7)
+ s5pc1xx_ac97_irq(s, PCM_IN_TH_INT, 0);
+
+ if (mic_depth == 16)
+ s5pc1xx_ac97_irq(s, MIC_IN_OVER_INT, 0);
+
+ if (mic_depth > 7)
+ s5pc1xx_ac97_irq(s, MIC_IN_TH_INT, 0);
+}
+
+/* Controls output fifo stage */
+static void s5pc1xx_ac97_outfifo_control(S5pc1xxAC97State *s)
+{
+ uint8_t pcm_depth;
+
+ pcm_depth = (s->out.pcm_write_idx - s->out.pcm_read_idx) % 17;
+
+ if (pcm_depth == 0)
+ s5pc1xx_ac97_irq(s, PCM_OUT_UNDER_INT, 0);
+
+ if (pcm_depth < 9)
+ s5pc1xx_ac97_irq(s, PCM_OUT_TH_INT, 0);
+}
+
+/* Sync timer */
+static void s5pc1xx_ac97_sync(void *opaque)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+ uint64_t next_ac97_time;
+
+ if (s->sync_en) {
+ s->delay = 0; /* used for 1/12MHz delay */
+ s->last_ac97_time = qemu_get_clock(vm_clock);
+ next_ac97_time = s->last_ac97_time +
+ muldiv64(1, get_ticks_per_sec(), 48000); /* 48 KHz cycle */
+ qemu_mod_timer(s->ac97_timer, next_ac97_time);
+ } else {
+ qemu_del_timer(s->ac97_timer);
+ }
+}
+
+/* Bitclk cycles counter */
+static uint8_t s5pc1xx_ac97_spent_cycles(S5pc1xxAC97State *s)
+{
+ uint64_t spent_1G, spent_12M;
+
+ spent_1G = qemu_get_clock(vm_clock) - s->last_ac97_time;
+ spent_12M = muldiv64(spent_1G, 12288000, get_ticks_per_sec());
+
+ return (spent_12M % 256);
+}
+
+/* Get current input frame and prepare next output frame */
+static void s5pc1xx_ac97_next_frame(S5pc1xxAC97State *s)
+{
+ short i;
+
+ /* Get input frame */
+
+ /* check if codec_ready */
+ if (s->in.tag_phase & (1 << 15)) {
+ /* check tag bits and check if the received address
+ * is equal to the most recent sent address */
+ if ((s->in.tag_phase & (1 << 14)) &&
+ (s->in.tag_phase & (1 << 13)) &&
+ (s->in.data_phase[0] & ALL_BITS(18, 12)) ==
+ (s->out.data_phase[0] & ALL_BITS(18, 12)))
+ s->codec_stat = (s->in.data_phase[0] << 4 & ALL_BITS(22, 16)) |
+ (s->in.data_phase[1] >> 4);
+
+ if (s->in.pcm_write_idx < s->in.pcm_read_idx + 16) {
+
+ if (s->in.tag_phase & (1 << 12))
+ s->in.pcm_left_fifo[s->in.pcm_write_idx % 16] =
+ (s->in.data_phase[2] >> 4);
+
+ if (s->in.tag_phase & (1 << 11))
+ s->in.pcm_right_fifo[s->in.pcm_write_idx % 16] =
+ (s->in.data_phase[3] >> 4);
+
+ s->in.pcm_write_idx++;
+ }
+
+ if (s->in.mic_write_idx < s->in.mic_read_idx + 16) {
+
+ if (s->in.tag_phase & (1 << 9))
+ s->in.mic_fifo[s->in.mic_write_idx % 16] =
+ (s->in.data_phase[5] >> 4);
+
+ s->in.mic_write_idx++;
+ }
+
+ s5pc1xx_ac97_infifo_control(s);
+ }
+
+ /* Set output frame */
+
+ s->out.tag_phase = 0;
+ for (i = 0; i < 4; i++)
+ s->out.data_phase[i] = 0;
+
+ if (s->codec_cmd) {
+ /* enable slots 1 and 2 */
+ s->out.tag_phase |= ALL_BITS(14, 13);
+ s->out.data_phase[0] = s->codec_cmd >> 4 & ALL_BITS(19, 12);
+ s->out.data_phase[1] = s->codec_cmd << 4 & ALL_BITS(19, 4);
+ s->codec_cmd = 0;
+ } else {
+ s->out.tag_phase &= ~ALL_BITS(14, 13);
+ }
+
+ /* check if fifo is not empty */
+ if (s->out.pcm_read_idx < s->out.pcm_write_idx) {
+ /* enable slots 3 and 4 */
+ s->out.tag_phase |= ALL_BITS(12, 11);
+ s->out.data_phase[2] =
+ s->out.pcm_left_fifo[s->out.pcm_read_idx % 16] << 4;
+ s->out.data_phase[3] =
+ s->out.pcm_right_fifo[s->out.pcm_read_idx % 16] << 4;
+ s->out.pcm_read_idx++;
+ } else {
+ s->out.tag_phase &= ~ALL_BITS(12, 11);
+ }
+
+ /* set bit 15 if any of bits 14~11 is high */
+ if (s->out.tag_phase & ALL_BITS(14, 11))
+ s->out.tag_phase |= (1 << 15);
+
+ s5pc1xx_ac97_outfifo_control(s);
+}
+
+/* Read AC97 by GPIO */
+static uint32_t s5pc1xx_ac97_gpio_read(void *opaque, int io_index)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+ uint8_t ret_val;
+
+ switch (io_index) {
+ case GPIO_AC97RESETn:
+ return s->reset;
+
+ case GPIO_AC97SYNC:
+ return ((s->sync_en) && (s5pc1xx_ac97_spent_cycles(s) < 16));
+
+ case GPIO_AC97SDO:
+ if (!s->stream)
+ break;
+
+ /* Note: '-1' is a delay just after start */
+ s->cur_pos = s5pc1xx_ac97_spent_cycles(s) - 1;
+
+ if (s->cur_pos < 16)
+ ret_val = s->out.tag_phase >> (15 - s->cur_pos) & 0x1;
+ else
+ ret_val =
+ s->out.data_phase[(s->cur_pos - 16) / 20] >>
+ (19 - (s->cur_pos - 16) % 20) & 0x1;
+ return ret_val;
+ }
+ return 0;
+}
+
+/* Write AC97 by GPIO */
+static void s5pc1xx_ac97_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+ switch (io_index) {
+ case GPIO_AC97BITCLK:
+ if (value) {
+ if (s->delay == 1)
+ s5pc1xx_ac97_next_frame(s);
+ if (s->delay < 2)
+ s->delay++;
+ }
+ break;
+
+ case GPIO_AC97SDI:
+ if (!(s->stream))
+ break;
+
+ /* Note: '-1' is a delay just after start */
+ s->cur_pos = s5pc1xx_ac97_spent_cycles(s) - 1;
+
+ if (s->cur_pos < 16)
+ s->in.tag_phase |= value << (15 - s->cur_pos);
+ else
+ s->in.data_phase[(s->cur_pos - 16) / 20] |=
+ value << (19 - (s->cur_pos - 16) % 20);
+ break;
+ }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_ac97_gpio_readfn = s5pc1xx_ac97_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_ac97_gpio_writefn = s5pc1xx_ac97_gpio_write;
+
+/* Read AC97 by OS */
+static uint32_t s5pc1xx_ac97_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+ switch(offset) {
+ case AC_GLBCTRL:
+ return s->glbctrl;
+ case AC_GLBSTAT:
+ return s->glbstat;
+ case AC_CODEC_CMD:
+ return s->codec_cmd;
+ case AC_CODEC_STAT:
+ return s->codec_stat;
+ case AC_PCMADDR:
+ s->pcmaddr = ((s->in.pcm_write_idx % 16) << 0) |
+ ((s->out.pcm_write_idx % 16) << 8) |
+ ((s->in.pcm_read_idx % 16) << 16) |
+ ((s->out.pcm_read_idx % 16) << 24);
+ return s->pcmaddr;
+ case AC_MICADDR:
+ s->micaddr = ((s->in.mic_write_idx % 16) << 0) |
+ ((s->in.mic_read_idx % 16) << 16);
+ return s->micaddr;
+ case AC_PCMDATA:
+ /* check if fifo is not empty */
+ if (s->in.pcm_read_idx < s->in.pcm_write_idx) {
+ s->pcmdata = s->in.pcm_left_fifo[s->in.pcm_read_idx % 16] |
+ (s->in.pcm_right_fifo[s->in.pcm_read_idx % 16] << 16);
+ s->in.pcm_read_idx++;
+ } else {
+ return 0;
+ }
+ return s->pcmdata;
+ case AC_MICDATA:
+ /* check if fifo is not empty */
+ if (s->in.mic_read_idx < s->in.mic_write_idx) {
+ s->micdata = s->in.mic_fifo[s->in.mic_read_idx % 16];
+ s->in.mic_read_idx++;
+ } else {
+ return 0;
+ }
+ return s->micdata;
+ default:
+ hw_error("s5pc1xx.ac97: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* Write AC97 by OS */
+static void s5pc1xx_ac97_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+
+ switch(offset) {
+ case AC_GLBCTRL:
+ if (value & COLD_RESET) {
+ s->reset = 1;
+ s5pc1xx_ac97_reset(s);
+ }
+
+ if (value & WARM_RESET) {
+ s->reset = 0;
+ s5pc1xx_ac97_irq(s, CODEC_READY_INT, 0);
+ }
+
+ if (s->reset)
+ break;
+
+ if ((value & AC_LINK_ON) > (s->glbctrl & AC_LINK_ON)) {
+ s->sync_en = 1;
+ s5pc1xx_ac97_sync(s);
+ }
+
+ /* the value is set high above */
+ s->sync_en = (value & AC_LINK_ON) ? : 0;
+ s->stream = (value & TRANSFER_EN) ? 1 : 0;
+
+ if (value & ALL_CLEAR)
+ s5pc1xx_ac97_irq(s, 0, value & ALL_CLEAR);
+
+ s->glbctrl = value & ~ALL_CLEAR;
+ break;
+ case AC_CODEC_CMD:
+ if (!s->reset)
+ s->codec_cmd = value;
+ break;
+ case AC_PCMDATA:
+ if (s->reset)
+ break;
+
+ /* check if fifo is full */
+ if (s->out.pcm_write_idx == s->out.pcm_read_idx + 16)
+ break;
+
+ s->out.pcm_left_fifo[s->out.pcm_write_idx % 16] =
+ value & ALL_BITS(15, 0);
+ s->out.pcm_right_fifo[s->out.pcm_write_idx % 16] =
+ value >> 16 & ALL_BITS(15, 0);
+
+ s->out.pcm_write_idx++;
+ break;
+ case AC_MICDATA:
+ /* mic data can't be written */
+ break;
+ default:
+ hw_error("s5pc1xx.ac97: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_ac97_readfn[] = {
+ s5pc1xx_ac97_read,
+ s5pc1xx_ac97_read,
+ s5pc1xx_ac97_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_ac97_writefn[] = {
+ s5pc1xx_ac97_write,
+ s5pc1xx_ac97_write,
+ s5pc1xx_ac97_write
+};
+
+static void s5pc1xx_ac97_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+ uint64_t last;
+ int i;
+
+ qemu_put_be32s(f, &s->glbctrl);
+ qemu_put_be32s(f, &s->glbstat);
+ qemu_put_be32s(f, &s->codec_cmd);
+ qemu_put_be32s(f, &s->codec_stat);
+ qemu_put_be32s(f, &s->pcmaddr);
+ qemu_put_be32s(f, &s->micaddr);
+ qemu_put_be32s(f, &s->pcmdata);
+ qemu_put_be32s(f, &s->micdata);
+
+ for (i = 0; i < 16; i++) {
+ qemu_put_be16s(f, &s->in.pcm_left_fifo[i]);
+ qemu_put_be16s(f, &s->in.pcm_right_fifo[i]);
+ qemu_put_be16s(f, &s->in.mic_fifo[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_put_be32s(f, &s->in.data_phase[i]);
+ }
+
+ qemu_put_be64s(f, &s->in.pcm_write_idx);
+ qemu_put_be64s(f, &s->in.pcm_read_idx);
+
+ qemu_put_be64s(f, &s->in.mic_write_idx);
+ qemu_put_be64s(f, &s->in.mic_read_idx);
+
+ qemu_put_be16s(f, &s->in.tag_phase);
+
+ for (i = 0; i < 16; i++) {
+ qemu_put_be16s(f, &s->out.pcm_left_fifo[i]);
+ qemu_put_be16s(f, &s->out.pcm_right_fifo[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_put_be32s(f, &s->out.data_phase[i]);
+ }
+
+ qemu_put_be64s(f, &s->out.pcm_write_idx);
+ qemu_put_be64s(f, &s->out.pcm_read_idx);
+ qemu_put_be16s(f, &s->out.tag_phase);
+
+ qemu_put_8s(f, &s->delay);
+ qemu_put_8s(f, &s->cur_pos);
+ qemu_put_8s(f, &s->stream);
+ qemu_put_8s(f, &s->sync_en);
+ qemu_put_8s(f, &s->reset);
+
+ last = qemu_get_clock(vm_clock) - s->last_ac97_time;
+ qemu_put_be64s(f, &last);
+
+ qemu_put_timer(f, s->ac97_timer);
+}
+
+static int s5pc1xx_ac97_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxAC97State *s = (S5pc1xxAC97State *)opaque;
+ uint64_t last;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->glbctrl);
+ qemu_get_be32s(f, &s->glbstat);
+ qemu_get_be32s(f, &s->codec_cmd);
+ qemu_get_be32s(f, &s->codec_stat);
+ qemu_get_be32s(f, &s->pcmaddr);
+ qemu_get_be32s(f, &s->micaddr);
+ qemu_get_be32s(f, &s->pcmdata);
+ qemu_get_be32s(f, &s->micdata);
+
+ for (i = 0; i < 16; i++) {
+ qemu_get_be16s(f, &s->in.pcm_left_fifo[i]);
+ qemu_get_be16s(f, &s->in.pcm_right_fifo[i]);
+ qemu_get_be16s(f, &s->in.mic_fifo[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_get_be32s(f, &s->in.data_phase[i]);
+ }
+
+ qemu_get_be64s(f, &s->in.pcm_write_idx);
+ qemu_get_be64s(f, &s->in.pcm_read_idx);
+
+ qemu_get_be64s(f, &s->in.mic_write_idx);
+ qemu_get_be64s(f, &s->in.mic_read_idx);
+
+ qemu_get_be16s(f, &s->in.tag_phase);
+
+ for (i = 0; i < 16; i++) {
+ qemu_get_be16s(f, &s->out.pcm_left_fifo[i]);
+ qemu_get_be16s(f, &s->out.pcm_right_fifo[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_get_be32s(f, &s->out.data_phase[i]);
+ }
+
+ qemu_get_be64s(f, &s->out.pcm_write_idx);
+ qemu_get_be64s(f, &s->out.pcm_read_idx);
+ qemu_get_be16s(f, &s->out.tag_phase);
+
+ qemu_get_8s(f, &s->delay);
+ qemu_get_8s(f, &s->cur_pos);
+ qemu_get_8s(f, &s->stream);
+ qemu_get_8s(f, &s->sync_en);
+ qemu_get_8s(f, &s->reset);
+
+ qemu_get_be64s(f, &last);
+ s->last_ac97_time = qemu_get_clock(vm_clock) - last;
+
+ qemu_get_timer(f, s->ac97_timer);
+
+ return 0;
+}
+
+/* AC97 initialization */
+static int s5pc1xx_ac97_init(SysBusDevice *dev)
+{
+ S5pc1xxAC97State *s = FROM_SYSBUS(S5pc1xxAC97State, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_ac97_readfn, s5pc1xx_ac97_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_AC97_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_AC97, 0, s5pc1xx_ac97_gpio_readfn,
+ s5pc1xx_ac97_gpio_writefn, NULL, s);
+ s->ac97_timer = qemu_new_timer(vm_clock, s5pc1xx_ac97_sync, s);
+
+ qemu_register_reset(s5pc1xx_ac97_reset, s);
+ s5pc1xx_ac97_reset(s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.ac97", -1, 1,
+ s5pc1xx_ac97_save, s5pc1xx_ac97_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_ac97_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.ac97", sizeof(S5pc1xxAC97State),
+ s5pc1xx_ac97_init);
+}
+
+device_init(s5pc1xx_ac97_register_devices)
--- /dev/null
+/*
+ * Clock controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on OMAP CMU (hw/omap_clk.c)
+ */
+
+#include "sysbus.h"
+#include "s5pc1xx.h"
+
+
+/* PLL lock */
+#define APLL_LOCK 0x000 /* R/W Control PLL locking period for APLL 0x0000_0FFF */
+#define MPLL_LOCK 0x008 /* R/W Control PLL locking period for MPLL 0x0000_0FFF */
+#define EPLL_LOCK 0x010 /* R/W Control PLL locking period for EPLL 0x0000_0FFF */
+#define VPLL_LOCK 0x020 /* R/W Control PLL locking period for VPLL 0x0000_0FFF */
+
+/* PLL control */
+#define APLL_CON 0x100 /* R/W Control PLL output frequency for APLL 0x0C80_0301 */
+#define MPLL_CON 0x108 /* R/W Control PLL output frequency for MPLL 0x014D_0301 */
+#define EPLL_CON 0x110 /* R/W Control PLL output frequency for EPLL 0x0085_0302 */
+#define VPLL_CON 0x120 /* R/W Control PLL output frequency for VPLL 0x006C_0303 */
+
+/* Clock source */
+#define CLK_SRC0 0x200 /* R/W Select clock source 0 (Main) 0x0000_0000 */
+
+ #define ONENAND_SHIFT 28
+ #define MUX133_SHIFT 24
+ #define MUX166_SHIFT 20
+ #define MUX200_SHIFT 16
+ #define VPLL_SHIFT 12
+ #define EPLL_SHIFT 8
+ #define MPLL_SHIFT 4
+ #define APLL_SHIFT 0
+
+#define CLK_SRC1 0x204 /* R/W Select clock source 1 (Multimedia) 0x0000_0000 */
+#define CLK_SRC2 0x208 /* R/W Select clock source 2 (Multimedia) 0x0000_0000 */
+
+ #define MFC_SHIFT 4
+ #define G3D_SHIFT 0
+
+#define CLK_SRC3 0x20C /* R/W Select clock source 3 (Multimedia) 0x0000_0000 */
+#define CLK_SRC4 0x210 /* R/W Select clock source 4 (Connectivity) 0x0000_0000 */
+#define CLK_SRC5 0x214 /* R/W Select clock source 5 (Connectivity) 0x0000_0000 */
+
+ #define MUX_PWM_SHIFT 12
+
+#define CLK_SRC6 0x218 /* R/W Select clock source 6 (Audio) 0x0000_0000 */
+
+ #define MUX_ONEDRAM_SHIFT 24
+ #define MUX_HPM_SHIFT 16
+ #define MUX_SPDIF_SHIFT 12
+ #define MUX_AUDIO_2_SHIFT 8
+ #define MUX_AUDIO_1_SHIFT 4
+ #define MUX_AUDIO_0_SHIFT 0
+
+#define CLK_SRC_MASK0 0x280 /* R/W Clock source mask0 0xFFFF_FFFF */
+#define CLK_SRC_MASK1 0x284 /* R/W Clock source mask1 0xFFFF_FFFF */
+
+/* Clock divider */
+#define CLK_DIV0 0x300 /* R/W Set clock divider ratio 0 (System clocks) 0x0000_0000 */
+
+#define PCLK66_SHIFT 28
+#define HCLK133_SHIFT 24
+#define PCLK83_SHIFT 20
+#define HCLK166_SHIFT 16
+#define PCLK100_SHIFT 12
+#define HCLK200_SHIFT 8
+#define A2M_SHIFT 4
+#define APLL_SHIFT 0
+
+#define CLK_DIV1 0x304 /* R/W Set clock divider ratio 1 (Multimedia) 0x0000_0000 */
+#define CLK_DIV2 0x308 /* R/W Set clock divider ratio 2 (Multimedia) 0x0000_0000 */
+#define CLK_DIV3 0x30C /* R/W Set clock divider ratio 3 (Multimedia) 0x0000_0000 */
+#define CLK_DIV4 0x310 /* R/W Set clock divider ratio 4 (Connectivity) 0x0000_0000 */
+#define CLK_DIV5 0x314 /* R/W Set clock divider ratio 5 (Connectivity) 0x0000_0000 */
+
+ #define DIV_PWM_SHIFT 12
+
+#define CLK_DIV6 0x318 /* R/W Set clock divider ratio 6 (Audio & Others) 0x0000_0000 */
+
+ #define DIV_AUDIO_2_SHIFT 8
+ #define DIV_AUDIO_1_SHIFT 4
+ #define DIV_AUDIO_0_SHIFT 0
+
+#define CLK_DIV7 0x31C /* R/W Set clock divider ratio 7 (IEM_IEC) 0x0000_0000 */
+
+/* Clock gating */
+#define CLK_GATE_MAIN0 0x400 /* R/W Control AXI/AHB clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_MAIN1 0x404 /* R/W Control AXI/AHB clock gating 1 0xFFFF_FFFF */
+#define CLK_GATE_MAIN2 0x408 /* R/W Control AXI/AHB clock gating 2 0xFFFF_FFFF */
+#define CLK_GATE_PERI0 0x420 /* R/W Control APB clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_PERI1 0x424 /* R/W Control APB clock gating 1 0xFFFF_FFFF */
+#define CLK_GATE_SCLK0 0x440 /* R/W Control SCLK clock gating0 0xFFFF_FFFF */
+#define CLK_GATE_SCLK1 0x444 /* R/W Control SCLK clock gating1 0xFFFF_FFFF */
+#define CLK_GATE_IP0 0x460 /* R/W Control IP clock gating0 0xFFFF_FFFF */
+#define CLK_GATE_IP1 0x464 /* R/W Control IP clock gating1 0xFFFF_FFFF */
+#define CLK_GATE_IP2 0x468 /* R/W Control IP clock gating2 0xFFFF_FFFF */
+#define CLK_GATE_IP3 0x46C /* R/W Control IP clock gating3 0xFFFF_FFFF */
+#define CLK_GATE_IP4 0x470 /* R/W Control IP clock gating4 0xFFFF_FFFF */
+#define CLK_GATE_BLOCK 0x480 /* R/W Control block clock gating 0xFFFF_FFFF */
+#define CLK_GATE_BUS0 0x484 /* R/W Control AXI/AHB bus clock gating 0 0xFFFF_FFFF */
+#define CLK_GATE_BUS1 0x488 /* R/W Control AXI/AHB bus clock gating 1 0xFFFF_FFFF */
+
+/* Clock output */
+#define CLK_OUT 0x500 /* R/W Select clock output 0x0000_0000 */
+
+/* Clock divider status */
+#define CLK_DIV_STAT0 0x1000 /* R Clock divider status 0 (CLK_DIV0~3) 0x1111_1111 */
+#define CLK_DIV_STAT1 0x1004 /* R Clock divider status 1 (CLK_DIV4~5) 0x0001_0000 */
+
+/* Clock MUX status */
+#define CLK_MUX_STAT0 0x1100 /* R Clock MUX status 0 0x0000_0000 */
+#define CLK_MUX_STAT1 0x1104 /* R Clock MUX status 1 0x0000_0000 */
+
+/* Control bits */
+#define XOM_0 0
+#define VPLLSRC_SEL 1
+#define ALL_MUX_BITS_0 (1 << ONENAND_SHIFT) | (1 << MUX133_SHIFT) | \
+ (1 << MUX166_SHIFT) | (1 << MUX200_SHIFT) | \
+ (1 << VPLL_SHIFT) | (1 << EPLL_SHIFT) | \
+ (1 << MPLL_SHIFT) | (1 << APLL_SHIFT)
+#define ALL_DIV_BITS_0 (7 << PCLK66_SHIFT) | (0xf << HCLK133_SHIFT) | \
+ (7 << PCLK83_SHIFT) | (0xf << HCLK166_SHIFT) | \
+ (7 << PCLK100_SHIFT)| (7 << HCLK200_SHIFT) | \
+ (7 << A2M_SHIFT) | (7 << APLL_SHIFT)
+
+#define ALL_MUX_BITS_5 0xffff /* all the [15:0] bits are used */
+#define ALL_DIV_BITS_5 0xffff /* all the [15:0] bits are used */
+
+#define NONE -1
+
+#define S5PC1XX_CLK_REG_MEM_SIZE 0x1110
+
+
+typedef struct {
+ SysBusDevice busdev;
+
+ uint32_t lock_data[5]; /* PLL lock */
+ uint32_t conf_data[5]; /* PLL control */
+ uint32_t clk_src[7]; /* Clock source */
+ uint32_t src_mask[2]; /* Clock source mask */
+ uint32_t clk_div[8]; /* Clock divider */
+ uint32_t clk_gate[23]; /* Clock gating */
+ uint32_t clk_out; /* Clock output */
+ uint32_t div_stat[2]; /* Clock divider status */
+ uint32_t mux_stat[2]; /* Clock MUX status */
+} CmuStat;
+
+typedef struct Clk {
+ const char *name; /* Clock name */
+ const char *alias; /* Clock notes */
+ struct Clk *parent; /* Parent clock */
+
+ struct Clk *parents[9]; /* Parent cases */
+
+ unsigned short enabled; /* Is enabled, regardless of its input clk */
+ unsigned long rate; /* Current rate */
+
+ unsigned int div_val; /* Rate relative to input (if .enabled) */
+ unsigned int mult_val; /* Rate relative to input (if .enabled) */
+
+ short mux_shift; /* MANDATORY FIELD - Shift for mux value in CLK_SRC<src_reg_num> */
+ short div_shift; /* MANDATORY FIELD - Shift for divisor value in CLK_DIV<div_reg_num> */
+
+ unsigned short src_reg_num; /* See above (default value = 0) */
+ unsigned short div_reg_num; /* See above (default value = 0) */
+} Clk;
+
+
+/* Clocks */
+
+/* oscillators */
+static Clk xtal_osc12m = {
+ .name = "XXTI",
+ .rate = 24000000,
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk xtal_osc27m = {
+ .name = "XXTI27",
+ .rate = 27000000,
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk xtal_usb_osc48m = {
+ .name = "XusbXTI",
+ .rate = 24000000,
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk xtal_rtc_osc32k = {
+ .name = "XrtcXTI",
+ .rate = 32768,
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk sclk_hdmi27m = {
+ .name = "SCLK_HDMI27M",
+ .alias = "clkin",
+ .parents = {&xtal_osc27m},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk fin_pll = {
+ .name = "fin_pll",
+ .alias = "clkin",
+ .parents = {XOM_0 ? &xtal_usb_osc48m : &xtal_osc12m},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+/* PLLs */
+static Clk apll = {
+ .name = "apll",
+ .parents = {&fin_pll},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk mpll = {
+ .name = "mpll",
+ .parents = {&fin_pll},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk epll = {
+ .name = "epll",
+ .parents = {&fin_pll},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+static Clk vpll = {
+ .name = "vpll",
+ .parents = {VPLLSRC_SEL ? &sclk_hdmi27m : &fin_pll},
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+/* reference clocks */
+static Clk sclk_apll = {
+ .name = "sclk_apll",
+ .parents = {&fin_pll, &apll},
+ .mux_shift = APLL_SHIFT, /* MUXapll */
+ .div_shift = NONE,
+};
+
+static Clk sclk_mpll = {
+ .name = "sclk_mpll",
+ .parents = {&fin_pll, &mpll},
+ .mux_shift = MPLL_SHIFT, /* MUXmpll */
+ .div_shift = NONE,
+};
+
+static Clk sclk_epll = {
+ .name = "sclk_epll",
+ .parents = {&fin_pll, &epll},
+ .mux_shift = EPLL_SHIFT, /* MUXepll */
+ .div_shift = NONE,
+};
+
+static Clk sclk_vpll = {
+ .name = "sclk_vpll",
+ .parents = {&fin_pll, &vpll},
+ .mux_shift = VPLL_SHIFT, /* MUXvpll */
+ .div_shift = NONE,
+};
+
+static Clk sclk_a2m = {
+ .name = "sclk_a2m",
+ .parents = {&sclk_apll},
+ .mux_shift = NONE,
+ .div_shift = A2M_SHIFT, /* DIVa2m (1~8) */
+};
+
+/* 133MHz domain clocks */
+static Clk hclk133 = {
+ .name = "hclk_133",
+ .parents = {&sclk_mpll, &sclk_a2m},
+ .mux_shift = MUX133_SHIFT, /* MUX133 */
+ .div_shift = HCLK133_SHIFT, /* DIVck133 (1~16) */
+};
+
+static Clk pclk66 = {
+ .name = "pclk_66",
+ .parents = {&hclk133},
+ .mux_shift = NONE,
+ .div_shift = PCLK66_SHIFT, /* DIVck66 (1~8) */
+};
+
+/* 166MHz domain clocks */
+static Clk hclk166 = {
+ .name = "hclk_166",
+ .parents = {&sclk_mpll, &sclk_a2m},
+ .mux_shift = MUX166_SHIFT, /* MUX166 */
+ .div_shift = HCLK166_SHIFT, /* DIVck166 (1~16) */
+};
+
+static Clk pclk83 = {
+ .name = "pclk_83",
+ .parents = {&hclk166},
+ .mux_shift = NONE,
+ .div_shift = PCLK83_SHIFT, /* DIVck83 (1~8) */
+};
+
+/* 200MHz domain clocks */
+static Clk armclk = {
+ .name = "armclk",
+ .parents = {&sclk_apll, &sclk_mpll},
+ .mux_shift = MUX200_SHIFT, /* MUX200 */
+ .div_shift = APLL_SHIFT, /* DIVapll (1~8) */
+};
+
+static Clk hclk200 = {
+ .name = "hclk_200",
+ .parents = {&armclk},
+ .mux_shift = NONE,
+ .div_shift = HCLK200_SHIFT, /* DIVck200 (1~8) */
+};
+
+static Clk pclk100 = {
+ .name = "pclk_100",
+ .parents = {&hclk200},
+ .mux_shift = NONE,
+ .div_shift = PCLK100_SHIFT, /* DIVck100 (1~8) */
+};
+
+static Clk hclk100 = {
+ .name = "hclk_100",
+ .parents = {&hclk200},
+ .div_val = 2, /* DIVimem (2) */
+ .mux_shift = NONE,
+ .div_shift = NONE,
+};
+
+/* Special clocks */
+
+static Clk sclk_pwm = {
+ .name = "sclk_pwm",
+ .parents = {&xtal_osc12m, &xtal_usb_osc48m, &sclk_hdmi27m,
+
+ /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+ &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+ &sclk_mpll, &sclk_epll, &sclk_vpll},
+ .src_reg_num = 5, /* CLK_SRC5 register */
+ .mux_shift = MUX_PWM_SHIFT, /* MUXpwm */
+ .div_reg_num = 5, /* CLK_DIV5 register */
+ .div_shift = DIV_PWM_SHIFT, /* DIVpwm (1~16) */
+};
+
+static Clk sclk_audio_0 = {
+ .name = "sclk_audio_0",
+ .parents = {&xtal_osc12m,
+
+ /* TODO: should be PCMCDCLK0 below */
+ &xtal_usb_osc48m,
+
+ &sclk_hdmi27m,
+
+ /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+ &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+ &sclk_mpll, &sclk_epll, &sclk_vpll},
+ .src_reg_num = 6, /* CLK_SRC6 register */
+ .mux_shift = MUX_AUDIO_0_SHIFT, /* MUXaudio0 */
+ .div_reg_num = 6, /* CLK_DIV6 register */
+ .div_shift = DIV_AUDIO_0_SHIFT, /* DIVaudio0 (1~16) */
+};
+
+static Clk sclk_audio_1 = {
+ .name = "sclk_audio_1",
+ .parents = {
+ /* TODO should be I2SCDCLK1, PCMCDCLK1 below */
+ &xtal_osc12m, &xtal_usb_osc48m,
+
+ &sclk_hdmi27m,
+
+ /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+ &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+ &sclk_mpll, &sclk_epll, &sclk_vpll},
+ .src_reg_num = 6, /* CLK_SRC6 register */
+ .mux_shift = MUX_AUDIO_1_SHIFT, /* MUXaudio1 */
+ .div_reg_num = 6, /* CLK_DIV6 register */
+ .div_shift = DIV_AUDIO_1_SHIFT, /* DIVaudio1 (1~16) */
+};
+
+static Clk sclk_audio_2 = {
+ .name = "sclk_audio_2",
+ .parents = {
+ /* TODO: should be I2SCDCLK2, PCMCDCLK2 below */
+ &xtal_osc12m, &xtal_usb_osc48m,
+
+ &sclk_hdmi27m,
+
+ /* TODO: should be SCLK_USBPHY0, SCLK_USBPHY1, SCLK_HDMIPHY below */
+ &sclk_hdmi27m, &sclk_hdmi27m, &sclk_hdmi27m,
+
+ &sclk_mpll, &sclk_epll, &sclk_vpll},
+ .src_reg_num = 6, /* CLK_SRC6 register */
+ .mux_shift = MUX_AUDIO_2_SHIFT, /* MUXaudio2 */
+ .div_reg_num = 6, /* CLK_DIV6 register */
+ .div_shift = DIV_AUDIO_2_SHIFT, /* DIVaudio2 (1~16) */
+};
+
+static Clk sclk_spdif = {
+ .name = "sclk_spdif",
+ .parents = {&sclk_audio_0, &sclk_audio_1, &sclk_audio_2},
+ .src_reg_num = 6, /* CLK_SRC6 register */
+ .mux_shift = MUX_SPDIF_SHIFT, /* MUXspdif */
+ .div_shift = NONE,
+};
+
+static Clk *onchip_clks[] = {
+
+ /* non-ULPD clocks */
+ &xtal_osc12m,
+ &xtal_osc27m,
+ &xtal_usb_osc48m,
+ &xtal_rtc_osc32k,
+ &sclk_hdmi27m,
+ &fin_pll,
+
+ /* CLOCKS FROM CMU */
+ &apll,
+ &mpll,
+ &epll,
+ &vpll,
+ &sclk_apll,
+ &sclk_mpll,
+ &sclk_epll,
+ &sclk_vpll,
+ &sclk_a2m,
+ &hclk133,
+ &pclk66,
+ &hclk166,
+ &pclk83,
+ &armclk,
+ &hclk200,
+ &pclk100,
+ &hclk100,
+ &sclk_pwm,
+ &sclk_audio_0,
+ &sclk_audio_1,
+ &sclk_audio_2,
+ &sclk_spdif,
+
+ 0
+};
+
+static void s5pc1xx_clk_reset(void *opaque)
+{
+ CmuStat *s = (CmuStat *)opaque;
+
+ /* Set default values for registers */
+ /* TODO: Add all the rest */
+ s->clk_div[0] = 0x14131330;
+ s->clk_src[0] = 0x10001111;
+ s->clk_src[4] = 0x66666666;
+ s->clk_src[5] = 0x777;
+ s->clk_src[6] = 0x1000000;
+
+ /* LOCKED bit is always set */
+ s->conf_data[0] = 0xA0C80601 | (1 << 29);
+ s->conf_data[1] = 0xA29B0C01 | (1 << 29);
+ s->conf_data[2] = 0xA0600602 | (1 << 29);
+ s->conf_data[4] = 0xA06C0603 | (1 << 29);
+}
+
+/* Find a clock by its name and return the clk structure */
+Clk *s5pc1xx_findclk(const char *name)
+{
+ Clk **i, *cur;
+
+ for (i = onchip_clks; *i; i++) {
+ cur = *i;
+ if (!strcmp(cur->name, name) ||
+ (cur->alias && !strcmp(cur->alias, name)))
+ return cur;
+ }
+ hw_error("%s: clock %s not found\n", __FUNCTION__, name);
+}
+
+/* Get a frequency */
+int64_t s5pc1xx_clk_getrate(Clk *clk)
+{
+ return clk->rate;
+}
+
+/* Update parents flow */
+static void s5pc1xx_clk_reparent(CmuStat *cmu_stat)
+{
+ Clk **i, *cur;
+ unsigned short parent_num;
+
+ for (i = onchip_clks; *i; i++) {
+ cur = *i;
+ parent_num = 0;
+
+ if (cur->mux_shift > NONE)
+ parent_num =
+ cmu_stat->clk_src[cur->src_reg_num] >> cur->mux_shift & 0xf;
+ cur->parent = cur->parents[parent_num];
+ }
+}
+
+/* Update clocks rates */
+static void s5pc1xx_clk_rate_update(CmuStat *cmu_stat)
+{
+ Clk **i, *cur;
+
+ for (i = onchip_clks; *i; i++) {
+ cur = *i;
+
+ cur->div_val = cur->div_val ?: 1;
+ cur->mult_val = cur->mult_val ?: 1;
+
+ /* update all divisors using div_shift if any,
+ * divisors for (A,E,M,V)PLL are not updated here */
+ if (cur->div_shift > NONE)
+ cur->div_val =
+ (cmu_stat->clk_div[cur->div_reg_num] >> cur->div_shift & 0xf) + 1;
+
+ /* update frequencies for all the clocks except the oscillators */
+ if (cur->parent)
+ cur->rate = muldiv64(cur->parent->rate, cur->mult_val, cur->div_val);
+ }
+}
+
+/* Set a frequency */
+static void s5pc1xx_clk_setrate(CmuStat *cmu_stat, Clk *clk,
+ int divide, int multiply)
+{
+ clk->div_val = divide;
+ clk->mult_val = multiply;
+}
+
+/* Set (A,M,E,V)PLL params after write operation */
+static void set_pll_conf(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ CmuStat *s = (CmuStat *)opaque;
+
+ /* Calculate control values depending on clock kind */
+ switch ((offset - 0x100) >> 3) {
+
+ case 0:
+ apll.enabled = (val >> 31) & 0x1;
+ /* include/exclude clock depending on .enabled value */
+ if (apll.enabled)
+ s->clk_src[0] |= (1 << sclk_apll.mux_shift);
+ else
+ s->clk_src[0] &= ~(1 << sclk_apll.mux_shift);
+
+ s5pc1xx_clk_setrate(s, &apll,
+ ((val >> 8) & 0x3F) << ((val & 0x7) - 1),
+ (val >> 16) & 0x3FF);
+ break;
+
+ case 1:
+ mpll.enabled = (val >> 31) & 0x1;
+ if (mpll.enabled)
+ s->clk_src[0] |= (1 << sclk_mpll.mux_shift);
+ else
+ s->clk_src[0] &= ~(1 << sclk_mpll.mux_shift);
+
+ s5pc1xx_clk_setrate(s, &mpll,
+ ((val >> 8) & 0x3F) << (val & 0x7),
+ (val >> 16) & 0x3FF);
+ break;
+
+ case 2:
+ epll.enabled = (val >> 31) & 0x1;
+ if (epll.enabled)
+ s->clk_src[0] |= (1 << sclk_epll.mux_shift);
+ else
+ s->clk_src[0] &= ~(1 << sclk_epll.mux_shift);
+
+ s5pc1xx_clk_setrate(s, &epll,
+ ((val >> 8) & 0x3F) << (val & 0x7),
+ (val >> 16) & 0x3FF);
+ break;
+
+ case 4:
+ vpll.enabled = (val >> 31) & 0x1;
+ if (vpll.enabled)
+ s->clk_src[0] |= (1 << sclk_vpll.mux_shift);
+ else
+ s->clk_src[0] &= ~(1 << sclk_vpll.mux_shift);
+
+ s5pc1xx_clk_setrate(s, &vpll,
+ ((val >> 8) & 0x3F) << (val & 0x7),
+ (val >> 16) & 0x3FF);
+ break;
+
+ default:
+ hw_error("s5pc1xx.clk: bad pll offset 0x" TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static void s5pc1xx_clk_config(CmuStat *s)
+{
+ set_pll_conf(s, APLL_CON, s->conf_data[0]);
+ set_pll_conf(s, MPLL_CON, s->conf_data[1]);
+ set_pll_conf(s, EPLL_CON, s->conf_data[2]);
+ set_pll_conf(s, VPLL_CON, s->conf_data[4]);
+
+ s5pc1xx_clk_reparent(s);
+ s5pc1xx_clk_rate_update(s);
+}
+
+static uint32_t register_read(void *opaque, target_phys_addr_t offset)
+{
+ CmuStat *s = (CmuStat *)opaque;
+
+ switch (offset) {
+ case 0x000 ... 0x020:
+ return s->lock_data[offset >> 3];
+ case 0x100 ... 0x120:
+ return s->conf_data[(offset - 0x100) >> 3];
+ case 0x200 ... 0x218:
+ return s->clk_src[(offset - 0x200) >> 2];
+ case 0x280 ... 0x284:
+ return s->src_mask[(offset - 0x280) >> 2];
+ case 0x300 ... 0x31C:
+ return s->clk_div[(offset - 0x300) >> 2];
+ case 0x400 ... 0x488:
+ return s->clk_gate[(offset - 0x400) >> 2];
+ case 0x500:
+ return s->clk_out;
+ case 0x1000:
+ case 0x1004:
+ return 0;
+ case 0x1100:
+ return
+ (1 << ((s->clk_src[0] >> ONENAND_SHIFT) & 0x1)) << ONENAND_SHIFT |
+ (1 << ((s->clk_src[0] >> MUX133_SHIFT) & 0x1)) << MUX133_SHIFT |
+ (1 << ((s->clk_src[0] >> MUX166_SHIFT) & 0x1)) << MUX166_SHIFT |
+ (1 << ((s->clk_src[0] >> MUX200_SHIFT) & 0x1)) << MUX200_SHIFT |
+ (1 << ((s->clk_src[0] >> VPLL_SHIFT) & 0x1)) << VPLL_SHIFT |
+ (1 << ((s->clk_src[0] >> EPLL_SHIFT) & 0x1)) << EPLL_SHIFT |
+ (1 << ((s->clk_src[0] >> MPLL_SHIFT) & 0x1)) << MPLL_SHIFT |
+ (1 << ((s->clk_src[0] >> APLL_SHIFT) & 0x1)) << APLL_SHIFT;
+ case 0x1104:
+ return
+ (1 << ((s->clk_src[6] >> MUX_HPM_SHIFT) & 0x1)) << MUX_HPM_SHIFT |
+ ((((s->clk_src[6] >> MUX_ONEDRAM_SHIFT) << 1) & 0x6) |
+ ((s->clk_src[6] >> MUX_ONEDRAM_SHIFT) & 0x1)) << 8 |
+ ((((s->clk_src[2] >> MFC_SHIFT) << 1) & 0x6) |
+ ((s->clk_src[2] >> MFC_SHIFT) & 0x1)) << MFC_SHIFT |
+ ((((s->clk_src[2] >> G3D_SHIFT) << 1) & 0x6) |
+ ((s->clk_src[2] >> G3D_SHIFT) & 0x1)) << G3D_SHIFT;
+ default:
+ hw_error("s5pc1xx.clk: bad read offset 0x" TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+static void register_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ CmuStat *s = (CmuStat *)opaque;
+
+ switch (offset) {
+ case 0x000 ... 0x020:
+ s->lock_data[offset >> 3] = val;
+ break;
+ case 0x100 ... 0x120:
+ /* LOCKED bit is always set */
+ s->conf_data[(offset - 0x100) >> 3] = val | (1 << 29);
+
+ set_pll_conf(s, offset, val);
+ s5pc1xx_clk_reparent(s);
+ s5pc1xx_clk_rate_update(s);
+
+ break;
+ case 0x200 ... 0x218:
+ s->clk_src[(offset - 0x200) >> 2] = val;
+
+ /* clear reserved bits for security */
+ s->clk_src[0] &= ALL_MUX_BITS_0;
+ s->clk_src[5] &= ALL_MUX_BITS_5;
+
+ s5pc1xx_clk_reparent(s);
+
+ break;
+ case 0x280 ... 0x284:
+ s->src_mask[(offset - 0x280) >> 2] = val;
+ break;
+ case 0x300 ... 0x31C:
+ s->clk_div[(offset - 0x300) >> 2] = val;
+
+ /* clear reserved bits for security */
+ s->clk_div[0] &= ALL_DIV_BITS_0;
+ s->clk_div[5] &= ALL_DIV_BITS_5;
+
+ s5pc1xx_clk_rate_update(s);
+
+ break;
+ case 0x400 ... 0x488:
+ s->clk_gate[(offset - 0x400) >> 2] = val;
+ break;
+ case 0x500:
+ s->clk_out = val;
+ break;
+ case 0x1000 ... 0x1004:
+ case 0x1100 ... 0x1104:
+ default:
+ hw_error("s5pc1xx.clk: bad write offset 0x" TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const register_readfn[] = {
+ register_read,
+ register_read,
+ register_read
+};
+
+static CPUWriteMemoryFunc * const register_writefn[] = {
+ register_write,
+ register_write,
+ register_write
+};
+
+static void s5pc1xx_clk_save(QEMUFile *f, void *opaque)
+{
+ CmuStat *s = (CmuStat *)opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->clk_out);
+
+ for (i = 0; i < 23; i++) {
+ qemu_put_be32s(f, s->clk_gate + i);
+ if (i > 7) {
+ continue;
+ }
+ qemu_put_be32s(f, s->clk_div + i);
+ if (i > 6) {
+ continue;
+ }
+ qemu_put_be32s(f, s->clk_src + i);
+ if (i > 4) {
+ continue;
+ }
+ qemu_put_be32s(f, s->lock_data + i);
+ qemu_put_be32s(f, s->conf_data + i);
+ if (i > 1) {
+ continue;
+ }
+ qemu_put_be32s(f, s->src_mask + i);
+ qemu_put_be32s(f, s->div_stat + i);
+ qemu_put_be32s(f, s->mux_stat + i);
+ }
+}
+
+static int s5pc1xx_clk_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CmuStat *s = (CmuStat *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->clk_out);
+
+ for (i = 0; i < 23; i++) {
+ qemu_get_be32s(f, s->clk_gate + i);
+ if (i > 7) {
+ continue;
+ }
+ qemu_get_be32s(f, s->clk_div + i);
+ if (i > 6) {
+ continue;
+ }
+ qemu_get_be32s(f, s->clk_src + i);
+ if (i > 4) {
+ continue;
+ }
+ qemu_get_be32s(f, s->lock_data + i);
+ qemu_get_be32s(f, s->conf_data + i);
+ if (i > 1) {
+ continue;
+ }
+ qemu_get_be32s(f, s->src_mask + i);
+ qemu_get_be32s(f, s->div_stat + i);
+ qemu_get_be32s(f, s->mux_stat + i);
+ }
+ s5pc1xx_clk_config(s);
+ return 0;
+}
+
+/* Initialize clock */
+static int s5pc1xx_clk_init(SysBusDevice *dev)
+{
+ CmuStat *s = FROM_SYSBUS(CmuStat, dev);
+ int iomemtype;
+
+ s5pc1xx_clk_reset(s);
+ s5pc1xx_clk_config(s);
+
+ /* memory mapping */
+ iomemtype = cpu_register_io_memory(register_readfn, register_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_CLK_REG_MEM_SIZE, iomemtype);
+
+ qemu_register_reset(s5pc1xx_clk_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.clk", -1, 1,
+ s5pc1xx_clk_save, s5pc1xx_clk_load, s);
+ return 0;
+}
+
+static void s5pc1xx_clk_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.clk", sizeof(CmuStat), s5pc1xx_clk_init);
+}
+
+device_init(s5pc1xx_clk_register_devices)
--- /dev/null
+/*
+ * S5PC110 Test & Debug
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+/* Print all system information to stderr */
+static __attribute__((unused)) void debug_sysinfo(const struct s5pc1xx_state_s *s)
+{
+ fprintf(stderr, "CPU: %s\n", s->env->cpu_model_str);
+ fprintf(stderr, "SDRAM: %lu MB\n", s->sdram_size / (1024 * 1024));
+ fprintf(stderr, "SRAM: %lu KB\n", s->isram_size / 1024);
+ fprintf(stderr, "SROM: %lu KB\n", s->isrom_size / 1024);
+}
+
+
+/* Interrupt Controller */
+
+#define ADDR 0xF2000F00
+
+
+target_phys_addr_t vic_base[] = {
+ S5PC1XX_VIC0_BASE, S5PC1XX_VIC1_BASE, S5PC1XX_VIC2_BASE, S5PC1XX_VIC3_BASE
+};
+
+typedef enum {
+ enable, disable, swint, swclear, select_irq, selclear, priomask, daisyprio
+} irq_op;
+
+
+static __attribute__((unused)) void test_irq_handler(void *opaque, int irq,
+ int level)
+{
+ const char *name;
+
+ if (irq)
+ name = "FIQ";
+ else
+ name = "IRQ";
+
+ if (level)
+ fprintf(stderr, "%s was raised\n", name);
+}
+
+static __attribute__((unused)) qemu_irq *test_irq_init(void)
+{
+ return qemu_allocate_irqs(test_irq_handler, NULL, 2);
+}
+
+static __attribute__((unused)) uint32_t test_irq_op(irq_op op, int irq,
+ int is_wr, uint32_t val)
+{
+ int vic_id = irq / S5PC1XX_VIC_SIZE;
+ uint32_t res;
+ target_phys_addr_t base = vic_base[vic_id];
+ target_phys_addr_t off;
+
+ switch (op) {
+ case enable:
+ off = 0x10;
+ break;
+ case disable:
+ off = 0x14;
+ break;
+ case swint:
+ off = 0x18;
+ break;
+ case swclear:
+ off = 0x1C;
+ break;
+ case select_irq:
+ case selclear:
+ off = 0xC;
+ break;
+ case priomask:
+ off = 0x24;
+ break;
+ case daisyprio:
+ off = 0x28;
+ break;
+ default:
+ off = 0x0;
+ break;
+ }
+ if (op == priomask || op == daisyprio || !is_wr) {
+ if (is_wr) {
+ cpu_physical_memory_write(base + off, (uint8_t *)&val, 4);
+ } else {
+ cpu_physical_memory_read(base + off, (uint8_t *)&res, 4);
+ }
+ return res;
+ }
+ if (op == select_irq || op == selclear)
+ cpu_physical_memory_read(base + off, (uint8_t *)&res, 4);
+ if (op == select_irq)
+ res |= 1 << (irq % S5PC1XX_VIC_SIZE);
+ else if (op == selclear)
+ res &= ~(1 << (irq % S5PC1XX_VIC_SIZE));
+ else
+ res = 1 << (irq % S5PC1XX_VIC_SIZE);
+ cpu_physical_memory_write(base + off, (uint8_t *)&res, 4);
+ return 0;
+}
+
+static __attribute__((unused)) void test_irq_script(struct s5pc1xx_state_s *s)
+{
+ uint32_t res;
+
+ fprintf(stderr,"Step 1: Interrupts disabled. Raising and lowering them.\n");
+ qemu_irq_raise(s5pc1xx_get_irq(s, 14));
+ qemu_irq_raise(s5pc1xx_get_irq(s, 33));
+ qemu_irq_lower(s5pc1xx_get_irq(s, 14));
+ qemu_irq_lower(s5pc1xx_get_irq(s, 33));
+ qemu_irq_raise(s5pc1xx_get_irq(s, 69));
+ qemu_irq_lower(s5pc1xx_get_irq(s, 69));
+ qemu_irq_raise(s5pc1xx_get_irq(s, 101));
+
+ fprintf(stderr, "Step 2: Interrupt 101 is raised. Enable some other.\n");
+ test_irq_op(enable, 4, 1, 0);
+ test_irq_op(enable, 34, 1, 0);
+ test_irq_op(enable, 5, 1, 0);
+
+ fprintf(stderr, "Step 3: Interrupt 101 is raised. Enable it.\n");
+ res = 0xDDEEAABB;
+ cpu_physical_memory_write(0xF2300114, (const uint8_t *)&res, 4);
+ test_irq_op(enable, 101, 1, 0);
+ cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+ fprintf(stderr, "Interrupt 101 vector is %x\n", res);
+ qemu_irq_raise(s5pc1xx_get_irq(s, 5));
+ fprintf(stderr, "Step 4: Interrupt 101 has been acknoledged. "
+ "Interrupt 5 has been raised.\n");
+
+ fprintf(stderr, "Step 5: Generate IRQ 4 with higher priority.\n");
+ res = 0xa;
+ cpu_physical_memory_write(0xF2000210, (const uint8_t *)&res, 4);
+ res = 0xDDEEBBAA;
+ cpu_physical_memory_write(0xF2000110, (const uint8_t *)&res, 4);
+ qemu_irq_raise(s5pc1xx_get_irq(s, 4));
+
+ fprintf(stderr, "Step 6: Acknoledge IRQ 4. Then lower it.\n");
+ cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+ fprintf(stderr, "Interrupt 4 vector is %x\n", res);
+ qemu_irq_lower(s5pc1xx_get_irq(s, 4));
+
+ fprintf(stderr, "Step 7: Finalize IRQ 4 processing. No new interrupts "
+ "should appear as IRQ 101 is in progress.\n");
+ cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+
+ fprintf(stderr, "Step 8: Mask IRQ 4's priority, then raise it again.\n");
+ test_irq_op(priomask, 0, 1, 0xffff & ~(1 << 0xa));
+ qemu_irq_raise(s5pc1xx_get_irq(s, 4));
+
+ fprintf(stderr, "Step 9: Finalize IRQ 101 processing. "
+ "We should recive IRQ 5.\n");
+ res = 0xDDEEBBCC;
+ cpu_physical_memory_write(0xF2000114, (const uint8_t *)&res, 4);
+ qemu_irq_lower(s5pc1xx_get_irq(s, 101));
+ cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+ cpu_physical_memory_read(ADDR, (uint8_t *)&res, 4);
+ fprintf(stderr, "Interrupt 5 vector is %x\n", res);
+
+ fprintf(stderr, "Step 10: Finalize IRQs. Clear them all.\n");
+ qemu_irq_lower(s5pc1xx_get_irq(s, 5));
+ qemu_irq_lower(s5pc1xx_get_irq(s, 4));
+ cpu_physical_memory_write(ADDR, (const uint8_t *)&res, 4);
+}
+
+
+/* DMA */
+
+#define DATA_ADDR 0x20010000
+#define RESULT_ADDR 0x20020000
+#define PROG_ADDR 0x20030000
+
+#define DBGGO_ADDR 0xFA200D04
+#define DBG0_ADDR 0xFA200D08
+#define DBG1_ADDR 0xFA200D0C
+
+#define FSC_ADDR 0xFA200034
+#define FTC1_ADDR 0xFA200044
+#define CPC1_ADDR 0xFA20010C
+
+
+uint32_t dbg0 = 0x01A00000;
+uint32_t dbg1 = PROG_ADDR;
+uint32_t dbggo = 0x0;
+uint32_t dbgkill = 0x00010101;
+
+static const uint8_t dma_data[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32
+};
+
+static const uint8_t dma_stz[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV CCR SAI SS4 SB4 DAI DS4 DB4 */
+ 0x0C, /* DMASTZ */
+ 0x00 /* DMAEND */
+};
+
+static const uint8_t dma_stzlp[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV CCR SAI SS4 SB4 DAI DS4 DB4 */
+ 0x20, 0x01, /* DMALP 2 */
+ 0x0C, /* DMASTZ */
+ 0x38, 0x01, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+static const uint8_t dma_copy[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV CCR SAI SS4 SB4 DAI DS4 DB4 */
+ 0x20, 0x01, /* DMALP 2 */
+ 0x04, /* DMALD */
+ 0x08, /* DMAST */
+ 0x38, 0x02, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+/* Paradoxically but this should work correctly too. */
+static const uint8_t dma_copy_2[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x35, 0x40, 0x0D, 0x00, /* DMAMOV CCR SAI SS4 SB4 DAI DS4 DB4 */
+ 0x20, 0x01, /* DMALP 2 */
+ 0x08, /* DMAST */
+ 0x04, /* DMALD */
+ 0x38, 0x02, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+static const uint8_t dma_scatter[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x34, 0x40, 0x0D, 0x00, /* DMAMOV CCR SAF SS4 SB4 DAI DS4 DB4 */
+ 0x20, 0x01, /* DMALP 2 */
+ 0x04, /* DMALD */
+ 0x08, /* DMAST */
+ 0x38, 0x02, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+static const uint8_t dma_gather[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x35, 0x00, 0x0D, 0x00, /* DMAMOV CCR SAI SS4 SB4 DAF DS4 DB4 */
+ 0x20, 0x01, /* DMALP 2 */
+ 0x04, /* DMALD */
+ 0x08, /* DMAST */
+ 0x38, 0x02, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+/* Watchdog abort at DMAEND */
+static const uint8_t dma_nold[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV CCR SAF SS4 SB4 DAF DS4 DB4 */
+ 0x08, /* DMAST */
+ 0x18, /* DMANOP */
+ 0x00 /* DMAEND */
+};
+
+/* Watchdog abort at DMAEND */
+static const uint8_t dma_nost[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV CCR SAF SS4 SB4 DAF DS4 DB4 */
+ 0x04, /* DMALD */
+ 0x18, /* DMANOP */
+ 0x00 /* DMAEND */
+};
+
+/* Watchdog abort at DMALD */
+static const uint8_t dma_ldfe[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV CCR SAF SS4 SB4 DAF DS4 DB4 */
+ /* DMALPFE */
+ 0x04, /* DMALD */
+ 0x28, 0x01, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+/* Watchdog abort at DMAST */
+static const uint8_t dma_stfe[] = {
+ 0xBC, 0x00, 0x00, 0x00, 0x01, 0x20, /* DMAMOV SAR 0x20010000 */
+ 0xBC, 0x02, 0x00, 0x00, 0x02, 0x20, /* DMAMOV DAR 0x20020000 */
+ 0xBC, 0x01, 0x34, 0x00, 0x0D, 0x00, /* DMAMOV CCR SAF SS4 SB4 DAF DS4 DB4 */
+ /* DMALPFE */
+ 0x08, /* DMAST */
+ 0x28, 0x01, /* DMALPEND */
+ 0x00 /* DMAEND */
+};
+
+
+static inline void dma_exec_dbg(const uint8_t *prog, int size)
+{
+ cpu_physical_memory_write(PROG_ADDR, prog, size);
+ cpu_physical_memory_write(DBG0_ADDR, (uint8_t *)&dbg0, 4);
+ cpu_physical_memory_write(DBG1_ADDR, (uint8_t *)&dbg1, 4);
+ cpu_physical_memory_write(DBGGO_ADDR, (uint8_t *)&dbggo, 4);
+}
+
+static inline void dma_kill_dbg(void)
+{
+ uint32_t zeroval = 0x0;
+
+ cpu_physical_memory_write(DBG0_ADDR, (uint8_t *)&dbgkill, 4);
+ cpu_physical_memory_write(DBG1_ADDR, (uint8_t *)&zeroval, 4);
+ cpu_physical_memory_write(DBGGO_ADDR, (uint8_t *)&dbggo, 4);
+}
+
+static __attribute__((unused)) void test_dma_script(struct s5pc1xx_state_s *s)
+{
+ uint8_t res[32];
+ uint32_t reg;
+ int outcome;
+ int i;
+
+ cpu_physical_memory_write(DATA_ADDR, dma_data, 32);
+
+ /* TEST 1 */
+ cpu_physical_memory_write(RESULT_ADDR, dma_data, 32);
+ dma_exec_dbg(dma_stz, 20);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 16);
+ for (i = 0; i < 16; i++) {
+ if (res[i] != 0) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 1: DMASTZ. FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 1: DMASTZ. OK\n");
+ }
+
+ /* TEST 2 */
+ cpu_physical_memory_write(RESULT_ADDR, dma_data, 32);
+ dma_exec_dbg(dma_stzlp, 24);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 32);
+ for (i = 0; i < 32; i++) {
+ if (res[i] != 0) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 2: DMASTZ in loop. FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 2: DMASTZ in loop. OK\n");
+ }
+
+ /* TEST 3 */
+ dma_exec_dbg(dma_stzlp, 24);
+ dma_exec_dbg(dma_copy, 25);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 32);
+ for (i = 0; i < 32; i++) {
+ if (res[i] != dma_data[i]) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 3: DMA copy of 32 bytes of data. FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 3: DMA copy of 32 bytes of data. OK\n");
+ }
+
+ /* TEST 4 */
+ dma_exec_dbg(dma_stzlp, 24);
+ dma_exec_dbg(dma_copy_2, 25);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 32);
+ for (i = 0; i < 32; i++) {
+ if (res[i] != dma_data[i]) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 4: DMA copy of 32 bytes of data (store before load). FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 4: DMA copy of 32 bytes of data (store before load). OK\n");
+ }
+
+ /* TEST 5 */
+ dma_exec_dbg(dma_stzlp, 24);
+ dma_exec_dbg(dma_scatter, 25);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 32);
+ for (i = 0; i < 32; i++) {
+ if (res[i] != dma_data[i % 4]) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 5: DMA scatter. FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 5: DMA scatter. OK\n");
+ }
+
+ /* TEST 6 */
+ dma_exec_dbg(dma_stzlp, 24);
+ dma_exec_dbg(dma_gather, 25);
+ outcome = 0;
+ cpu_physical_memory_read(RESULT_ADDR, res, 32);
+ for (i = 0; i < 32; i++) {
+ if (res[i] != ((i > 3) ? 0 : dma_data[28 + i])) {
+ outcome = 1;
+ }
+ }
+ if (outcome) {
+ fprintf(stderr, "DMA test 6: DMA gather. FAILED\n");
+ } else {
+ fprintf(stderr, "DMA test 6: DMA gather. OK\n");
+ }
+
+ /* TEST 7 */
+ dma_exec_dbg(dma_stzlp, 24);
+ dma_exec_dbg(dma_nost, 21);
+ outcome = 0;
+ cpu_physical_memory_read(FSC_ADDR, (uint8_t *)®, 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);
+}
+
--- /dev/null
+/*
+ * GPIO controller for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define GPIO_CONF_CASE 6
+#define GPIO_PIN_CONF(s, group, pin) (((s)->con[(group)] >> ((pin) * 4)) & 0xF)
+#define S5PC1XX_GPIO_REG_MEM_SIZE 0xF84
+
+
+typedef struct S5pc1xxGPIOState {
+ SysBusDevice busdev;
+
+ /* Port Registers */
+ uint32_t con[GPH3 + 1];
+
+ /* Interrupt GPIO Registers
+ * GPI has no interrupts so corresponding field is not used */
+ uint32_t int_con[GPJ4 + 1];
+ uint32_t int_fltcon[GPJ4 + 1][2];
+ uint32_t int_mask[GPJ4 + 1];
+ uint32_t int_pend[GPJ4 + 1];
+ uint32_t int_fixpri[GPJ4 + 1];
+
+ uint32_t int_grppri;
+ uint32_t int_priority;
+ uint32_t int_ser;
+ uint32_t int_ser_pend;
+ uint32_t int_grpfixpri;
+
+ /* Extended Interrupt Registers */
+ uint32_t ext_int_mask[4];
+ uint32_t ext_int_pend[4];
+
+ /* Parent Interrupt for GPIO interrupts */
+ qemu_irq gpioint;
+ /* Parent Extended Interrupt */
+ qemu_irq extend;
+} S5pc1xxGPIOState;
+
+
+int s5pc1xx_gpio_case[GPH3 + 1][8][7] = {
+
+/* GPA0 */ {{GPIO_UART_RXD(0)}, {GPIO_UART_TXD(0)}, {GPIO_UART_CTS(0)}, {GPIO_UART_RTS(0)},
+ {GPIO_UART_RXD(1)}, {GPIO_UART_TXD(1)}, {GPIO_UART_CTS(1)}, {GPIO_UART_RTS(1)}},
+
+/* GPA1 */ {{GPIO_UART_RXD(2), 0, GPIO_UART_AUDIO_RXD}, {GPIO_UART_TXD(2), 0, GPIO_UART_AUDIO_TXD},
+ {GPIO_UART_RXD(3), 0, GPIO_UART_CTS(2)}, {GPIO_UART_TXD(3), 0, GPIO_UART_RTS(2)}},
+/* not
+ * covered */ {},
+
+/* GPC0 */ {{0, PCM_SCLK(1), GPIO_AC97BITCLK}, {0, PCM_EXTCLK(1), GPIO_AC97RESETn}, {0, PCM_FSYNC(1), GPIO_AC97SYNC},
+ {0, PCM_SIN(1), GPIO_AC97SDI}, {0, PCM_SOUT(1), GPIO_AC97SDO}},
+
+/* GPC1 */ {{PCM_SCLK(0), SPDIF_0_OUT}, {PCM_EXTCLK(0), SPDIF_EXTCLK},
+ {PCM_FSYNC(0), LCD_FRM}, {PCM_SIN(0)}, {PCM_SOUT(0)}},
+
+/* GPD0 */ {{GPIO_PWM_TOUT(0)}, {GPIO_PWM_TOUT(1)}, {GPIO_PWM_TOUT(2)}, {GPIO_PWM_TOUT(3), PWM_MIE_MDNI}},
+
+/* not
+ * covered */ {}, {}, {},
+
+/* GPF0 */ {{LCD_HSYNC}, {LCD_VSYNC}, {LCD_VDEN}, {LCD_VCLK},
+ {LCD_VD(0)}, {LCD_VD(1)}, {LCD_VD(2)}, {LCD_VD(3)}},
+
+/* GPF1 */ {{LCD_VD(4)}, {LCD_VD(5)}, {LCD_VD(6)}, {LCD_VD(7)},
+ {LCD_VD(8)}, {LCD_VD(9)}, {LCD_VD(10)}, {LCD_VD(11)}},
+
+/* GPF2 */ {{LCD_VD(12)}, {LCD_VD(13)}, {LCD_VD(14)}, {LCD_VD(15)},
+ {LCD_VD(16)}, {LCD_VD(17)}, {LCD_VD(18)}, {LCD_VD(19)}},
+
+/* GPF3 */ {{LCD_VD(20)}, {LCD_VD(21)}, {LCD_VD(22)}, {LCD_VD(23)}},
+
+/* not
+ * covered */ {}, {}, {}, {},
+
+/* GPI */ {{0, PCM_SCLK(2)}, {0, PCM_EXTCLK(2)}, {0, PCM_FSYNC(2)}, {0, PCM_SIN(2)}, {0, PCM_SOUT(2)}},
+
+/* not
+ * covered */ {},
+
+/* GPJ1 */ {{}, {}, {}, {}, {0, GPIO_KP_COL(0)}},
+
+/* GPJ2 */ {{0, GPIO_KP_COL(1)}, {0, GPIO_KP_COL(2)}, {0, GPIO_KP_COL(3)}, {0, GPIO_KP_COL(4)},
+ {0, GPIO_KP_COL(5)}, {0, GPIO_KP_COL(6)}, {0, GPIO_KP_COL(7)}, {0, GPIO_KP_ROW(0)}},
+
+/* GPJ3 */ {{0, GPIO_KP_ROW(1), 0, 0, GPIO_I2C_SDA(10), 0, GPIO_I2C_SDA(10)},
+ {0, GPIO_KP_ROW(2), 0, 0, GPIO_I2C_SCL(10), 0, GPIO_I2C_SCL(10)},
+ {0, GPIO_KP_ROW(3)},
+ {0, GPIO_KP_ROW(4)}, {0, GPIO_KP_ROW(5)}, {0, GPIO_KP_ROW(6)},
+ {0, GPIO_KP_ROW(7), 0, 0, GPIO_I2C_SDA(3), 0, GPIO_I2C_SDA(3)},
+ {0, GPIO_KP_ROW(8), 0, 0, GPIO_I2C_SCL(3), 0, GPIO_I2C_SCL(3)}},
+
+/* GPJ4 */ {{0, GPIO_KP_ROW(9), 0, 0, GPIO_I2C_SDA(4), 0, GPIO_I2C_SDA(4)},
+ {0, GPIO_KP_ROW(10)}, {0, GPIO_KP_ROW(11)},
+ {0, GPIO_KP_ROW(12), 0, 0, GPIO_I2C_SCL(4), 0, GPIO_I2C_SCL(4)},
+ {0, GPIO_KP_ROW(13)}},
+/* not
+ * covered */ {}, {}, {}, {},
+
+/* MP0_5 */ {{}, {}, {0, 0, 0, 0, GPIO_I2C_SCL(5), 0, GPIO_I2C_SCL(5)}, {0, 0, 0, 0, GPIO_I2C_SDA(5), 0, GPIO_I2C_SDA(5)},
+ {}, {}, {}, {}},
+/* not
+* covered */ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+
+/* GPH2 */ {{0, GPIO_KP_COL(0)}, {0, GPIO_KP_COL(1)}, {0, GPIO_KP_COL(2)}, {0, GPIO_KP_COL(3)},
+ {0, GPIO_KP_COL(4)}, {0, GPIO_KP_COL(5)}, {0, GPIO_KP_COL(6)}, {0, GPIO_KP_COL(7)}},
+
+/* GPH3 */ {{0, GPIO_KP_ROW(0)}, {0, GPIO_KP_ROW(1)}, {0, GPIO_KP_ROW(2)}, {0, GPIO_KP_ROW(3)},
+ {0, GPIO_KP_ROW(4)}, {0, GPIO_KP_ROW(5)}, {0, GPIO_KP_ROW(6)}, {0, GPIO_KP_ROW(7)}},
+};
+
+
+/* IO Memory Support */
+
+GPIOWriteMemoryFunc *gpio_io_mem_write[GPIO_IDX_MAX];
+GPIOReadMemoryFunc *gpio_io_mem_read[GPIO_IDX_MAX];
+GPIOWriteMemoryFunc *gpio_io_mem_conf[GPIO_IDX_MAX] = {NULL};
+void *gpio_io_mem_opaque[GPIO_IDX_MAX][8];
+
+
+void s5pc1xx_gpio_register_io_memory(int io_index, int instance,
+ GPIOReadMemoryFunc *mem_read,
+ GPIOWriteMemoryFunc *mem_write,
+ GPIOWriteMemoryFunc *mem_conf,
+ void *opaque)
+{
+ gpio_io_mem_read[io_index] = mem_read;
+ gpio_io_mem_write[io_index] = mem_write;
+ gpio_io_mem_conf[io_index] = mem_conf;
+ gpio_io_mem_opaque[io_index][instance] = opaque;
+}
+
+uint32_t s5pc1xx_empty_gpio_read(void *opaque, int io_index)
+{
+ return 0;
+}
+
+void s5pc1xx_empty_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+}
+
+/* Empty read and write functions references are used as plugs
+ * for the elements gpio_io_mem_read[0] and gpio_io_mem_write[0] */
+static GPIOReadMemoryFunc *s5pc1xx_empty_gpio_readfn = s5pc1xx_empty_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_empty_gpio_writefn = s5pc1xx_empty_gpio_write;
+
+
+/* GPIO General Logic */
+
+/* GPIO Reset Function */
+static void s5pc1xx_gpio_reset(void *opaque)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ unsigned int group;
+
+ /* Note: no groups from ETC2 (50) till GPH0 (96) */
+ for (group = GPA0;
+ group <= GPH3;
+ group = (group == ETC2) ? GPH0 : group + 1) {
+ s->con[group] = 0x0;
+ }
+
+ for (group = 0; group < 4; group++) {
+ s->ext_int_mask[group] = 0xFF;
+ s->ext_int_pend[group] = 0x0;
+ }
+
+ for (group = GPA0; group <= GPJ4; group++) {
+ s->int_con[group] = 0x0;
+ s->int_fltcon[group][0] = 0x0;
+ s->int_fltcon[group][1] = 0x0;
+ s->int_mask[group] = 0xFF; /* other values in documentation */
+ s->int_pend[group] = 0x0;
+ s->int_fixpri[group] = 0x0;
+ }
+
+ s->int_grppri = 0x0;
+ s->int_priority = 0x0;
+ s->int_ser = 0x0;
+ s->int_ser_pend = 0x0;
+ s->int_grpfixpri = 0x0;
+}
+
+static void s5pc1xx_gpio_irq_lower(S5pc1xxGPIOState *s,
+ unsigned int group, uint32_t pinmask)
+{
+ unsigned int i = 0;
+ s->int_pend[group] &= ~pinmask;
+ for (i = GPA0; i <= GPJ4; i++) {
+ if (s->int_pend[i]) {
+ return;
+ }
+ }
+
+ qemu_irq_lower(s->gpioint);
+}
+
+static void s5pc1xx_gpio_extended_irq_lower(S5pc1xxGPIOState *s,
+ unsigned int group, uint32_t pinmask)
+{
+ unsigned int i = 0;
+ s->ext_int_pend[group] &= ~pinmask;
+ for (i = 0; i < 4; i++)
+ if (s->ext_int_pend[i])
+ return;
+
+ qemu_irq_lower(s->extend);
+}
+
+static void s5pc1xx_gpio_irq_handler(void *opaque, int irq, int level)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ unsigned int group = GPIOINT_GROUP(irq);
+ unsigned int pin = GPIOINT_PIN (irq);
+
+ /* Special case of extended IRQs */
+ if (irq < IRQ_EXTEND_NUM) {
+ group = irq >> 3;
+ pin = irq & 0x7;
+
+ /* FIXME: IRQs 0~15 are not supported */
+ if (irq < 16)
+ hw_error("s5pc1xx.gpio: "
+ "extended IRQs 0-15 through GPIO are not supported");
+
+ if (s->ext_int_mask[group] & (1 << pin))
+ return;
+
+ if (s->ext_int_pend[group] & (1 << pin))
+ return;
+
+ if (level) {
+ s->ext_int_pend[group] |= (1 << pin);
+ qemu_irq_raise(s->extend);
+ } else {
+ s5pc1xx_gpio_extended_irq_lower(s, group, (1 << pin));
+ }
+
+ return;
+ }
+
+ if (GPIO_PIN_CONF(s, group, pin) != GIPIO_CONF_INT)
+ return;
+
+ if (s->int_mask[group] & (1 << pin))
+ return;
+
+ /* if the interrupt has already happened */
+ if (s->int_pend[group] & (1 << pin))
+ return;
+
+ if (level) {
+ s->int_pend[group] |= (1 << pin);
+ qemu_irq_raise(s->gpioint);
+ } else {
+ s5pc1xx_gpio_irq_lower(s, group, (1 << pin));
+ }
+}
+
+/* GPIO Read Function */
+static uint32_t s5pc1xx_gpio_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ uint32_t value = 0, ret_val;
+ int index, device, instance, io_index;
+ unsigned int group, pin, n;
+ int gpio_case;
+
+ group = offset / STEP;
+
+ if ((group <= ETC2) || ((group >= GPH0) && (group <= GPH3))) {
+
+ if (offset % STEP == 0x00)
+ return s->con[group];
+
+ if (offset % STEP == 0x04) {
+ ret_val = 0;
+
+ for (pin = 0; pin < 8; pin++) {
+ index = (s->con[group] >> pin * 4) & 0xF;
+ switch (index) {
+ case 0x1:
+ /* Input port can't be read */
+ break;
+ case 0x0:
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ gpio_case = s5pc1xx_gpio_case[group][pin][CASE(index)];
+ if (gpio_case) {
+ device = gpio_case >> 11 & 0x1F;
+ instance = gpio_case >> 6 & 0x1F;
+ io_index = gpio_case >> 0 & 0x3F;
+
+ if (!(gpio_io_mem_read[device]))
+ device = 0;
+
+ value = (gpio_io_mem_read[device])
+ (gpio_io_mem_opaque[device][instance],
+ io_index);
+ }
+ break;
+ case 0xF:
+ /* TODO: implement this case */
+ break;
+ default:
+ hw_error("s5pc1xx.gpio: "
+ "bad pin configuration index 0x%X\n", index);
+ }
+ ret_val |= (value & 0x1) << pin;
+ }
+ return ret_val;
+ }
+ }
+
+ if (offset >= INT_CON_BASE && offset < INT_CON_BASE + INT_REGS_SIZE)
+ return s->int_con[GET_GROUP_INT_CON(offset)];
+
+ if (offset >= INT_FLTCON_BASE &&
+ offset < INT_FLTCON_BASE + INT_REGS_SIZE * 2)
+ return s->int_fltcon[GET_GROUP_INT_FLTCON(offset)]
+ [(offset & 0x4) >> 2];
+
+ if (offset >= INT_MASK_BASE && offset < INT_MASK_BASE + INT_REGS_SIZE)
+ return s->int_mask[GET_GROUP_INT_MASK(offset)];
+
+ if (offset >= INT_PEND_BASE && offset < INT_PEND_BASE + INT_REGS_SIZE)
+ return s->int_pend[GET_GROUP_INT_PEND(offset)];
+
+ if (offset >= INT_FIXPRI_BASE && offset < INT_FIXPRI_BASE + INT_REGS_SIZE)
+ return s->int_fixpri[GET_GROUP_INT_FIXPRI(offset)];
+
+ switch (offset) {
+ case GPIO_INT_GRPPRI:
+ return s->int_grppri;
+ case GPIO_INT_PRIORITY:
+ return s->int_priority;
+ case GPIO_INT_SER:
+ return s->int_ser;
+ case GPIO_INT_SER_PEND:
+ return s->int_ser_pend;
+ case GPIO_INT_GRPFIXPRI:
+ return s->int_grpfixpri;
+ }
+
+ for (n = 0; n < 4; n++) {
+ /* EINT Mask Register */
+ if (offset == EXT_INT_MASK(n))
+ return s->ext_int_mask[n];
+ /* EINT Pend Register */
+ if (offset == EXT_INT_PEND(n))
+ return s->ext_int_pend[n];
+ }
+
+ return 0;
+}
+
+/* GPIO Write Function */
+static void s5pc1xx_gpio_write(void *opaque,
+ target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ int index, device, instance, io_index;
+ unsigned int group, pin, n;
+ int gpio_case;
+
+ group = offset / STEP;
+
+ if ((group <= ETC2) || ((group >= GPH0) && (group <= GPH3))) {
+
+ if (offset % STEP == 0x00) {
+ for (pin = 0; pin < 8; pin++) {
+ int new_con = (value >> pin * 4) & 0xF;
+
+ gpio_case = s5pc1xx_gpio_case[group][pin][GPIO_CONF_CASE];
+ if (gpio_case) {
+ device = gpio_case >> 11 & 0x1F;
+ instance = gpio_case >> 6 & 0x1F;
+ io_index = gpio_case >> 0 & 0x3F;
+
+ if (!(gpio_io_mem_conf[device]))
+ device = 0;
+
+ gpio_io_mem_conf[device](gpio_io_mem_opaque[device][instance],
+ io_index, new_con);
+ }
+ }
+ s->con[group] = value;
+ return;
+ }
+
+ if (offset % STEP == 0x04) {
+ for (pin = 0; pin < 8; pin++) {
+ index = (s->con[group] >> pin * 4) & 0xF;
+ switch (index) {
+ case 0x0:
+ /* Output port can't be written */
+ break;
+ case 0x1:
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ gpio_case = s5pc1xx_gpio_case[group][pin][CASE(index)];
+ if (gpio_case) {
+ device = gpio_case >> 11 & 0x1F;
+ instance = gpio_case >> 6 & 0x1F;
+ io_index = gpio_case >> 0 & 0x3F;
+
+ if (!(gpio_io_mem_write[device]))
+ device = 0;
+
+ gpio_io_mem_write[device]
+ (gpio_io_mem_opaque[device][instance],
+ io_index, (value >> pin) & 0x1);
+ }
+ break;
+ case 0xF:
+ /* TODO: implement this case */
+ break;
+ default:
+ hw_error("s5pc1xx.gpio: "
+ "bad pin configuration index 0x%X\n", index);
+ }
+ }
+ return;
+ }
+ }
+
+ if (offset >= INT_CON_BASE && offset < INT_CON_BASE + INT_REGS_SIZE) {
+ s->int_con[GET_GROUP_INT_CON(offset)] = value;
+ return;
+ }
+
+ if (offset >= INT_FLTCON_BASE &&
+ offset < INT_FLTCON_BASE + INT_REGS_SIZE * 2) {
+ s->int_fltcon[GET_GROUP_INT_FLTCON(offset)][(offset & 0x4) >> 2] =
+ value;
+ return;
+ }
+
+ if (offset >= INT_MASK_BASE && offset < INT_MASK_BASE + INT_REGS_SIZE) {
+ s->int_mask[GET_GROUP_INT_MASK(offset)] = value;
+ return;
+ }
+
+ if (offset >= INT_PEND_BASE && offset < INT_PEND_BASE + INT_REGS_SIZE) {
+ group = GET_GROUP_INT_PEND(offset);
+ s5pc1xx_gpio_irq_lower(s, group, value);
+ return;
+ }
+
+ if (offset >= INT_FIXPRI_BASE && offset < INT_FIXPRI_BASE + INT_REGS_SIZE) {
+ s->int_fixpri[GET_GROUP_INT_FIXPRI(offset)] = value;
+ return;
+ }
+
+ switch (offset) {
+ case GPIO_INT_GRPPRI:
+ s->int_grppri = value;
+ return;
+ case GPIO_INT_PRIORITY:
+ s->int_priority = value;
+ return;
+ case GPIO_INT_SER:
+ s->int_ser = value;
+ return;
+ case GPIO_INT_SER_PEND:
+ s->int_ser_pend = value;
+ return;
+ case GPIO_INT_GRPFIXPRI:
+ s->int_grpfixpri = value;
+ return;
+ }
+
+ for (n = 0; n < 4; n++) {
+ /* EINT Mask Register */
+ if (offset == EXT_INT_MASK(n)) {
+ s->ext_int_mask[n] = value;
+ return;
+ }
+ /* EINT Pend Register */
+ if (offset == EXT_INT_PEND(n)) {
+ s5pc1xx_gpio_extended_irq_lower(s, group, value);
+ return;
+ }
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_gpio_readfn[] = {
+ s5pc1xx_gpio_read,
+ s5pc1xx_gpio_read,
+ s5pc1xx_gpio_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_gpio_writefn[] = {
+ s5pc1xx_gpio_write,
+ s5pc1xx_gpio_write,
+ s5pc1xx_gpio_write
+};
+
+static void s5pc1xx_gpio_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ int i;
+
+ for (i = GPA0; i <= GPH3; i = (i == ETC2) ? GPH0 : i + 1) {
+ qemu_put_be32s(f, s->con + i);
+ if (i <= GPJ4) {
+ qemu_put_be32s(f, s->int_con + i);
+ qemu_put_be32s(f, &s->int_fltcon[i][0]);
+ qemu_put_be32s(f, &s->int_fltcon[i][1]);
+
+ qemu_put_be32s(f, s->int_mask + i);
+ qemu_put_be32s(f, s->int_pend + i);
+ qemu_put_be32s(f, s->int_fixpri + i);
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ qemu_put_be32s(f, s->ext_int_mask + i);
+ qemu_put_be32s(f, s->ext_int_pend + i);
+ }
+
+ qemu_put_be32s(f, &s->int_grppri);
+ qemu_put_be32s(f, &s->int_priority);
+ qemu_put_be32s(f, &s->int_ser);
+ qemu_put_be32s(f, &s->int_ser_pend);
+ qemu_put_be32s(f, &s->int_grpfixpri);
+}
+
+static int s5pc1xx_gpio_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxGPIOState *s = (S5pc1xxGPIOState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ for (i = GPA0; i <= GPH3; i = (i == ETC2) ? GPH0 : i + 1) {
+ qemu_get_be32s(f, s->con + i);
+ if (i <= GPJ4) {
+ qemu_get_be32s(f, s->int_con + i);
+ qemu_get_be32s(f, &s->int_fltcon[i][0]);
+ qemu_get_be32s(f, &s->int_fltcon[i][1]);
+
+ qemu_get_be32s(f, s->int_mask + i);
+ qemu_get_be32s(f, s->int_pend + i);
+ qemu_get_be32s(f, s->int_fixpri + i);
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ qemu_get_be32s(f, s->ext_int_mask + i);
+ qemu_get_be32s(f, s->ext_int_pend + i);
+ }
+
+ qemu_get_be32s(f, &s->int_grppri);
+ qemu_get_be32s(f, &s->int_priority);
+ qemu_get_be32s(f, &s->int_ser);
+ qemu_get_be32s(f, &s->int_ser_pend);
+ qemu_get_be32s(f, &s->int_grpfixpri);
+
+ return 0;
+}
+
+qemu_irq s5pc1xx_gpoint_irq(DeviceState *d, int irq_num)
+{
+ return qdev_get_gpio_in(d, irq_num);
+}
+
+/* GPIO Init Function */
+static int s5pc1xx_gpio_init(SysBusDevice *dev)
+{
+ S5pc1xxGPIOState *s = FROM_SYSBUS(S5pc1xxGPIOState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->gpioint);
+ sysbus_init_irq(dev, &s->extend);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_gpio_readfn, s5pc1xx_gpio_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_GPIO_REG_MEM_SIZE, iomemtype);
+
+ /* First IRQ_EXTEND_NUM interrupts are for extended interrupts */
+ qdev_init_gpio_in(&dev->qdev, s5pc1xx_gpio_irq_handler,
+ IRQ_EXTEND_NUM + MAX_PIN_IN_GROUP * (GPJ4 + 1));
+
+ s5pc1xx_gpio_register_io_memory(0, 0, s5pc1xx_empty_gpio_readfn,
+ s5pc1xx_empty_gpio_writefn,
+ s5pc1xx_empty_gpio_writefn, NULL);
+ s5pc1xx_gpio_reset(s);
+ qemu_register_reset(s5pc1xx_gpio_reset, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.gpio", -1, 1,
+ s5pc1xx_gpio_save, s5pc1xx_gpio_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_gpio_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.gpio", sizeof(S5pc1xxGPIOState),
+ s5pc1xx_gpio_init);
+}
+
+device_init(s5pc1xx_gpio_register_devices)
--- /dev/null
+/*
+ * S5C GPIO Controller Constants
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+
+/* Registers */
+
+#define STEP 0x20
+
+#define CON(group) (0x000 + STEP * group) /* R/W Configuration Register */
+#define DAT(group) (0x004 + STEP * group) /* R/W Data Register */
+#define PUD(group) (0x008 + STEP * group) /* R/W Pull-up/down Register */
+#define DRV(group) (0x00C + STEP * group) /* R/W Drive Strength Control Register */
+#define CONPDN(group) (0x010 + STEP * group) /* R/W Power Down Mode Configuration Register */
+#define PUDPDN(group) (0x014 + STEP * group) /* R/W Power Down Mode Pullup/down Register */
+
+/* General PIN configurations */
+#define GIPIO_CONF_INPUT 0x0
+#define GIPIO_CONF_OUTPUT 0x1
+#define GIPIO_CONF_INT 0xF
+
+/* Warning: the group GPI is absent in the registers below */
+
+/* inter - group without GPI */
+#define GROUP_TO_INT_NUM(group) ((group) - ((group) < 17 ? 0:1))
+#define INT_TO_GROUP_NUM(inter) ((inter) + ((inter) < 17 ? 0:1))
+
+#define INT_REGS_SIZE 0x58
+
+#define INT_CON_BASE 0x700
+/* FLTCON has double set of regesters. So its' size is 2*INT_REGS_SIZE */
+#define INT_FLTCON_BASE 0x800
+
+#define INT_MASK_BASE 0x900
+
+#define INT_PEND_BASE 0xA00
+#define INT_FIXPRI_BASE 0xB14
+
+/* R/W GPIO Interrupt Configuration Register */
+#define INT_CON(group) (INT_CON_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_CON(addr) INT_TO_GROUP_NUM(((addr) - INT_CON_BASE) / 4)
+
+/* R/W GPIO Interrupt Filter Configuration Register 0 and 1 */
+#define INT_FLTCON(group) (INT_FLTCON_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_FLTCON(addr) INT_TO_GROUP_NUM(((addr) - INT_FLTCON_BASE) / 8)
+
+
+/* R/W GPIO Interrupt Mask Register */
+#define INT_MASK(group) (INT_MASK_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_MASK(addr) INT_TO_GROUP_NUM(((addr) - INT_MASK_BASE) / 4)
+
+/* R/W GPIO Interrupt Pending Register */
+#define INT_PEND(group) (INT_PEND_BASE + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_PEND(addr) INT_TO_GROUP_NUM(((addr) - INT_PEND_BASE) / 4)
+
+
+#define GPIO_INT_GRPPRI 0xB00 /* R/W GPIO Interrupt Group Priority Control Register 0x0 */
+#define GPIO_INT_PRIORITY 0xB04 /* R/W GPIO Interrupt Priority Control Register 0x00 */
+#define GPIO_INT_SER 0xB08 /* R Current Service Register 0x00 */
+#define GPIO_INT_SER_PEND 0xB0C /* R Current Service Pending Register 0x00 */
+#define GPIO_INT_GRPFIXPRI 0xB10 /* R/W GPIO Interrupt Group Fixed Priority Control Register 0x00 */
+
+/* R/W GPIO Interrupt Fixed Priority Control Register */
+#define INT_FIXPRI(group) (INT_PEND_FIXPRI + 4 * GROUP_TO_INT_NUM(group))
+#define GET_GROUP_INT_FIXPRI(addr) INT_TO_GROUP_NUM(((addr)-INT_FIXPRI_BASE) / 4)
+
+/* R/W External Interrupt Configuration Register */
+#define EXT_INT_CON(n) (0xE00 + 0x04 * n)
+
+/* R/W External Interrupt Filter Configuration Register 0 */
+#define EXT_INT_FLTCON0(n) (0xE80 + 0x08 * n)
+
+/* R/W External Interrupt Filter Configuration Register 1 */
+#define EXT_INT_FLTCON1(n) (0xE84 + 0x08 * n)
+
+/* R/W External Interrupt Mask Register */
+#define EXT_INT_MASK(n) (0xF00 + 0x04 * n)
+
+/* R/W External Interrupt Pending Register */
+#define EXT_INT_PEND(n) (0xF40 + 0x04 * n)
+
+/* R/W Power down mode Pad Configure Register */
+#define PDNEN 0xF80
+
+
+/* Groups */
+
+#define GPA0 0
+#define GPA1 1
+#define GPB 2
+#define GPC0 3
+#define GPC1 4
+#define GPD0 5
+#define GPD1 6
+#define GPE0 7
+#define GPE1 8
+#define GPF0 9
+#define GPF1 10
+#define GPF2 11
+#define GPF3 12
+#define GPG0 13
+#define GPG1 14
+#define GPG2 15
+#define GPG3 16
+#define GPI 17
+#define GPJ0 18
+#define GPJ1 19
+#define GPJ2 20
+#define GPJ3 21
+#define GPJ4 22
+
+#define MP0_1 23
+#define MP0_2 24
+#define MP0_3 25
+#define MP0_4 26
+#define MP0_5 27
+#define MP0_6 28
+#define MP0_7 29
+
+#define MP1_0 30
+#define MP1_1 31
+#define MP1_2 32
+#define MP1_3 33
+#define MP1_4 34
+#define MP1_5 35
+#define MP1_6 36
+#define MP1_7 37
+#define MP1_8 38
+
+#define MP2_0 39
+#define MP2_1 40
+#define MP2_2 41
+#define MP2_3 42
+#define MP2_4 43
+#define MP2_5 44
+#define MP2_6 45
+#define MP2_7 46
+#define MP2_8 47
+
+#define ETC0 48
+#define ETC1 49
+#define ETC2 50
+
+#define GPH0 96
+#define GPH1 97
+#define GPH2 98
+#define GPH3 99
+
+
+#define MAX_PIN_IN_GROUP 8
+#define IRQ_EXTEND_NUM 32
+
+/* GPIOINT IRQs */
+#define GPIOEXT_IRQ(irq) (irq % 32)
+#define GPIOINT_IRQ(group, pin) \
+ ((group) * MAX_PIN_IN_GROUP + (pin) + IRQ_EXTEND_NUM)
+#define GPIOINT_GROUP(irq) ((irq - IRQ_EXTEND_NUM) / MAX_PIN_IN_GROUP)
+#define GPIOINT_PIN(irq) ((irq - IRQ_EXTEND_NUM) % MAX_PIN_IN_GROUP)
+
+
+/* GPIO device indexes and io_cases */
+
+/* CASE_ID has the structure: device_num [15:11];
+ * instance_num [10:6];
+ * event_num [5:0]
+ */
+#define CASE_ID(device, instance, event) ((device << 11) | \
+ (instance << 6) | \
+ (event << 0))
+
+#define GPIO_IDX_PWM 1
+ #define GPIO_PWM_TOUT(n) CASE_ID(GPIO_IDX_PWM, 0, n) /* n = 0~3 */
+ #define PWM_MIE_MDNI CASE_ID(GPIO_IDX_PWM, 0, 4)
+
+#define GPIO_IDX_UART 2
+ #define GPIO_UART_RXD(n) CASE_ID(GPIO_IDX_UART, n, 1) /* n = 0~3 */
+ #define GPIO_UART_TXD(n) CASE_ID(GPIO_IDX_UART, n, 2) /* n = 0~3 */
+ #define GPIO_UART_CTS(n) CASE_ID(GPIO_IDX_UART, n, 3) /* n = 0~2 */
+ #define GPIO_UART_RTS(n) CASE_ID(GPIO_IDX_UART, n, 4) /* n = 0~2 */
+ #define GPIO_UART_AUDIO_RXD CASE_ID(GPIO_IDX_UART, 0, 5)
+ #define GPIO_UART_AUDIO_TXD CASE_ID(GPIO_IDX_UART, 0, 6)
+
+#define GPIO_IDX_KEYIF 3
+ #define GPIO_KP_COL(n) CASE_ID(GPIO_IDX_KEYIF, n, 1) /* n = 0~7 */
+ #define GPIO_KP_ROW(n) CASE_ID(GPIO_IDX_KEYIF, n, 2) /* n = 0~13 */
+
+#define GPIO_IDX_LCD 4
+ #define LCD_FRM CASE_ID(GPIO_IDX_LCD, 0, 1)
+ #define LCD_HSYNC CASE_ID(GPIO_IDX_LCD, 0, 2)
+ #define LCD_VSYNC CASE_ID(GPIO_IDX_LCD, 0, 3)
+ #define LCD_VDEN CASE_ID(GPIO_IDX_LCD, 0, 4)
+ #define LCD_VCLK CASE_ID(GPIO_IDX_LCD, 0, 5)
+ #define LCD_VD(n) CASE_ID(GPIO_IDX_LCD, n, 6) /* n = 0~23 */
+
+#define GPIO_IDX_I2C 5
+ #define GPIO_IDX_I2C_SCL 1
+ #define GPIO_IDX_I2C_SDA 2
+ #define GPIO_I2C_SDA(n) CASE_ID(GPIO_IDX_I2C, n, GPIO_IDX_I2C_SDA)
+ #define GPIO_I2C_SCL(n) CASE_ID(GPIO_IDX_I2C, n, GPIO_IDX_I2C_SCL)
+
+#define GPIO_IDX_AC97 6
+ #define GPIO_AC97BITCLK CASE_ID(GPIO_IDX_AC97, 0, 1)
+ #define GPIO_AC97RESETn CASE_ID(GPIO_IDX_AC97, 0, 2)
+ #define GPIO_AC97SYNC CASE_ID(GPIO_IDX_AC97, 0, 3)
+ #define GPIO_AC97SDI CASE_ID(GPIO_IDX_AC97, 0, 4)
+ #define GPIO_AC97SDO CASE_ID(GPIO_IDX_AC97, 0, 5)
+
+#define GPIO_IDX_PCM 7
+ #define PCM_SCLK(n) CASE_ID(GPIO_IDX_PCM, n, 1) /* n = 0~2 */
+ #define PCM_EXTCLK(n) CASE_ID(GPIO_IDX_PCM, n, 2) /* n = 0~2 */
+ #define PCM_FSYNC(n) CASE_ID(GPIO_IDX_PCM, n, 3) /* n = 0~2 */
+ #define PCM_SIN(n) CASE_ID(GPIO_IDX_PCM, n, 4) /* n = 0~2 */
+ #define PCM_SOUT(n) CASE_ID(GPIO_IDX_PCM, n, 5) /* n = 0~2 */
+
+#define GPIO_IDX_SPDIF 8
+ #define SPDIF_0_OUT CASE_ID(GPIO_IDX_SPDIF, 0, 1)
+ #define SPDIF_EXTCLK CASE_ID(GPIO_IDX_SPDIF, 0, 2)
+
+#define GPIO_IDX_MAX 9
+
+
+/* GPIO io_cases storage */
+
+/* Note: cases with indexes = 2~5 are stored in the array first */
+#define CASE(index) (index > 1 ? index - 2 : 4 + index)
--- /dev/null
+/*
+ * S5C HS-MMC Controller Constants
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on SMDK6400 MMC (hw/regs-hsmmc.h)
+ */
+
+#ifndef __ASM_ARCH_REGS_HSMMC_H
+#define __ASM_ARCH_REGS_HSMMC_H __FILE__
+
+/*
+ * HS MMC Interface
+ */
+
+#define S5C_HSMMC_REG(x) (x)
+
+/* R/W SDMA System Address register 0x0 */
+#define S5C_HSMMC_SYSAD 0x00
+
+/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */
+#define S5C_HSMMC_BLKSIZE 0x04
+#define S5C_HSMMC_MAKE_BLKSZ(dma, bblksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+/* R/W Blocks count for current transfer 0x0 */
+#define S5C_HSMMC_BLKCNT 0x06
+/* R/W Command Argument Register 0x0 */
+#define S5C_HSMMC_ARGUMENT 0x08
+
+/* R/W Transfer Mode Setting Register 0x0 */
+#define S5C_HSMMC_TRNMOD 0x0C
+#define S5C_HSMMC_TRNS_DMA 0x01
+#define S5C_HSMMC_TRNS_BLK_CNT_EN 0x02
+#define S5C_HSMMC_TRNS_ACMD12 0x04
+#define S5C_HSMMC_TRNS_READ 0x10
+#define S5C_HSMMC_TRNS_MULTI 0x20
+
+#define S5C_HSMMC_TRNS_BOOTCMD 0x1000
+#define S5C_HSMMC_TRNS_BOOTACK 0x2000
+
+/* R/W Command Register 0x0 */
+#define S5C_HSMMC_CMDREG 0x0E
+/* ROC Response Register 0 0x0 */
+#define S5C_HSMMC_RSPREG0 0x10
+/* ROC Response Register 1 0x0 */
+#define S5C_HSMMC_RSPREG1 0x14
+/* ROC Response Register 2 0x0 */
+#define S5C_HSMMC_RSPREG2 0x18
+/* ROC Response Register 3 0x0 */
+#define S5C_HSMMC_RSPREG3 0x1C
+#define S5C_HSMMC_CMD_RESP_MASK 0x03
+#define S5C_HSMMC_CMD_CRC 0x08
+#define S5C_HSMMC_CMD_INDEX 0x10
+#define S5C_HSMMC_CMD_DATA 0x20
+#define S5C_HSMMC_CMD_RESP_NONE 0x00
+#define S5C_HSMMC_CMD_RESP_LONG 0x01
+#define S5C_HSMMC_CMD_RESP_SHORT 0x02
+#define S5C_HSMMC_CMD_RESP_SHORT_BUSY 0x03
+#define S5C_HSMMC_MAKE_CMD(c, f) (((c & 0xFF) << 8) | (f & 0xFF))
+
+/* R/W Buffer Data Register 0x0 */
+#define S5C_HSMMC_BDATA 0x20
+/* R/ROC Present State Register 0x000A0000 */
+#define S5C_HSMMC_PRNSTS 0x24
+#define S5C_HSMMC_CMD_INHIBIT 0x00000001
+#define S5C_HSMMC_DATA_INHIBIT 0x00000002
+#define S5C_HSMMC_DOING_WRITE 0x00000100
+#define S5C_HSMMC_DOING_READ 0x00000200
+#define S5C_HSMMC_SPACE_AVAILABLE 0x00000400
+#define S5C_HSMMC_DATA_AVAILABLE 0x00000800
+#define S5C_HSMMC_CARD_PRESENT 0x00010000
+#define S5C_HSMMC_WRITE_PROTECT 0x00080000
+
+/* R/W Present State Register 0x0 */
+#define S5C_HSMMC_HOSTCTL 0x28
+#define S5C_HSMMC_CTRL_LED 0x01
+#define S5C_HSMMC_CTRL_4BITBUS 0x02
+#define S5C_HSMMC_CTRL_HIGHSPEED 0x04
+#define S5C_HSMMC_CTRL_1BIT 0x00
+#define S5C_HSMMC_CTRL_4BIT 0x02
+#define S5C_HSMMC_CTRL_8BIT 0x20
+#define S5C_HSMMC_CTRL_SDMA 0x00
+#define S5C_HSMMC_CTRL_ADMA2_32 0x10
+
+/* R/W Present State Register 0x0 */
+#define S5C_HSMMC_PWRCON 0x29
+#define S5C_HSMMC_POWER_OFF 0x00
+#define S5C_HSMMC_POWER_ON 0x01
+#define S5C_HSMMC_POWER_180 0x0A
+#define S5C_HSMMC_POWER_300 0x0C
+#define S5C_HSMMC_POWER_330 0x0E
+#define S5C_HSMMC_POWER_ON_ALL 0xFF
+
+/* R/W Block Gap Control Register 0x0 */
+#define S5C_HSMMC_BLKGAP 0x2A
+/* R/W Wakeup Control Register 0x0 */
+#define S5C_HSMMC_WAKCON 0x2B
+
+#define S5C_HSMMC_STAWAKEUP 0x8
+
+/* R/W Command Register 0x0 */
+#define S5C_HSMMC_CLKCON 0x2C
+#define S5C_HSMMC_DIVIDER_SHIFT 0x8
+#define S5C_HSMMC_CLOCK_EXT_STABLE 0x8
+#define S5C_HSMMC_CLOCK_CARD_EN 0x4
+#define S5C_HSMMC_CLOCK_INT_STABLE 0x2
+#define S5C_HSMMC_CLOCK_INT_EN 0x1
+
+/* R/W Timeout Control Register 0x0 */
+#define S5C_HSMMC_TIMEOUTCON 0x2E
+#define S5C_HSMMC_TIMEOUT_MAX 0x0E
+
+/* R/W Software Reset Register 0x0 */
+#define S5C_HSMMC_SWRST 0x2F
+#define S5C_HSMMC_RESET_ALL 0x01
+#define S5C_HSMMC_RESET_CMD 0x02
+#define S5C_HSMMC_RESET_DATA 0x04
+
+/* ROC/RW1C Normal Interrupt Status Register 0x0 */
+#define S5C_HSMMC_NORINTSTS 0x30
+#define S5C_HSMMC_NIS_ERR 0x00008000
+#define S5C_HSMMC_NIS_CMDCMP 0x00000001
+#define S5C_HSMMC_NIS_TRSCMP 0x00000002
+#define S5C_HSMMC_NIS_DMA 0x00000008
+#define S5C_HSMMC_NIS_INSERT 0x00000040
+#define S5C_HSMMC_NIS_REMOVE 0x00000080
+
+/* ROC/RW1C Error Interrupt Status Register 0x0 */
+#define S5C_HSMMC_ERRINTSTS 0x32
+#define S5C_HSMMC_EIS_CMDTIMEOUT 0x00000001
+#define S5C_HSMMC_EIS_CMDERR 0x0000000E
+#define S5C_HSMMC_EIS_DATATIMEOUT 0x00000010
+#define S5C_HSMMC_EIS_DATAERR 0x00000060
+#define S5C_HSMMC_EIS_CMD12ERR 0x00000100
+#define S5C_HSMMC_EIS_ADMAERR 0x00000200
+#define S5C_HSMMC_EIS_STABOOTACKERR 0x00000400
+
+/* R/W Normal Interrupt Status Enable Register 0x0 */
+#define S5C_HSMMC_NORINTSTSEN 0x34
+/* R/W Error Interrupt Status Enable Register 0x0 */
+#define S5C_HSMMC_ERRINTSTSEN 0x36
+#define S5C_HSMMC_ENSTABOOTACKERR 0x400
+
+/* R/W Normal Interrupt Signal Enable Register 0x0 */
+#define S5C_HSMMC_NORINTSIGEN 0x38
+#define S5C_HSMMC_INT_MASK_ALL 0x00
+#define S5C_HSMMC_INT_RESPONSE 0x00000001
+#define S5C_HSMMC_INT_DATA_END 0x00000002
+#define S5C_HSMMC_INT_DMA_END 0x00000008
+#define S5C_HSMMC_INT_SPACE_AVAIL 0x00000010
+#define S5C_HSMMC_INT_DATA_AVAIL 0x00000020
+#define S5C_HSMMC_INT_CARD_INSERT 0x00000040
+#define S5C_HSMMC_INT_CARD_REMOVE 0x00000080
+#define S5C_HSMMC_INT_CARD_CHANGE 0x000000C0
+#define S5C_HSMMC_INT_CARD_INT 0x00000100
+#define S5C_HSMMC_INT_TIMEOUT 0x00010000
+#define S5C_HSMMC_INT_CRC 0x00020000
+#define S5C_HSMMC_INT_END_BIT 0x00040000
+#define S5C_HSMMC_INT_INDEX 0x00080000
+#define S5C_HSMMC_INT_DATA_TIMEOUT 0x00100000
+#define S5C_HSMMC_INT_DATA_CRC 0x00200000
+#define S5C_HSMMC_INT_DATA_END_BIT 0x00400000
+#define S5C_HSMMC_INT_BUS_POWER 0x00800000
+#define S5C_HSMMC_INT_ACMD12ERR 0x01000000
+#define S5C_HSMMC_INT_ADMAERR 0x02000000
+
+#define S5C_HSMMC_INT_NORMAL_MASK 0x00007FFF
+#define S5C_HSMMC_INT_ERROR_MASK 0xFFFF8000
+
+#define S5C_HSMMC_INT_CMD_MASK (S5C_HSMMC_INT_RESPONSE | \
+ S5C_HSMMC_INT_TIMEOUT | \
+ S5C_HSMMC_INT_CRC | \
+ S5C_HSMMC_INT_END_BIT | \
+ S5C_HSMMC_INT_INDEX | \
+ S5C_HSMMC_NIS_ERR)
+#define S5C_HSMMC_INT_DATA_MASK (S5C_HSMMC_INT_DATA_END | \
+ S5C_HSMMC_INT_DMA_END | \
+ S5C_HSMMC_INT_DATA_AVAIL | \
+ S5C_HSMMC_INT_SPACE_AVAIL | \
+ S5C_HSMMC_INT_DATA_TIMEOUT | \
+ S5C_HSMMC_INT_DATA_CRC | \
+ S5C_HSMMC_INT_DATA_END_BIT)
+
+/* R/W Error Interrupt Signal Enable Register 0x0 */
+#define S5C_HSMMC_ERRINTSIGEN 0x3A
+#define S5C_HSMMC_ENSIGBOOTACKERR 0x400
+
+/* ROC Auto CMD12 error status register 0x0 */
+#define S5C_HSMMC_ACMD12ERRSTS 0x3C
+
+/* HWInit Capabilities Register 0x05E80080 */
+#define S5C_HSMMC_CAPAREG 0x40
+#define S5C_HSMMC_TIMEOUT_CLK_MASK 0x0000003F
+#define S5C_HSMMC_TIMEOUT_CLK_SHIFT 0x0
+#define S5C_HSMMC_TIMEOUT_CLK_UNIT 0x00000080
+#define S5C_HSMMC_CLOCK_BASE_MASK 0x00003F00
+#define S5C_HSMMC_CLOCK_BASE_SHIFT 0x8
+#define S5C_HSMMC_MAX_BLOCK_MASK 0x00030000
+#define S5C_HSMMC_MAX_BLOCK_SHIFT 0x10
+#define S5C_HSMMC_CAN_DO_DMA 0x00400000
+#define S5C_HSMMC_CAN_DO_ADMA2 0x00080000
+#define S5C_HSMMC_CAN_VDD_330 0x01000000
+#define S5C_HSMMC_CAN_VDD_300 0x02000000
+#define S5C_HSMMC_CAN_VDD_180 0x04000000
+
+/* HWInit Maximum Current Capabilities Register 0x0 */
+#define S5C_HSMMC_MAXCURR 0x48
+
+/* For ADMA2 */
+
+/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */
+#define S5C_HSMMC_FEAER 0x50
+/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */
+#define S5C_HSMMC_FEERR 0x52
+
+/* R/W ADMA Error Status Register 0x00 */
+#define S5C_HSMMC_ADMAERR 0x54
+#define S5C_HSMMC_ADMAERR_CONTINUE_REQUEST (1 << 9)
+#define S5C_HSMMC_ADMAERR_INTRRUPT_STATUS (1 << 8)
+#define S5C_HSMMC_ADMAERR_LENGTH_MISMATCH (1 << 2)
+#define S5C_HSMMC_ADMAERR_STATE_ST_STOP (0 << 0)
+#define S5C_HSMMC_ADMAERR_STATE_ST_FDS (1 << 0)
+#define S5C_HSMMC_ADMAERR_STATE_ST_TFR (3 << 0)
+
+/* R/W ADMA System Address Register 0x00 */
+#define S5C_HSMMC_ADMASYSADDR 0x58
+#define S5C_HSMMC_ADMA_ATTR_MSK 0x3F
+#define S5C_HSMMC_ADMA_ATTR_ACT_NOP (0 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_RSV (1 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_TRAN (2 << 4)
+#define S5C_HSMMC_ADMA_ATTR_ACT_LINK (3 << 4)
+#define S5C_HSMMC_ADMA_ATTR_INT (1 << 2)
+#define S5C_HSMMC_ADMA_ATTR_END (1 << 1)
+#define S5C_HSMMC_ADMA_ATTR_VALID (1 << 0)
+
+/* R/W Control register 2 0x0 */
+#define S5C_HSMMC_CONTROL2 0x80
+/* R/W FIFO Interrupt Control (Control Register 3) 0x7F5F3F1F */
+#define S5C_HSMMC_CONTROL3 0x84
+/* R/W Control register 4 0x0 */
+#define S5C_HSMMC_CONTROL4 0x8C
+
+
+/* Magic register which is used from kernel! */
+#define S5C_HSMMC_SLOT_INT_STATUS 0xFC
+
+
+/* HWInit Host Controller Version Register 0x0401 */
+#define S5C_HSMMC_HCVER 0xFE
+#define S5C_HSMMC_VENDOR_VER_MASK 0xFF00
+#define S5C_HSMMC_VENDOR_VER_SHIFT 0x8
+#define S5C_HSMMC_SPEC_VER_MASK 0x00FF
+#define S5C_HSMMC_SPEC_VER_SHIFT 0x0
+
+#define S5C_HSMMC_REG_SIZE 0x100
+
+#endif /* __ASM_ARCH_REGS_HSMMC_H */
--- /dev/null
+/*
+ * I2C controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ * Alexey Merkulov <steelart@ispras.ru>
+ *
+ * Based on SMDK6400 I2C (hw/smdk6400/smdk_i2c.c)
+ */
+
+#include "i2c.h"
+#include "sysbus.h"
+
+
+#define I2CCON 0x00 /* I2C Control register */
+#define I2CSTAT 0x04 /* I2C Status register */
+#define I2CADD 0x08 /* I2C Slave Address register */
+#define I2CDS 0x0c /* I2C Data Shift register */
+#define I2CLC 0x10 /* I2C Line Control register */
+
+#define SR_MODE 0x0 /* Slave Receive Mode */
+#define ST_MODE 0x1 /* Slave Transmit Mode */
+#define MR_MODE 0x2 /* Master Receive Mode */
+#define MT_MODE 0x3 /* Master Transmit Mode */
+
+
+#define S5PC1XX_IICCON_ACKEN (1<<7)
+#define S5PC1XX_IICCON_TXDIV_16 (0<<6)
+#define S5PC1XX_IICCON_TXDIV_512 (1<<6)
+#define S5PC1XX_IICCON_IRQEN (1<<5)
+#define S5PC1XX_IICCON_IRQPEND (1<<4)
+
+#define S5PC1XX_IICSTAT_START (1<<5)
+#define S5PC1XX_IICSTAT_BUSBUSY (1<<5)
+#define S5PC1XX_IICSTAT_TXRXEN (1<<4)
+#define S5PC1XX_IICSTAT_ARBITR (1<<3)
+#define S5PC1XX_IICSTAT_ASSLAVE (1<<2)
+#define S5PC1XX_IICSTAT_ADDR0 (1<<1)
+#define S5PC1XX_IICSTAT_LASTBIT (1<<0)
+
+#define S5PC1XX_I2C_REG_MEM_SIZE 0x1000
+
+
+/* I2C Interface */
+typedef struct S5pc1xxI2CState {
+ SysBusDevice busdev;
+
+ i2c_bus *bus;
+ qemu_irq irq;
+
+ uint8_t control;
+ uint8_t status;
+ uint8_t address;
+ uint8_t datashift;
+ uint8_t line_ctrl;
+
+ uint8_t ibmr;
+ uint8_t data;
+} S5pc1xxI2CState;
+
+
+static void s5pc1xx_i2c_update(S5pc1xxI2CState *s)
+{
+ uint16_t level;
+ level = (s->status & S5PC1XX_IICSTAT_START) &&
+ (s->control & S5PC1XX_IICCON_IRQEN);
+
+ if (s->control & S5PC1XX_IICCON_IRQPEND)
+ level = 0;
+ qemu_set_irq(s->irq, !!level);
+}
+
+static int s5pc1xx_i2c_receive(S5pc1xxI2CState *s)
+{
+ int r;
+
+ r = i2c_recv(s->bus);
+ s5pc1xx_i2c_update(s);
+ return r;
+}
+
+static int s5pc1xx_i2c_send(S5pc1xxI2CState *s, uint8_t data)
+{
+ if (!(s->status & S5PC1XX_IICSTAT_LASTBIT)) {
+ /*s->status |= 1 << 7;*/
+ s->data = data;
+ i2c_send(s->bus, s->data);
+ }
+ s5pc1xx_i2c_update(s);
+ return 1;
+}
+
+/* I2C read function */
+static uint32_t s5pc1xx_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+ switch (offset) {
+ case I2CCON:
+ return s->control;
+ case I2CSTAT:
+ return s->status;
+ case I2CADD:
+ return s->address;
+ case I2CDS:
+ s->data = s5pc1xx_i2c_receive(s);
+ return s->data;
+ case I2CLC:
+ return s->line_ctrl;
+ default:
+ hw_error("s5pc1xx.i2c: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+ return 0;
+}
+
+/* I2C write function */
+static void s5pc1xx_i2c_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+ int mode;
+
+ qemu_irq_lower(s->irq);
+
+ switch (offset) {
+ case I2CCON:
+ s->control = value & 0xff;
+
+ if (value & S5PC1XX_IICCON_IRQEN)
+ s5pc1xx_i2c_update(s);
+ break;
+
+ case I2CSTAT:
+ s->status = value & 0xff;
+ mode = (s->status >> 6) & 0x3;
+ if (value & S5PC1XX_IICSTAT_TXRXEN) {
+ /* IIC-bus data output enable/disable bit */
+ switch(mode) {
+ case SR_MODE:
+ s->data = s5pc1xx_i2c_receive(s);
+ break;
+ case ST_MODE:
+ s->data = s5pc1xx_i2c_receive(s);
+ break;
+ case MR_MODE:
+ if (value & (1 << 5)) {
+ /* START condition */
+ s->status &= ~S5PC1XX_IICSTAT_LASTBIT;
+
+ i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+ } else {
+ i2c_end_transfer(s->bus);
+ s->status |= S5PC1XX_IICSTAT_TXRXEN;
+ }
+ break;
+ case MT_MODE:
+ if (value & (1 << 5)) {
+ /* START condition */
+ s->status &= ~S5PC1XX_IICSTAT_LASTBIT;
+
+ i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+ } else {
+ i2c_end_transfer(s->bus);
+ s->status |= S5PC1XX_IICSTAT_TXRXEN;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ s5pc1xx_i2c_update(s);
+ break;
+
+ case I2CADD:
+ s->address = value & 0xff;
+ break;
+
+ case I2CDS:
+ s5pc1xx_i2c_send(s, value & 0xff);
+ break;
+
+ case I2CLC:
+ s->line_ctrl = value & 0xff;
+ break;
+
+ default:
+ hw_error("s5pc1xx.i2c: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_i2c_readfn[] = {
+ s5pc1xx_i2c_read,
+ s5pc1xx_i2c_read,
+ s5pc1xx_i2c_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_i2c_writefn[] = {
+ s5pc1xx_i2c_write,
+ s5pc1xx_i2c_write,
+ s5pc1xx_i2c_write
+};
+
+static void s5pc1xx_i2c_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+ qemu_put_8s(f, &s->control);
+ qemu_put_8s(f, &s->status);
+ qemu_put_8s(f, &s->address);
+ qemu_put_8s(f, &s->datashift);
+ qemu_put_8s(f, &s->line_ctrl);
+ qemu_put_8s(f, &s->ibmr);
+ qemu_put_8s(f, &s->data);
+}
+
+static int s5pc1xx_i2c_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxI2CState *s = (S5pc1xxI2CState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_8s(f, &s->control);
+ qemu_get_8s(f, &s->status);
+ qemu_get_8s(f, &s->address);
+ qemu_get_8s(f, &s->datashift);
+ qemu_get_8s(f, &s->line_ctrl);
+ qemu_get_8s(f, &s->ibmr);
+ qemu_get_8s(f, &s->data);
+
+ return 0;
+}
+
+/* I2C init */
+static int s5pc1xx_i2c_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxI2CState *s = FROM_SYSBUS(S5pc1xxI2CState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ s->bus = i2c_init_bus(&dev->qdev, "i2c");
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_i2c_readfn, s5pc1xx_i2c_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_I2C_REG_MEM_SIZE, iomemtype);
+
+ register_savevm(&dev->qdev, "s5pc1xx.i2c", -1, 1,
+ s5pc1xx_i2c_save, s5pc1xx_i2c_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_i2c_register(void)
+{
+ sysbus_register_dev("s5pc1xx.i2c", sizeof(S5pc1xxI2CState),
+ s5pc1xx_i2c_init);
+}
+
+device_init(s5pc1xx_i2c_register)
--- /dev/null
+/*
+ * I2C bus through GPIO pins
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "i2c.h"
+#include "sysbus.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "bitbang_i2c.h"
+
+
+//#define DEBUG
+#define SDA_PIN_NUM GPIO_IDX_I2C_SDA
+
+
+typedef struct S5pc1xxI2CGPIOState {
+ SysBusDevice busdev;
+
+ bitbang_i2c_interface *bitbang;
+ i2c_bus *bus;
+
+ int32_t sda;
+ int32_t scl;
+ uint32_t instance;
+} S5pc1xxI2CGPIOState;
+
+
+static void s5pc1xx_i2c_gpio_reset(void *opaque)
+{
+ S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+ s->sda = 1;
+ s->scl = 1;
+}
+
+static void s5pc1xx_i2c_bitbang_set_conf(void *opaque, int io_index,
+ uint32_t conf)
+{
+ S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+#ifdef DEBUG
+ fprintf(stderr, "QEMU BITBANG I2C set configuration: io_index = %s, "
+ "conf = 0x%02X\n",
+ io_index == SDA_PIN_NUM ? "sda" : "scl", conf == GIPIO_CONF_INPUT);
+#endif
+
+ if (io_index == SDA_PIN_NUM) {
+ s->sda = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, conf == GIPIO_CONF_INPUT);
+ } else {
+ s->sda = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, conf == GIPIO_CONF_INPUT);
+ s->scl = (conf == GIPIO_CONF_INPUT);
+ }
+}
+
+static uint32_t s5pc1xx_i2c_bitbang_read(void *opaque, int io_index)
+{
+ S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+ uint32_t ret = io_index == SDA_PIN_NUM ? s->sda : s->scl;
+
+#ifdef DEBUG
+ fprintf(stderr, "QEMU BITBANG I2C read: io_index = %s, value = %d\n",
+ io_index == SDA_PIN_NUM ? "sda" : "scl", ret);
+#endif
+
+ return ret;
+}
+
+static void s5pc1xx_i2c_bitbang_write(void *opaque, int io_index, uint32_t value)
+{
+#ifdef DEBUG
+ fprintf(stderr, "QEMU BITBANG I2C write: io_index = %s, value = %u\n",
+ io_index == SDA_PIN_NUM ? "sda" : "scl", value);
+#endif
+}
+
+static void s5pc1xx_i2c_gpio_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+ /* FIXME: save bitbang? */
+ qemu_put_sbe32s(f, &s->sda);
+ qemu_put_sbe32s(f, &s->scl);
+}
+
+static int s5pc1xx_i2c_gpio_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxI2CGPIOState *s = (S5pc1xxI2CGPIOState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_sbe32s(f, &s->sda);
+ qemu_get_sbe32s(f, &s->scl);
+
+ return 0;
+}
+
+DeviceState *s5pc1xx_i2c_gpio_init(int instance)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.i2c.gpio");
+ qdev_prop_set_uint32(dev, "instance", instance);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
+/* I2C init */
+static int s5pc1xx_i2c_gpio_init1(SysBusDevice *dev)
+{
+ S5pc1xxI2CGPIOState *s = FROM_SYSBUS(S5pc1xxI2CGPIOState, dev);
+
+ s->bus = i2c_init_bus(&dev->qdev, "i2c");
+ s->bitbang = bitbang_i2c_init(s->bus);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_I2C, s->instance,
+ s5pc1xx_i2c_bitbang_read,
+ s5pc1xx_i2c_bitbang_write,
+ s5pc1xx_i2c_bitbang_set_conf, s);
+
+ s5pc1xx_i2c_gpio_reset(s);
+ qemu_register_reset(s5pc1xx_i2c_gpio_reset, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.i2c.gpio", s->instance, 1,
+ s5pc1xx_i2c_gpio_save, s5pc1xx_i2c_gpio_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_i2c_gpio_info = {
+ .init = s5pc1xx_i2c_gpio_init1,
+ .qdev.name = "s5pc1xx.i2c.gpio",
+ .qdev.size = sizeof(S5pc1xxI2CGPIOState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("instance", S5pc1xxI2CGPIOState, instance, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_i2c_gpio_register(void)
+{
+ sysbus_register_withprop(&s5pc1xx_i2c_gpio_info);
+}
+
+device_init(s5pc1xx_i2c_gpio_register)
--- /dev/null
+/*
+ * IIS Multi Audio Interface for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ *
+ * TODO: make support of second channel!
+ */
+
+#include "sysbus.h"
+#include "i2c.h"
+#include "s5pc1xx.h"
+
+
+#define AUDIO_WORDS_SEND 0x800
+
+/*Magic size*/
+#define AUDIO_BUFFER_SIZE 0x100000
+
+#define S5PC1XX_I2S_IISCON 0x00
+#define S5PC1XX_I2S_IISMOD 0x04
+#define S5PC1XX_I2S_IISFIC 0x08
+#define S5PC1XX_I2S_IISPSR 0x0C
+#define S5PC1XX_I2S_IISTXD 0x10
+#define S5PC1XX_I2S_IISRXD 0x14
+#define S5PC1XX_I2S_IISFICS 0x18
+#define S5PC1XX_I2S_IISTXDS 0x1C
+#define S5PC1XX_I2S_IISAHB 0x20
+#define S5PC1XX_I2S_IISSTR 0x24
+#define S5PC1XX_I2S_IISSIZE 0x28
+#define S5PC1XX_I2S_IISTRNCNT 0x2C
+#define S5PC1XX_I2S_IISADDR0 0x30
+#define S5PC1XX_I2S_IISADDR1 0x34
+#define S5PC1XX_I2S_IISADDR2 0x38
+#define S5PC1XX_I2S_IISADDR3 0x3C
+#define S5PC1XX_I2S_IISSTR1 0x40
+
+#define S5PC1XX_I2S_REG_MEM_SIZE 0x44
+
+#define S5PC1XX_IISCON_I2SACTIVE (0x1<< 0)
+#define S5PC1XX_IISCON_RXDMACTIVE (0x1<< 1)
+#define S5PC1XX_IISCON_TXDMACTIVE (0x1<< 2)
+#define S5PC1XX_IISCON_RXCHPAUSE (0x1<< 3)
+#define S5PC1XX_IISCON_TXCHPAUSE (0x1<< 4)
+#define S5PC1XX_IISCON_RXDMAPAUSE (0x1<< 5)
+#define S5PC1XX_IISCON_TXDMAPAUSE (0x1<< 6)
+#define S5PC1XX_IISCON_FRXFULL (0x1<< 7) /*Read only*/
+#define S5PC1XX_IISCON_FTX0FULL (0x1<< 8) /*Read only*/
+#define S5PC1XX_IISCON_FRXEMPT (0x1<< 9) /*Read only*/
+#define S5PC1XX_IISCON_FTX0EMPT (0x1<<10) /*Read only*/
+#define S5PC1XX_IISCON_LRI (0x1<<11) /*Read only*/
+#define S5PC1XX_IISCON_FTX1FULL (0x1<<12) /*Read only*/
+#define S5PC1XX_IISCON_FTX2FULL (0x1<<13) /*Read only*/
+#define S5PC1XX_IISCON_FTX1EMPT (0x1<<14) /*Read only*/
+#define S5PC1XX_IISCON_FTX2EMPT (0x1<<15) /*Read only*/
+#define S5PC1XX_IISCON_FTXURINTEN (0x1<<16)
+#define S5PC1XX_IISCON_FTXURSTATUS (0x1<<17) /*Write to clear*/
+#define S5PC1XX_IISCON_TXSDMACTIVE (0x1<<18)
+#define S5PC1XX_IISCON_TXSDMAPAUSE (0x1<<20)
+#define S5PC1XX_IISCON_FTXSFULL (0x1<<21) /*Read only*/
+#define S5PC1XX_IISCON_FTXSEMPT (0x1<<22) /*Read only*/
+#define S5PC1XX_IISCON_FTXSURINTEN (0x1<<23)
+#define S5PC1XX_IISCON_FTXSURSTAT (0x1<<24) /*Write to clear*/
+#define S5PC1XX_IISCON_FRXOFINTEN (0x1<<25)
+#define S5PC1XX_IISCON_FRXOFSTAT (0x1<<26) /*Write to clear*/
+#define S5PC1XX_IISCON_SWRESET (0x1<<31)
+
+#define S5PC1XX_IISCON_WRITE_MASK (0x8295007F)
+#define S5PC1XX_IISCON_READ_MASK (0x0060FF80)
+#define S5PC1XX_IISCON_WRITE_TO_CLEAR_MASK (0x05020000)
+
+
+#define S5PC1XX_IISMOD_BFSMASK (3<<1)
+#define S5PC1XX_IISMOD_32FS (0<<1)
+#define S5PC1XX_IISMOD_48FS (1<<1)
+#define S5PC1XX_IISMOD_16FS (2<<1)
+#define S5PC1XX_IISMOD_24FS (3<<1)
+
+#define S5PC1XX_IISMOD_RFSMASK (3<<3)
+#define S5PC1XX_IISMOD_256FS (0<<3)
+#define S5PC1XX_IISMOD_512FS (1<<3)
+#define S5PC1XX_IISMOD_384FS (2<<3)
+#define S5PC1XX_IISMOD_768FS (3<<3)
+
+#define S5PC1XX_IISMOD_SDFMASK (3<<5)
+#define S5PC1XX_IISMOD_IIS (0<<5)
+#define S5PC1XX_IISMOD_MSB (1<<5)
+#define S5PC1XX_IISMOD_LSB (2<<5)
+
+#define S5PC1XX_IISMOD_LRP (1<<7)
+
+#define S5PC1XX_IISMOD_TXRMASK (3<<8)
+#define S5PC1XX_IISMOD_TX (0<<8)
+#define S5PC1XX_IISMOD_RX (1<<8)
+#define S5PC1XX_IISMOD_TXRX (2<<8)
+
+#define S5PC1XX_IISMOD_TX_SET(r) (!((1<<8)&(r)))
+#define S5PC1XX_IISMOD_RX_SET(r) (((3<<8)&(r)) != 0)
+
+
+#define S5PC1XX_IISMOD_IMSMASK (3<<10)
+#define S5PC1XX_IISMOD_MSTPCLK (0<<10)
+#define S5PC1XX_IISMOD_MSTCLKAUDIO (1<<10)
+#define S5PC1XX_IISMOD_SLVPCLK (2<<10)
+#define S5PC1XX_IISMOD_SLVI2SCLK (3<<10)
+
+#define S5PC1XX_IISMOD_CDCLKCON (1<<12)
+
+#define S5PC1XX_IISMOD_BLCMASK (3<<13)
+#define S5PC1XX_IISMOD_16BIT (0<<13)
+#define S5PC1XX_IISMOD_8BIT (1<<13)
+#define S5PC1XX_IISMOD_24BIT (2<<13)
+
+#define S5PC1XX_IISMOD_SD1EN (1<<16)
+#define S5PC1XX_IISMOD_SD2EN (1<<17)
+
+#define S5PC1XX_IISMOD_CCD1MASK (3<<18)
+#define S5PC1XX_IISMOD_CCD1ND (0<<18)
+#define S5PC1XX_IISMOD_CCD11STD (1<<18)
+#define S5PC1XX_IISMOD_CCD12NDD (2<<18)
+
+#define S5PC1XX_IISMOD_CCD2MASK (3<<20)
+#define S5PC1XX_IISMOD_CCD2ND (0<<20)
+#define S5PC1XX_IISMOD_CCD21STD (1<<20)
+#define S5PC1XX_IISMOD_CCD22NDD (2<<20)
+
+#define S5PC1XX_IISMOD_BLCPMASK (3<<24)
+#define S5PC1XX_IISMOD_P16BIT (0<<24)
+#define S5PC1XX_IISMOD_P8BIT (1<<24)
+#define S5PC1XX_IISMOD_P24BIT (2<<24)
+#define S5PC1XX_IISMOD_BLCSMASK (3<<26)
+#define S5PC1XX_IISMOD_S16BIT (0<<26)
+#define S5PC1XX_IISMOD_S8BIT (1<<26)
+#define S5PC1XX_IISMOD_S24BIT (2<<26)
+#define S5PC1XX_IISMOD_TXSLP (1<<28)
+#define S5PC1XX_IISMOD_OPMSK (3<<30)
+#define S5PC1XX_IISMOD_OPCCO (0<<30)
+#define S5PC1XX_IISMOD_OPCCI (1<<30)
+#define S5PC1XX_IISMOD_OPBCO (2<<30)
+#define S5PC1XX_IISMOD_OPPCLK (3<<30)
+
+#define I2S_TOGGLE_BIT(old, new, bit) (((old) & (bit)) != ((new) & (bit)))
+
+
+/* I2C Interface */
+typedef struct S5pc1xxI2SState {
+ SysBusDevice busdev;
+
+ uint32_t iiscon;
+ uint32_t iismod;
+ uint32_t iisfic;
+ uint32_t iispsr;
+ uint32_t iisfics;
+ uint32_t iisahb;
+ uint32_t iisstr0;
+ uint32_t iissize;
+ uint32_t iistrncnt;
+ uint32_t iislvl0addr;
+ uint32_t iislvl1addr;
+ uint32_t iislvl2addr;
+ uint32_t iislvl3addr;
+ uint32_t iisstr1;
+
+ uint8_t *buffer;
+ uint32_t buf_size;
+ uint32_t play_pos;
+ uint32_t last_free;
+
+ qemu_irq irq;
+ qemu_irq dma_irq_stop1;
+ qemu_irq dma_irq_stop2;
+ DeviceState *wm8994;
+} S5pc1xxI2SState;
+
+
+static void s5pc1xx_i2s_stop(S5pc1xxI2SState *s)
+{
+ qemu_irq_raise(s->dma_irq_stop1);
+ qemu_irq_raise(s->dma_irq_stop2);
+}
+
+static void s5pc1xx_i2s_resume(S5pc1xxI2SState *s)
+{
+ qemu_irq_lower(s->dma_irq_stop1);
+ qemu_irq_lower(s->dma_irq_stop2);
+}
+
+
+static void s5pc1xx_i2s_reset(void *opaque)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+ s->iiscon = 0;
+ s->iismod = 0;
+ s->iisfic = 0;
+ s->iispsr = 0;
+ s->iisfics = 0;
+ s->iisahb = 0;
+ s->iisstr0 = 0;
+ s->iissize = 0x7FFF0000;
+ s->iistrncnt = 0;
+ s->iislvl0addr = 0;
+ s->iislvl1addr = 0;
+ s->iislvl2addr = 0;
+ s->iislvl3addr = 0;
+ s->iisstr1 = 0;
+
+ s->play_pos = 0;
+ s->last_free = 0;
+}
+
+static int s5pc1xx_i2s_pause(S5pc1xxI2SState *s) {
+ return s->iiscon & S5PC1XX_IISCON_TXCHPAUSE;
+}
+
+static int s5pc1xx_i2s_transmit(S5pc1xxI2SState *s) {
+ return S5PC1XX_IISMOD_TX_SET(s->iismod);
+}
+
+static void s5pc1xx_i2s_audio_callback(void *opaque, int free_out)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+ int8_t *codec_buffer = NULL;
+ int block_size = 0;
+
+
+ if (free_out <= 0) {
+ return;
+ }
+ if (free_out > AUDIO_WORDS_SEND) {
+ free_out = AUDIO_WORDS_SEND;
+ }
+
+ block_size = 4 * free_out;
+
+ if (s5pc1xx_i2s_pause(s)) {
+ return;
+ }
+
+ if (s->play_pos > s->last_free &&
+ s->play_pos + block_size > s->buf_size &&
+ s->play_pos + block_size - s->buf_size > s->last_free) {
+ s5pc1xx_i2s_resume(s);
+ return;
+ }
+
+ if (s->play_pos <= s->last_free &&
+ s->play_pos + block_size > s->last_free) {
+ s5pc1xx_i2s_resume(s);
+ return;
+ }
+
+ codec_buffer = wm8994_dac_buffer(s->wm8994, block_size);
+ memcpy(codec_buffer, s->buffer + s->play_pos, block_size);
+ s->play_pos = (s->play_pos + block_size) % s->buf_size;
+
+ s5pc1xx_i2s_resume(s);
+
+ wm8994_dac_commit(s->wm8994);
+}
+
+/* I2S write function */
+static void s5pc1xx_i2s_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+ int use_buf = 0;
+
+ switch (offset) {
+ case S5PC1XX_I2S_IISCON:
+ if ((value & S5PC1XX_IISCON_SWRESET) &&
+ !(s->iiscon & S5PC1XX_IISCON_SWRESET)) {
+ s5pc1xx_i2s_reset(s);
+ }
+ if (I2S_TOGGLE_BIT(s->iiscon, value, S5PC1XX_IISCON_TXCHPAUSE)) {
+ if (value & S5PC1XX_IISCON_TXCHPAUSE) {
+ s5pc1xx_i2s_stop(s);
+ } else {
+ s5pc1xx_i2s_resume(s);
+ }
+ }
+
+ if (I2S_TOGGLE_BIT(s->iiscon, value, S5PC1XX_IISCON_TXSDMACTIVE)) {
+ /*TODO: stop dma if (value & S5PC1XX_IISCON_TXSDMACTIVE) is 0*/
+ }
+
+ s->iiscon = (s->iiscon & ~S5PC1XX_IISCON_WRITE_MASK) |
+ (value & S5PC1XX_IISCON_WRITE_MASK);
+
+
+ if (!(s->iiscon & S5PC1XX_IISCON_I2SACTIVE)) {
+ s->play_pos = 0;
+ s->last_free = 0;
+ s5pc1xx_i2s_resume(s);
+ }
+
+ /* FIXME: Kernel wants this bit for synchronization. Fix this line */
+ s->iiscon |= S5PC1XX_IISCON_LRI;
+ break;
+ case S5PC1XX_I2S_IISMOD:
+ s->iismod = value;
+ break;
+ case S5PC1XX_I2S_IISFIC:
+ s->iisfic = value;
+ break;
+ case S5PC1XX_I2S_IISPSR:
+ s->iispsr = value;
+ break;
+ case S5PC1XX_I2S_IISTXDS:
+ case S5PC1XX_I2S_IISTXD:
+ if (!s5pc1xx_i2s_transmit(s))
+ break;
+
+ if ( (s->iismod & S5PC1XX_IISMOD_LRP) &&
+ ((s->iismod & S5PC1XX_IISMOD_BLCMASK) == S5PC1XX_IISMOD_16BIT ||
+ (s->iismod & S5PC1XX_IISMOD_BLCMASK) == S5PC1XX_IISMOD_8BIT)) {
+ *(uint32_t *)(s->buffer + s->last_free) =
+ (value << 16) | (value >> 16);
+ } else {
+ *(uint32_t *)(s->buffer + s->last_free) = value;
+ }
+
+ s->last_free = (s->last_free + sizeof(value)) % s->buf_size;
+
+ use_buf = s->last_free - s->play_pos;
+ if (use_buf < 0) {
+ use_buf = s->buf_size + use_buf;
+ }
+
+ if (use_buf >= AUDIO_WORDS_SEND*4) {
+ s5pc1xx_i2s_stop(s);
+ }
+ break;
+ case S5PC1XX_I2S_IISFICS:
+ s->iisfics = value;
+ break;
+ case S5PC1XX_I2S_IISAHB:
+ s->iisahb = value;
+ break;
+ case S5PC1XX_I2S_IISSTR:
+ s->iisstr0 = value;
+ break;
+ case S5PC1XX_I2S_IISSIZE:
+ s->iissize = value;
+ break;
+ case S5PC1XX_I2S_IISTRNCNT:
+ s->iistrncnt = value;
+ break;
+ case S5PC1XX_I2S_IISADDR0:
+ s->iislvl0addr = value;
+ break;
+ case S5PC1XX_I2S_IISADDR1:
+ s->iislvl1addr = value;
+ break;
+ case S5PC1XX_I2S_IISADDR2:
+ s->iislvl2addr = value;
+ break;
+ case S5PC1XX_I2S_IISADDR3:
+ s->iislvl3addr = value;
+ break;
+ case S5PC1XX_I2S_IISSTR1:
+ s->iisstr1 = value;
+ break;
+ default:
+ /* FIXME: all registers are accessible by byte */
+ hw_error("s5pc1xx.i2s: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* I2S read function */
+static uint32_t s5pc1xx_i2s_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+
+ switch (offset) {
+ case S5PC1XX_I2S_IISCON:
+ return s->iiscon;
+ case S5PC1XX_I2S_IISMOD:
+ return s->iismod;
+ case S5PC1XX_I2S_IISFIC:
+ return s->iisfic;
+ case S5PC1XX_I2S_IISPSR:
+ return s->iispsr;
+ case S5PC1XX_I2S_IISRXD:
+ /* TODO: Support receive data register */
+ return 0;
+ case S5PC1XX_I2S_IISFICS:
+ return s->iisfics;
+ case S5PC1XX_I2S_IISAHB:
+ return s->iisahb;
+ case S5PC1XX_I2S_IISSTR:
+ return s->iisstr0;
+ case S5PC1XX_I2S_IISSIZE:
+ return s->iissize;
+ case S5PC1XX_I2S_IISTRNCNT:
+ return s->iistrncnt;
+ case S5PC1XX_I2S_IISADDR0:
+ return s->iislvl0addr;
+ case S5PC1XX_I2S_IISADDR1:
+ return s->iislvl1addr;
+ case S5PC1XX_I2S_IISADDR2:
+ return s->iislvl2addr;
+ case S5PC1XX_I2S_IISADDR3:
+ return s->iislvl3addr;
+ case S5PC1XX_I2S_IISSTR1:
+ return s->iisstr1;
+ default:
+ /* FIXME: all registers are accessible by byte */
+ hw_error("s5pc1xx.i2s: bad write offset " TARGET_FMT_plx "\n", offset);
+ return 0;
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_i2s_readfn[] = {
+ s5pc1xx_i2s_read,
+ s5pc1xx_i2s_read,
+ s5pc1xx_i2s_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_i2s_writefn[] = {
+ s5pc1xx_i2s_write,
+ s5pc1xx_i2s_write,
+ s5pc1xx_i2s_write
+};
+
+static void s5pc1xx_i2s_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+ qemu_put_be32s(f, &s->iiscon);
+ qemu_put_be32s(f, &s->iismod);
+ qemu_put_be32s(f, &s->iisfic);
+ qemu_put_be32s(f, &s->iispsr);
+ qemu_put_be32s(f, &s->iisfics);
+ qemu_put_be32s(f, &s->iisahb);
+ qemu_put_be32s(f, &s->iisstr0);
+ qemu_put_be32s(f, &s->iissize);
+ qemu_put_be32s(f, &s->iistrncnt);
+ qemu_put_be32s(f, &s->iislvl0addr);
+ qemu_put_be32s(f, &s->iislvl1addr);
+ qemu_put_be32s(f, &s->iislvl2addr);
+ qemu_put_be32s(f, &s->iislvl3addr);
+ qemu_put_be32s(f, &s->iisstr1);
+
+ qemu_put_buffer(f, s->buffer, s->buf_size);
+
+ qemu_put_be32s(f, &s->buf_size);
+ qemu_put_be32s(f, &s->play_pos);
+ qemu_put_be32s(f, &s->last_free);
+}
+
+static int s5pc1xx_i2s_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxI2SState *s = (S5pc1xxI2SState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->iiscon);
+ qemu_get_be32s(f, &s->iismod);
+ qemu_get_be32s(f, &s->iisfic);
+ qemu_get_be32s(f, &s->iispsr);
+ qemu_get_be32s(f, &s->iisfics);
+ qemu_get_be32s(f, &s->iisahb);
+ qemu_get_be32s(f, &s->iisstr0);
+ qemu_get_be32s(f, &s->iissize);
+ qemu_get_be32s(f, &s->iistrncnt);
+ qemu_get_be32s(f, &s->iislvl0addr);
+ qemu_get_be32s(f, &s->iislvl1addr);
+ qemu_get_be32s(f, &s->iislvl2addr);
+ qemu_get_be32s(f, &s->iislvl3addr);
+ qemu_get_be32s(f, &s->iisstr1);
+
+ qemu_get_buffer(f, s->buffer, s->buf_size);
+
+ qemu_get_be32s(f, &s->buf_size);
+ qemu_get_be32s(f, &s->play_pos);
+ qemu_get_be32s(f, &s->last_free);
+
+ return 0;
+}
+
+/* I2S init */
+DeviceState *s5pc1xx_i2s_init(target_phys_addr_t base, qemu_irq irq,
+ DeviceState *wm8994_dev, qemu_irq dma_irq1,
+ qemu_irq dma_irq2)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.i2s");
+
+ qdev_prop_set_ptr(dev, "wm8994", wm8994_dev);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 1, dma_irq1);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 2, dma_irq2);
+ return dev;
+}
+
+static int s5pc1xx_i2s_init1(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxI2SState *s = FROM_SYSBUS(S5pc1xxI2SState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->dma_irq_stop1);
+ sysbus_init_irq(dev, &s->dma_irq_stop2);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_i2s_readfn, s5pc1xx_i2s_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_I2S_REG_MEM_SIZE, iomemtype);
+ s->buf_size = AUDIO_BUFFER_SIZE;
+ s->buffer = qemu_malloc(s->buf_size);
+
+ s5pc1xx_i2s_reset(s);
+ qemu_register_reset(s5pc1xx_i2s_reset, s);
+ wm8994_data_req_set(s->wm8994, s5pc1xx_i2s_audio_callback, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.i2s", -1, 1,
+ s5pc1xx_i2s_save, s5pc1xx_i2s_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_i2s_info = {
+ .init = s5pc1xx_i2s_init1,
+ .qdev.name = "s5pc1xx.i2s",
+ .qdev.size = sizeof(S5pc1xxI2SState),
+ .qdev.props = (Property[]) {
+ {
+ .name = "wm8994",
+ .info = &qdev_prop_ptr,
+ .offset = offsetof(S5pc1xxI2SState, wm8994),
+ },
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_i2s_register_devices(void)
+{
+ sysbus_register_withprop(&s5pc1xx_i2s_info);
+}
+
+device_init(s5pc1xx_i2s_register_devices)
--- /dev/null
+/*
+ * JPEG codec for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "qemu-common.h"
+#include "sysbus.h"
+
+#ifdef CONFIG_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+/* Control registers */
+#define JPGMOD 0x000 /* R/W Specifies the Sub-sampling Mode Register 0x0000_0000 */
+ #define PROC_MODE (1 << 3)
+
+#define JPGOPR 0x004 /* R Specifies the Operation Status Register 0x0000_0000 */
+#define QTBL 0x008 /* R/W Specifies the Quantization Table Number Register 0x0000_0000 */
+#define HTBL 0x00C /* R/W Specifies the Huffman Table Number Register 0x0000_0000 */
+#define JPGDRI_U 0x010 /* R/W Specifies the MCU, which inserts RST marker(upper 8-bit) 0x0000_0000 */
+#define JPGDRI_L 0x014 /* R/W Specifies the MCU, which inserts RST marker (lower 8-bit) 0x0000_0000 */
+#define JPGY_U 0x018 /* R/W Specifies the Vertical Resolution (upper 8-bit) 0x0000_0000 */
+#define JPGY_L 0x01C /* R/W Specifies the Vertical Resolution (lower 8-bit) 0x0000_0000 */
+#define JPGX_U 0x020 /* R/W Specifies the Horizontal Resolution (upper 8-bit) 0x0000_0000 */
+#define JPGX_L 0x024 /* R/W Specifies the Horizontal Resolution (lower 8-bit) 0x0000_0000 */
+#define JPGCNT_U 0x028 /* R Specifies the amount of the compressed data in bytes (upper 8- bit) 0x0000_0000 */
+#define JPGCNT_M 0x02C /* R Specifies the amount of the compressed data in bytes (middle 8-bit) 0x0000_0000 */
+#define JPGCNT_L 0x030 /* R Specifies the amount of the compressed data in bytes (lower 8-bit) 0x0000_0000 */
+#define JPGINTSE 0x034 /* R/W Specifies the Interrupt Setting Register 0x0000_0000 */
+#define JPGINTST 0x038 /* R Specifies the Interrupt Status Register 0x0000_0000 */
+ #define RESULT_STAT (1 << 6)
+ #define STREAM_STAT (1 << 5)
+
+#define JPGCOM 0x04C /* W Specifies the Command register 0x0000_0000 */
+ #define INT_RELEASE (1 << 2)
+
+#define IMGADR 0x050 /* R/W Specifies the Source or Destination Image Address 0x0000_0000 */
+#define JPGADR 0x058 /* R/W Specifies the Source or Destination JPEG File Address 0x0000_0000 */
+#define COEF1 0x05C /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define COEF2 0x060 /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define COEF3 0x064 /* R/W Specifies the Coefficient Values for RGB <-> YCbCr Converter 0x0000_0000 */
+#define JPGCMOD 0x068 /* R/W Specifies the Mode Selection and Core Clock Setting 0x0000_0020 */
+#define JPGCLKCON 0x06C /* R/W Specifies the Power On/ Off and Clock Down Control 0x0000_0002 */
+#define JSTART 0x070 /* W Specifies the Start Compression or Decompression 0x0000_0000 */
+#define SW_RESET 0x078 /* R/W Specifies the S/W Reset 0x0000_0000 */
+#define TIMER_SE 0x07C /* R/W Specifies the Internal Timer Setting Register 0x7FFF_FFFF */
+ #define TIMER_INT_EN (1 << 31)
+
+#define TIMER_ST 0x080 /* R/W Specifies the Internal Timer Status Register 0x7FFF_FFFF */
+ #define TIMER_INT_STAT (1 << 31)
+
+#define COMSTAT 0x084 /* R Specifies the Command Status Register 0x0000_0000 */
+#define OUTFORM 0x088 /* R/W Specifies the Output Color Format of Decompression 0x0000_0000 */
+#define VERSION 0x08C /* R Specifies the Version Register 0x0000_0003 */
+#define ENC_STREAM_INTSE 0x098 /* R/W Specifies the Compressed Stream Size Interrupt Setting Register 0x00FF_FFE0 */
+ #define ENC_STREAM_INT_EN (1 << 24)
+
+#define ENC_STREAM_INTST 0x09C /* R Specifies the Compressed Stream Size Interrupt Status Register 0x0000_0000 */
+ #define ENC_STREAM_INT_STAT (1 << 0)
+
+#define QTBL0(n) (0x400 + (n << 2)) /* R/W Specifies the Quantization table 0 0x0000_0000 */
+#define QTBL1(n) (0x500 + (n << 2)) /* R/W Specifies the Quantization table 1 0x0000_0000 */
+#define QTBL2(n) (0x600 + (n << 2)) /* R/W Specifies the Quantization table 2 0x0000_0000 */
+#define QTBL3(n) (0x700 + (n << 2)) /* R/W Specifies the Quantization table 3 0x0000_0000 */
+#define HDCTBL0(n) (0x800 + (n << 2)) /* W Specifies the Huffman DC Table 0 - the number of code per code length 0x0000_0000 */
+#define HDCTBLG0(n) (0x840 + (n << 2)) /* W Specifies the Huffman DC Table 0 - Group number of the order for occurrence 0x0000_0000 */
+#define HACTBL0(n) (0x880 + (n << 2)) /* W Specifies the Huffman AC Table 0 - the number of code per code length 0x0000_0000 */
+#define HACTBLG0(n) (0x8C0 + (n << 2)) /* W Specifies the Huffman AC Table 0 - Group number of the order for occurrence 0x0000_0000 */
+#define HDCTBL1(n) (0xC00 + (n << 2)) /* W Specifies the Huffman DC Table 1 - the number of code per code length 0x0000_0000 */
+#define HDCTBLG1(n) (0xC40 + (n << 2)) /* W Specifies the Huffman DC Table 1 - Group number of the order for occurrence 0x0000_0000 */
+#define HACTBL1(n) (0xC80 + (n << 2)) /* W Specifies the Huffman AC Table 1 - the number of code per code length 0x0000_0000 */
+#define HACTBLG1(n) (0xCC0 + (n << 2)) /* W Specifies the Huffman AC Table 1 - Group number of the order for occurrence 0x0000_0000 */
+
+#define S5PC1XX_JPEG_REG_MEM_SIZE 0xF48
+
+/* FIXME: jpeg header size (8192 bytes) is an experimental value */
+#define JPEG_HDR_SIZE 8192
+#define MAX_IMG_SIZE (8192 * 8192 * 3)
+
+/* Control values */
+#define COMPR 0
+#define DECOMPR 1
+
+#define YCbCr_422 (((4 + 2 + 2) * 2) | (16 << 5) | (8 << 10)) /* YCbCr4:2:2, x2 bits a sample, MCU block size = 16x8 */
+#define RGB_565 (5 + 6 + 5) /* RGB565, x1 bits a sample */
+
+#define YCbCr_444 (((4 + 4 + 4) * 2) | (8 << 5) | (8 << 10))
+#define YCbCr_420 (((4 + 2 + 0) * 2) | (16 << 5) | (16 << 10))
+#define GRAY (((4 + 0 + 0) * 2) | (8 << 5) | (8 << 10))
+
+#define JUST_RAISE_IRQ 0
+#define RESULT_INT (1 << 0)
+#define STREAM_INT (1 << 1)
+#define TIMER_INT (1 << 2)
+#define ENC_STREAM_INT (1 << 3)
+
+#define LOW 1
+#define HIGH 0
+#define NONE 0
+
+/* Arithmetical macroses */
+#define SIZE_OUT(value, min) ((((value) + (min) - 1) / (min)) * (min)) /* evaluate a length in 'min' units */
+#define ALL_BITS(b,a) (((1 << ((b) - (a) + 1)) - 1) << (a)) /* all bits from 'b' to 'a' are high */
+
+
+#ifdef CONFIG_JPEG
+typedef struct jpeg_compress_struct CInfo;
+typedef struct jpeg_decompress_struct DInfo;
+
+typedef struct Dummy_CInfo {
+ JDIMENSION image_width;
+ JDIMENSION image_height;
+ J_COLOR_SPACE in_color_space;
+ J_COLOR_SPACE jpeg_color_space;
+ struct comp_info {
+ int h_samp_factor;
+ int v_samp_factor;
+ } comp_info[3];
+} Dummy_CInfo;
+
+typedef struct Dummy_DInfo {
+ JDIMENSION image_width;
+ JDIMENSION image_height;
+ J_COLOR_SPACE out_color_space;
+} Dummy_DInfo;
+#endif
+
+typedef struct S5pc1xxJpegState {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint8_t proc_mode, c1;
+ uint32_t byte_cnt, src_len, dst_len;
+ uint32_t mode_in, mode_out;
+ target_phys_addr_t src_addr, dst_addr;
+ char *src_base, *dst_base;
+
+ /* Control registers */
+ uint32_t jpgmod;
+ uint32_t jpgopr;
+ uint32_t qtbl;
+ uint32_t htbl;
+ uint32_t jpgdri_u;
+ uint32_t jpgdri_l;
+ uint32_t jpgy_u;
+ uint32_t jpgy_l;
+ uint32_t jpgx_u;
+ uint32_t jpgx_l;
+ uint32_t jpgintse;
+ uint32_t jpgintst;
+ uint32_t imgadr;
+ uint32_t jpgadr;
+ uint32_t coef1;
+ uint32_t coef2;
+ uint32_t coef3;
+ uint32_t jpgcmod;
+ uint32_t jpgclkcon;
+ uint32_t sw_reset;
+ uint32_t timer_se;
+ uint32_t timer_st;
+ uint32_t comstat;
+ uint32_t outform;
+ uint32_t version;
+ uint32_t enc_stream_intse;
+ uint32_t enc_stream_intst;
+ uint8_t qtbl0[64];
+ uint8_t qtbl1[64];
+ uint8_t qtbl2[64];
+ uint8_t qtbl3[64];
+ uint8_t hdctbl0[28];
+ uint8_t hactbl0[178];
+ uint8_t hdctbl1[28];
+ uint8_t hactbl1[178];
+
+#ifdef CONFIG_JPEG
+ /* Two structures below are used to protect corresponding values
+ * in CInfo and DInfo from overwriting */
+ Dummy_CInfo *dummy_cinfo;
+ Dummy_DInfo *dummy_dinfo;
+
+ CInfo *cinfo;
+ DInfo *dinfo;
+#endif
+} S5pc1xxJpegState;
+
+static void s5pc1xx_jpeg_irq(S5pc1xxJpegState *s,
+ uint8_t irq_stat, short to_clear);
+
+////////// Special code (only when configured with libjpeg support) //////////
+
+#ifdef CONFIG_JPEG
+/* Combine image properties before compression */
+static void s5pc1xx_jpeg_pre_coding(S5pc1xxJpegState *s)
+{
+ uint8_t src_color_mode, dst_color_mode;
+ uint16_t cols_out, rows_out;
+
+ Dummy_CInfo *dummy_cinfo = s->dummy_cinfo;
+
+ /* Input and output images addresses */
+ s->src_addr = s->imgadr;
+ s->dst_addr = s->jpgadr;
+
+ /* Input image area */
+ dummy_cinfo->image_width = (s->jpgx_u << 8) | s->jpgx_l;
+ dummy_cinfo->image_height = (s->jpgy_u << 8) | s->jpgy_l;
+
+ /* Coeff for color mode converting */
+ s->c1 = (s->jpgcmod & 0x2) << 3; /* 0 or 16 */
+
+ /* Input image color mode */
+ src_color_mode = (s->jpgcmod & ALL_BITS(7, 5)) >> 5;
+ switch (src_color_mode) {
+ case 0x1:
+ s->mode_in = YCbCr_422;
+ dummy_cinfo->in_color_space = JCS_YCbCr;
+ break;
+ case 0x2:
+ s->mode_in = RGB_565;
+ dummy_cinfo->in_color_space = JCS_RGB;
+ break;
+ default:
+ hw_error("s5pc1xx.jpeg: bad input color space (num = %u)\n",
+ src_color_mode);
+ }
+
+ /* Input image size */
+ s->src_len = (dummy_cinfo->image_width * (s->mode_in & ALL_BITS(4, 0)) *
+ dummy_cinfo->image_height) >> 3;
+
+ /* Output image color mode */
+ dst_color_mode = (s->jpgmod & ALL_BITS(2, 0));
+ dummy_cinfo->comp_info[0].h_samp_factor = 2;
+ dummy_cinfo->comp_info[0].v_samp_factor = 2;
+ switch (dst_color_mode) {
+ case 0x1:
+ s->mode_out = YCbCr_422;
+ dummy_cinfo->jpeg_color_space = JCS_YCbCr;
+ dummy_cinfo->comp_info[1].h_samp_factor = 1;
+ dummy_cinfo->comp_info[1].v_samp_factor = 2;
+ dummy_cinfo->comp_info[2].h_samp_factor = 1;
+ dummy_cinfo->comp_info[2].v_samp_factor = 2;
+ break;
+ case 0x2:
+ s->mode_out = YCbCr_420;
+ dummy_cinfo->jpeg_color_space = JCS_YCbCr;
+ dummy_cinfo->comp_info[1].h_samp_factor = 1;
+ dummy_cinfo->comp_info[1].v_samp_factor = 1;
+ dummy_cinfo->comp_info[2].h_samp_factor = 1;
+ dummy_cinfo->comp_info[2].v_samp_factor = 1;
+ break;
+ default:
+ hw_error("s5pc1xx.jpeg: bad output color space (num = %u)\n",
+ dst_color_mode);
+ }
+
+ /* Output image area */
+ cols_out = SIZE_OUT(dummy_cinfo->image_width,
+ (s->mode_out >> 5) & ALL_BITS(4, 0));
+ rows_out = SIZE_OUT(dummy_cinfo->image_height,
+ (s->mode_out >> 10) & ALL_BITS(4, 0));
+
+ /* Output image size */
+ s->dst_len = JPEG_HDR_SIZE +
+ ((cols_out * (s->mode_out & ALL_BITS(4, 0)) * rows_out) >> 3);
+}
+
+/* Calculate JPEG data size */
+static uint32_t s5pc1xx_jpeg_data_size(S5pc1xxJpegState *s)
+{
+ target_phys_addr_t page_size = TARGET_PAGE_SIZE;
+ uint8_t *jpg_Buf = NULL;
+ int i, j;
+
+ s->dummy_dinfo->image_height =
+ s->dummy_dinfo->image_width = 0;
+
+ /* Travel over all memory pages occupied by the image */
+ for (i = 0, j = 1; i < (MAX_IMG_SIZE + JPEG_HDR_SIZE); i++) {
+ /* Map the first memory page or map the next memory page if less
+ * than 9 bytes till the end of the current one remains */
+ if (!i || ((i % TARGET_PAGE_SIZE) > (TARGET_PAGE_SIZE - 9))) {
+ jpg_Buf = cpu_physical_memory_map(s->jpgadr, &page_size, 0);
+ if (!jpg_Buf || (page_size != TARGET_PAGE_SIZE * j)) {
+ fprintf(stderr,
+ "s5pc1xx.jpeg: input memory can't be accessed\n");
+ /* Raise result interrupt as NOT OK */
+ s5pc1xx_jpeg_irq(s, STREAM_INT, HIGH);
+ return 0;
+ }
+ j++;
+ page_size = TARGET_PAGE_SIZE * j;
+ }
+
+ if (jpg_Buf[i] == 0xFF) {
+ if (jpg_Buf[i + 1] == 0xC0) {
+ s->dummy_dinfo->image_height =
+ (jpg_Buf[i + 5] << 8) | jpg_Buf[i + 6];
+ s->dummy_dinfo->image_width =
+ (jpg_Buf[i + 7] << 8) | jpg_Buf[i + 8];
+ }
+ if (jpg_Buf[i + 1] == 0xD9)
+ return (i + 2); /* Resulting size of the image */
+ }
+ }
+
+ fprintf(stderr, "s5pc1xx.jpeg: input image end can't be reached\n");
+ /* Raise result interrupt as NOT OK */
+ s5pc1xx_jpeg_irq(s, STREAM_INT, HIGH);
+ return 0;
+}
+
+/* Combine image properties before decompression */
+static void s5pc1xx_jpeg_pre_decoding(S5pc1xxJpegState *s)
+{
+ uint8_t dst_color_mode;
+ uint16_t cols_out, rows_out;
+
+ Dummy_DInfo *dummy_dinfo = s->dummy_dinfo;
+
+ /* Input and output image addresses */
+ s->src_addr = s->jpgadr;
+ s->dst_addr = s->imgadr;
+
+ s->src_len = s5pc1xx_jpeg_data_size(s);
+
+ /* Output image color mode */
+ dst_color_mode = (s->outform & 0x1);
+ switch (dst_color_mode) {
+ case 0x0:
+ s->mode_out = YCbCr_422;
+ dummy_dinfo->out_color_space = JCS_YCbCr;
+ break;
+ case 0x1:
+ s->mode_out = YCbCr_420;
+ dummy_dinfo->out_color_space = JCS_YCbCr;
+ break;
+ default:
+ hw_error("s5pc1xx.jpeg: bad output color space (num = %u)\n",
+ dst_color_mode);
+ }
+
+ /* Output image area */
+ cols_out = SIZE_OUT(dummy_dinfo->image_width,
+ s->mode_out >> 5 & ALL_BITS(4, 0));
+ rows_out = SIZE_OUT(dummy_dinfo->image_height,
+ s->mode_out >> 10 & ALL_BITS(4, 0));
+
+ /* Output image size */
+ s->dst_len = (cols_out * (s->mode_out & ALL_BITS(4, 0)) * rows_out) >> 3;
+}
+
+/* Get references to input and output data */
+static int s5pc1xx_jpeg_mem_map(S5pc1xxJpegState *s)
+{
+ target_phys_addr_t src_len, dst_len;
+
+ src_len = s->src_len;
+ dst_len = s->dst_len;
+
+ s->src_base = cpu_physical_memory_map(s->src_addr, &src_len, 0);
+ s->dst_base = cpu_physical_memory_map(s->dst_addr, &dst_len, 0);
+
+ if (!s->src_base || !s->dst_base) {
+ fprintf(stderr, "s5pc1xx.jpeg: bad image address\n");
+ /* Raise result interrupt as NOT OK */
+ s5pc1xx_jpeg_irq(s, JUST_RAISE_IRQ, NONE);
+ return 1;
+ }
+
+ if ((src_len == s->src_len) && (dst_len == s->dst_len))
+ return 0;
+
+ cpu_physical_memory_unmap(s->src_base, src_len, 0, 0);
+ cpu_physical_memory_unmap(s->dst_base, dst_len, 0, 0);
+ fprintf(stderr, "s5pc1xx.jpeg: not enough memory for image\n");
+ /* Raise result interrupt as NOT OK */
+ s5pc1xx_jpeg_irq(s, JUST_RAISE_IRQ, NONE);
+ return 1;
+}
+
+/* JPEG compression */
+static void s5pc1xx_jpeg_coding(S5pc1xxJpegState *s)
+{
+ int row_stride, i;
+ struct jpeg_error_mgr jerr;
+ JSAMPROW row_pointer[1];
+
+ CInfo *cinfo = s->cinfo;
+ Dummy_CInfo *dummy_cinfo = s->dummy_cinfo;
+
+ /* Allocate and initialize JPEG compression object */
+ cinfo->err = jpeg_std_error(&jerr);
+ jpeg_create_compress(cinfo);
+
+ cinfo->dest->next_output_byte = (JSAMPLE *) s->dst_base;
+ cinfo->dest->free_in_buffer = s->dst_len;
+
+ /* RGB or YCbCr (3 components) can be used as compressor input color space
+ * according to s5pc1xx specification */
+ cinfo->input_components = 3;
+ cinfo->image_width = dummy_cinfo->image_width;
+ cinfo->image_height = dummy_cinfo->image_height;
+ cinfo->in_color_space = dummy_cinfo->in_color_space;
+
+ jpeg_set_defaults(cinfo);
+
+ /* Note: using dummy_cinfo->jpeg_color_space instead of
+ * cinfo->jpeg_color_space because the second one is set by
+ * jpeg_set_defaults() and may be incorrect */
+ jpeg_set_colorspace(cinfo, dummy_cinfo->jpeg_color_space);
+
+#if JPEG_LIB_VERSION >= 61
+
+ for (i = 0; i < 4; i++) {
+ cinfo->comp_info[i].quant_tbl_no = (s->qtbl >> (i * 2)) & 0x3;
+ cinfo->comp_info[i].dc_tbl_no = (s->htbl >> (i * 2)) & 0x1;
+ cinfo->comp_info[i].ac_tbl_no = (s->htbl >> (i * 2 + 1)) & 0x1;
+ }
+
+ /* FIXME: force_baseline may be 'false' in the next four instructions */
+ jpeg_add_quant_table (cinfo, 0,
+ (const unsigned int *) s->qtbl0,
+ 100, true);
+ jpeg_add_quant_table (cinfo, 1,
+ (const unsigned int *) s->qtbl1,
+ 100, true);
+ jpeg_add_quant_table (cinfo, 2,
+ (const unsigned int *) s->qtbl2,
+ 100, true);
+ jpeg_add_quant_table (cinfo, 3,
+ (const unsigned int *) s->qtbl3,
+ 100, true);
+
+ cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL *) s->hdctbl0;
+ cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL *) s->hactbl0;
+
+ cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL *) s->hdctbl1;
+ cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL *) s->hactbl1;
+
+ jpeg_alloc_huff_table((j_common_ptr) cinfo);
+
+#endif
+
+ for (i = 0; i < 3; i++) {
+ cinfo->comp_info[i].h_samp_factor =
+ dummy_cinfo->comp_info[i].h_samp_factor;
+ cinfo->comp_info[i].v_samp_factor =
+ dummy_cinfo->comp_info[i].v_samp_factor;
+ }
+
+ jpeg_start_compress(cinfo, FALSE);
+
+ /* JSAMPLEs per row in input_buffer */
+ row_stride = cinfo->image_width * cinfo->input_components;
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ row_pointer[0] =
+ (JSAMPROW) &s->src_base[cinfo->next_scanline * row_stride];
+ (void) jpeg_write_scanlines(cinfo, row_pointer, 1);
+ }
+
+ /* Finish compression */
+ jpeg_finish_compress(cinfo);
+ jpeg_destroy_compress(cinfo);
+}
+
+/* JPEG decompression */
+static void s5pc1xx_jpeg_decoding(S5pc1xxJpegState *s)
+{
+ struct jpeg_error_mgr jerr;
+ JSAMPARRAY buffer; /* Output row buffer */
+ int row_stride; /* Physical row width in output buffer */
+ int i, count;
+
+ DInfo *dinfo = s->dinfo;
+ Dummy_DInfo *dummy_dinfo = s->dummy_dinfo;
+
+ /* Allocate and initialize JPEG decompression object */
+ dinfo->err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(dinfo);
+
+ dinfo->src->next_input_byte = (JSAMPLE *) s->src_base;
+ dinfo->src->bytes_in_buffer = s->src_len;
+
+ (void) jpeg_read_header(dinfo, TRUE);
+
+ dinfo->out_color_space = dummy_dinfo->out_color_space;
+
+ (void) jpeg_start_decompress(dinfo);
+
+ /* JSAMPLEs per row in output buffer */
+ row_stride = dinfo->output_width * dinfo->output_components;
+
+ /* Make a one-row-high sample array
+ * that will go away when done with image */
+ buffer = (*(dinfo->mem->alloc_sarray))
+ ((j_common_ptr) dinfo, JPOOL_IMAGE, row_stride, 1);
+
+ count = 0;
+ while (dinfo->output_scanline < dinfo->output_height) {
+ (void) jpeg_read_scanlines(dinfo, buffer, 1);
+
+ for (i = 0; i < row_stride; i++, count++)
+ s->dst_base[count] = (char) buffer[0][i];
+ }
+
+ /* Finish decompression */
+ (void) jpeg_finish_decompress(dinfo);
+ jpeg_destroy_decompress(dinfo);
+}
+#endif
+
+//////////////////////////// Common code //////////////////////////////////////
+
+/* Reset JPEG controller */
+static void s5pc1xx_jpeg_reset(void *opaque)
+{
+ S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+ int i;
+
+ s->jpgmod = 0x1;
+ s->jpgopr = 0x0;
+ s->qtbl = 0x0;
+ s->htbl = 0x0;
+ s->jpgdri_u = 0x0;
+ s->jpgdri_l = 0x0;
+ s->jpgy_u = 0x0;
+ s->jpgy_l = 0x0;
+ s->jpgx_u = 0x0;
+ s->jpgx_l = 0x0;
+ s->jpgintse = 0x0;
+ s->jpgintst = 0x0;
+ s->imgadr = 0x0;
+ s->jpgadr = 0x0;
+ s->coef1 = 0x0;
+ s->coef2 = 0x0;
+ s->coef3 = 0x0;
+ s->jpgcmod = 0x00000020;
+ s->jpgclkcon = 0x00000002;
+ s->sw_reset = 0x0;
+ s->timer_se = 0x7FFFFFFF;
+ s->timer_st = 0x7FFFFFFF;
+ s->comstat = 0x0;
+ s->outform = 0x0;
+ s->version = 0x00000003;
+ s->enc_stream_intse = 0x00FFFFE0;
+ s->enc_stream_intst = 0x0;
+
+ for(i = 0; i < 178; i++) {
+ s->hactbl0[i] = 0x0;
+ s->hactbl1[i] = 0x0;
+
+ if (i > 63)
+ continue;
+
+ s->qtbl0[i] = 0x0;
+ s->qtbl1[i] = 0x0;
+ s->qtbl2[i] = 0x0;
+ s->qtbl3[i] = 0x0;
+
+ if (i > 28)
+ continue;
+
+ s->hdctbl0[i] = 0x0;
+ s->hdctbl1[i] = 0x0;
+ }
+}
+
+/* Interrupts handler */
+static void s5pc1xx_jpeg_irq(S5pc1xxJpegState *s,
+ uint8_t irq_stat, short to_clear)
+{
+ switch(irq_stat) {
+ case JUST_RAISE_IRQ:
+ qemu_irq_raise(s->irq);
+ return;
+ case RESULT_INT:
+ if (to_clear) {
+ s->jpgintst &= ~RESULT_STAT;
+ break;
+ }
+ s->jpgintst |= RESULT_STAT;
+ qemu_irq_raise(s->irq);
+ return;
+ case STREAM_INT:
+ if (to_clear) {
+ s->jpgintst &= ~STREAM_STAT;
+ break;
+ }
+ s->jpgintst |= STREAM_STAT;
+ qemu_irq_raise(s->irq);
+ return;
+ case TIMER_INT:
+ if (to_clear) {
+ s->timer_st &= ~TIMER_INT_STAT;
+ break;
+ }
+ s->timer_st |= TIMER_INT_STAT;
+ if (s->timer_se & TIMER_INT_EN)
+ qemu_irq_raise(s->irq);
+ return;
+ case ENC_STREAM_INT:
+ if (to_clear) {
+ s->enc_stream_intst &= ~ENC_STREAM_INT_STAT;
+ break;
+ }
+ s->enc_stream_intst |= ENC_STREAM_INT_STAT;
+ if (s->enc_stream_intse & ENC_STREAM_INT_EN)
+ qemu_irq_raise(s->irq);
+ return;
+ default:
+ return;
+ }
+
+ /* Lower irq if all states are clear */
+ if (!((s->jpgintst & RESULT_STAT) |
+ (s->jpgintst & STREAM_STAT) |
+ (s->timer_st & TIMER_INT_STAT) |
+ (s->enc_stream_intst & ENC_STREAM_INT_STAT)))
+ qemu_irq_lower(s->irq);
+}
+
+/* JPEG controller registers read */
+static uint32_t s5pc1xx_jpeg_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+ switch(offset) {
+ case JPGMOD:
+#ifdef CONFIG_JPEG
+ if ((s->proc_mode == DECOMPR) && !s->jpgopr) {
+ if (s->dinfo->jpeg_color_space == JCS_GRAYSCALE) {
+ s->jpgmod = (s->jpgmod & ALL_BITS(31, 3)) | 0x3;
+ } else {
+ /* FIXME: the value below must be different
+ * depending on downsampling coefs */
+ s->jpgmod = (s->jpgmod & ALL_BITS(31, 3)) | 0x0;
+ }
+ }
+#endif
+ return s->jpgmod;
+ case JPGOPR:
+ return s->jpgopr;
+ case QTBL:
+ return s->qtbl;
+ case HTBL:
+ return s->htbl;
+ case JPGDRI_U:
+ return s->jpgdri_u;
+ case JPGDRI_L:
+ return s->jpgdri_l;
+ case JPGY_U:
+ return s->jpgy_u;
+ case JPGY_L:
+ return s->jpgy_l;
+ case JPGX_U:
+ return s->jpgx_u;
+ case JPGX_L:
+ return s->jpgx_l;
+ case JPGCNT_U:
+ if ((s->proc_mode & COMPR) && !s->jpgopr)
+ return (s->byte_cnt >> 16) & ALL_BITS(7, 0);
+ return 0;
+ case JPGCNT_M:
+ if ((s->proc_mode & COMPR) && !s->jpgopr)
+ return (s->byte_cnt >> 8) & ALL_BITS(7, 0);
+ return 0;
+ case JPGCNT_L:
+ if ((s->proc_mode & COMPR) && !s->jpgopr)
+ return (s->byte_cnt >> 0) & ALL_BITS(7, 0);
+ return 0;
+ case JPGINTSE:
+ return s->jpgintse;
+ case JPGINTST:
+ /* Set successful status if no operation is going on */
+ if (!s->jpgopr)
+ s->jpgintst = 0x40;
+ return s->jpgintst;
+ case IMGADR:
+ return s->imgadr;
+ case JPGADR:
+ return s->jpgadr;
+ case COEF1:
+ return s->coef1;
+ case COEF2:
+ return s->coef2;
+ case COEF3:
+ return s->coef3;
+ case JPGCMOD:
+ return s->jpgcmod;
+ case JPGCLKCON:
+ return s->jpgclkcon;
+ case SW_RESET:
+ return s->sw_reset;
+ case TIMER_SE:
+ return s->timer_se;
+ case TIMER_ST:
+ return s->timer_st;
+ case COMSTAT:
+ return s->comstat;
+ case OUTFORM:
+ return s->outform;
+ case VERSION:
+ return s->version;
+ case ENC_STREAM_INTSE:
+ return s->enc_stream_intse;
+ case ENC_STREAM_INTST:
+ return s->enc_stream_intst;
+ case QTBL0(0) ... QTBL0(63):
+ return s->qtbl0[(offset - QTBL0(0)) >> 2];
+ case QTBL1(0) ... QTBL1(63):
+ return s->qtbl1[(offset - QTBL1(0)) >> 2];
+ case QTBL2(0) ... QTBL2(63):
+ return s->qtbl2[(offset - QTBL2(0)) >> 2];
+ case QTBL3(0) ... QTBL3(63):
+ return s->qtbl3[(offset - QTBL3(0)) >> 2];
+ default:
+ hw_error("s5pc1xx.jpeg: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* JPEG controller register write */
+static void s5pc1xx_jpeg_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t old_val;
+ S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+ switch(offset) {
+ case JPGMOD:
+ old_val = s->jpgmod;
+ s->proc_mode = (value & PROC_MODE) ? DECOMPR : COMPR;
+ s->jpgmod = (s->proc_mode == COMPR) ?
+ value :
+ ((value & ALL_BITS(31, 3)) | (old_val & ALL_BITS(2, 0)));
+ break;
+ case QTBL:
+ s->qtbl = value;
+ break;
+ case HTBL:
+ s->htbl = value;
+ break;
+ case JPGDRI_U:
+ s->jpgdri_u = value;
+ break;
+ case JPGDRI_L:
+ s->jpgdri_l = value;
+ break;
+ case JPGY_U:
+ s->jpgy_u = value;
+ break;
+ case JPGY_L:
+ s->jpgy_l = value;
+ break;
+ case JPGX_U:
+ s->jpgx_u = value;
+ break;
+ case JPGX_L:
+ s->jpgx_l = value;
+ break;
+ case JPGINTSE:
+ s->jpgintse = value;
+ break;
+ case JPGCOM:
+ if (value & INT_RELEASE) {
+ s5pc1xx_jpeg_irq(s, RESULT_INT, LOW);
+ s5pc1xx_jpeg_irq(s, STREAM_INT, LOW);
+ }
+ break;
+ case IMGADR:
+ s->imgadr = value;
+ break;
+ case JPGADR:
+ s->jpgadr = value;
+ break;
+ case COEF1:
+ s->coef1 = value;
+ break;
+ case COEF2:
+ s->coef2 = value;
+ break;
+ case COEF3:
+ s->coef3 = value;
+ break;
+ case JPGCMOD:
+ s->jpgcmod = value;
+ break;
+ case JPGCLKCON:
+ s->jpgclkcon = value;
+ break;
+ case JSTART:
+ if (!value)
+ break;
+ s->jpgopr = 0x1;
+#ifdef CONFIG_JPEG
+ switch(s->proc_mode) {
+ case COMPR:
+ s5pc1xx_jpeg_pre_coding(s);
+ if (s5pc1xx_jpeg_mem_map(s))
+ break;
+ s5pc1xx_jpeg_coding(s);
+ s->byte_cnt = s5pc1xx_jpeg_data_size(s);
+ /* Raise result interrupt as OK */
+ s5pc1xx_jpeg_irq(s, RESULT_INT, HIGH);
+ break;
+ case DECOMPR:
+ s5pc1xx_jpeg_pre_decoding(s);
+ if (!s->src_len)
+ break;
+ if (s5pc1xx_jpeg_mem_map(s))
+ break;
+ s5pc1xx_jpeg_decoding(s);
+ /* Raise result interrupt as OK */
+ s5pc1xx_jpeg_irq(s, RESULT_INT, HIGH);
+ break;
+ }
+#endif
+ s->jpgopr = 0x0;
+ break;
+ case SW_RESET:
+ if (value) {
+ s->sw_reset = 0x1;
+ s5pc1xx_jpeg_reset(s);
+ }
+ s->sw_reset = 0x0;
+ break;
+ case TIMER_SE:
+ s->timer_se = value;
+ break;
+ case TIMER_ST:
+ s->timer_st = value;
+ if (value & TIMER_INT_STAT)
+ s5pc1xx_jpeg_irq(s, TIMER_INT, LOW);
+ break;
+ case OUTFORM:
+ s->outform = value;
+ break;
+ case ENC_STREAM_INTSE:
+ s->enc_stream_intse = value;
+ break;
+ case ENC_STREAM_INTST:
+ s->enc_stream_intst = value;
+ if (value & ENC_STREAM_INT_STAT)
+ s5pc1xx_jpeg_irq(s, ENC_STREAM_INT, LOW);
+ break;
+ case QTBL0(0) ... QTBL0(63):
+ s->qtbl0[(offset - QTBL0(0)) >> 2] = value & 0xFF;
+ break;
+ case QTBL1(0) ... QTBL1(63):
+ s->qtbl1[(offset - QTBL1(0)) >> 2] = value & 0xFF;
+ break;
+ case QTBL2(0) ... QTBL2(63):
+ s->qtbl2[(offset - QTBL2(0)) >> 2] = value & 0xFF;
+ break;
+ case QTBL3(0) ... QTBL3(63):
+ s->qtbl3[(offset - QTBL3(0)) >> 2] = value & 0xFF;
+ break;
+ case HDCTBL0(0) ... HDCTBL0(15):
+ s->hdctbl0[(offset - HDCTBL0(0)) >> 2] = value & 0xFF;
+ break;
+ case HDCTBLG0(0) ... HDCTBLG0(11):
+ s->hdctbl0[((offset - HDCTBLG0(0)) >> 2) + 16] = value & 0xFF;
+ break;
+ case HACTBL0(0) ... HACTBL0(15):
+ s->hactbl0[(offset - HACTBL0(0)) >> 2] = value & 0xFF;
+ break;
+ case HACTBLG0(0) ... HACTBLG0(161):
+ s->hactbl0[((offset - HACTBLG0(0)) >> 2) + 16] = value & 0xFF;
+ break;
+ case HDCTBL1(0) ... HDCTBL1(15):
+ s->hdctbl1[(offset - HDCTBL1(0)) >> 2] = value & 0xFF;
+ break;
+ case HDCTBLG1(0) ... HDCTBLG1(11):
+ s->hdctbl1[((offset - HDCTBLG1(0)) >> 2) + 16] = value & 0xFF;
+ break;
+ case HACTBL1(0) ... HACTBL1(15):
+ s->hactbl1[(offset - HACTBL1(0)) >> 2] = value & 0xFF;
+ break;
+ case HACTBLG1(0) ... HACTBLG1(161):
+ s->hactbl1[((offset - HACTBLG1(0)) >> 2) + 16] = value & 0xFF;
+ break;
+ default:
+ hw_error("s5pc1xx.jpeg: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_jpeg_readfn[] = {
+ s5pc1xx_jpeg_read,
+ s5pc1xx_jpeg_read,
+ s5pc1xx_jpeg_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_jpeg_writefn[] = {
+ s5pc1xx_jpeg_write,
+ s5pc1xx_jpeg_write,
+ s5pc1xx_jpeg_write
+};
+
+static void s5pc1xx_jpeg_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+ if (s->jpgopr)
+ hw_error("s5pc1xx.jpeg: JPEG process is going on, "
+ "saving snapshot is not possible\n");
+
+ qemu_put_8s(f, &s->proc_mode);
+
+ qemu_put_be32s(f, &s->jpgmod);
+ qemu_put_be32s(f, &s->jpgopr);
+ qemu_put_be32s(f, &s->qtbl);
+ qemu_put_be32s(f, &s->htbl);
+ qemu_put_be32s(f, &s->jpgdri_u);
+ qemu_put_be32s(f, &s->jpgdri_l);
+ qemu_put_be32s(f, &s->jpgy_u);
+ qemu_put_be32s(f, &s->jpgy_l);
+ qemu_put_be32s(f, &s->jpgx_u);
+ qemu_put_be32s(f, &s->jpgx_l);
+ qemu_put_be32s(f, &s->jpgintse);
+ qemu_put_be32s(f, &s->jpgintst);
+ qemu_put_be32s(f, &s->imgadr);
+ qemu_put_be32s(f, &s->jpgadr);
+ qemu_put_be32s(f, &s->coef1);
+ qemu_put_be32s(f, &s->coef2);
+ qemu_put_be32s(f, &s->coef3);
+ qemu_put_be32s(f, &s->jpgcmod);
+ qemu_put_be32s(f, &s->jpgclkcon);
+ qemu_put_be32s(f, &s->sw_reset);
+ qemu_put_be32s(f, &s->timer_se);
+ qemu_put_be32s(f, &s->timer_st);
+ qemu_put_be32s(f, &s->comstat);
+ qemu_put_be32s(f, &s->outform);
+ qemu_put_be32s(f, &s->version);
+ qemu_put_be32s(f, &s->enc_stream_intse);
+ qemu_put_be32s(f, &s->enc_stream_intst);
+
+ qemu_put_buffer(f, s->qtbl0, sizeof(s->qtbl0));
+ qemu_put_buffer(f, s->qtbl1, sizeof(s->qtbl1));
+ qemu_put_buffer(f, s->qtbl2, sizeof(s->qtbl2));
+ qemu_put_buffer(f, s->qtbl3, sizeof(s->qtbl3));
+ qemu_put_buffer(f, s->hdctbl0, sizeof(s->hdctbl0));
+ qemu_put_buffer(f, s->hactbl0, sizeof(s->hactbl0));
+ qemu_put_buffer(f, s->hdctbl1, sizeof(s->hdctbl1));
+ qemu_put_buffer(f, s->hactbl1, sizeof(s->hactbl1));
+}
+
+static int s5pc1xx_jpeg_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxJpegState *s = (S5pc1xxJpegState *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_8s(f, &s->proc_mode);
+
+ qemu_get_be32s(f, &s->jpgmod);
+ qemu_get_be32s(f, &s->jpgopr);
+ qemu_get_be32s(f, &s->qtbl);
+ qemu_get_be32s(f, &s->htbl);
+ qemu_get_be32s(f, &s->jpgdri_u);
+ qemu_get_be32s(f, &s->jpgdri_l);
+ qemu_get_be32s(f, &s->jpgy_u);
+ qemu_get_be32s(f, &s->jpgy_l);
+ qemu_get_be32s(f, &s->jpgx_u);
+ qemu_get_be32s(f, &s->jpgx_l);
+ qemu_get_be32s(f, &s->jpgintse);
+ qemu_get_be32s(f, &s->jpgintst);
+ qemu_get_be32s(f, &s->imgadr);
+ qemu_get_be32s(f, &s->jpgadr);
+ qemu_get_be32s(f, &s->coef1);
+ qemu_get_be32s(f, &s->coef2);
+ qemu_get_be32s(f, &s->coef3);
+ qemu_get_be32s(f, &s->jpgcmod);
+ qemu_get_be32s(f, &s->jpgclkcon);
+ qemu_get_be32s(f, &s->sw_reset);
+ qemu_get_be32s(f, &s->timer_se);
+ qemu_get_be32s(f, &s->timer_st);
+ qemu_get_be32s(f, &s->comstat);
+ qemu_get_be32s(f, &s->outform);
+ qemu_get_be32s(f, &s->version);
+ qemu_get_be32s(f, &s->enc_stream_intse);
+ qemu_get_be32s(f, &s->enc_stream_intst);
+
+ qemu_get_buffer(f, s->qtbl0, sizeof(s->qtbl0));
+ qemu_get_buffer(f, s->qtbl1, sizeof(s->qtbl1));
+ qemu_get_buffer(f, s->qtbl2, sizeof(s->qtbl2));
+ qemu_get_buffer(f, s->qtbl3, sizeof(s->qtbl3));
+ qemu_get_buffer(f, s->hdctbl0, sizeof(s->hdctbl0));
+ qemu_get_buffer(f, s->hactbl0, sizeof(s->hactbl0));
+ qemu_get_buffer(f, s->hdctbl1, sizeof(s->hdctbl1));
+ qemu_get_buffer(f, s->hactbl1, sizeof(s->hactbl1));
+
+ return 0;
+}
+
+/* JPEG initialization */
+static int s5pc1xx_jpeg_init(SysBusDevice *dev)
+{
+ int iomemtype;
+
+ S5pc1xxJpegState *s = FROM_SYSBUS(S5pc1xxJpegState, dev);
+ s5pc1xx_jpeg_reset(s);
+
+#ifdef CONFIG_JPEG
+ s->cinfo = (CInfo *) qemu_mallocz(sizeof(CInfo));
+ s->dinfo = (DInfo *) qemu_mallocz(sizeof(DInfo));
+ s->dummy_cinfo = (Dummy_CInfo *) qemu_mallocz(sizeof(Dummy_CInfo));
+ s->dummy_dinfo = (Dummy_DInfo *) qemu_mallocz(sizeof(Dummy_DInfo));
+#endif
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_jpeg_readfn, s5pc1xx_jpeg_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_JPEG_REG_MEM_SIZE, iomemtype);
+
+ qemu_register_reset(s5pc1xx_jpeg_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.jpeg", -1, 1,
+ s5pc1xx_jpeg_save, s5pc1xx_jpeg_load, s);
+ return 0;
+}
+
+static void s5pc1xx_jpeg_register(void)
+{
+ sysbus_register_dev("s5pc1xx.jpeg", sizeof(S5pc1xxJpegState),
+ s5pc1xx_jpeg_init);
+}
+
+device_init(s5pc1xx_jpeg_register)
--- /dev/null
+/*
+ * S5PC1XX Keypad Interface
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "console.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+
+#define KEY_RESERVED 0
+#define KEY_ESC 1
+#define KEY_1 2
+#define KEY_2 3
+#define KEY_3 4
+#define KEY_4 5
+#define KEY_5 6
+#define KEY_6 7
+#define KEY_7 8
+#define KEY_8 9
+#define KEY_9 10
+#define KEY_0 11
+#define KEY_MINUS 12
+#define KEY_EQUAL 13
+#define KEY_BACKSPACE 14
+#define KEY_TAB 15
+#define KEY_Q 16
+#define KEY_W 17
+#define KEY_E 18
+#define KEY_R 19
+#define KEY_T 20
+#define KEY_Y 21
+#define KEY_U 22
+#define KEY_I 23
+#define KEY_O 24
+#define KEY_P 25
+#define KEY_LEFTBRACE 26
+#define KEY_RIGHTBRACE 27
+#define KEY_ENTER 28
+#define KEY_LEFTCTRL 29
+#define KEY_A 30
+#define KEY_S 31
+#define KEY_D 32
+#define KEY_F 33
+#define KEY_G 34
+#define KEY_H 35
+#define KEY_J 36
+#define KEY_K 37
+#define KEY_L 38
+#define KEY_SEMICOLON 39
+#define KEY_APOSTROPHE 40
+#define KEY_GRAVE 41
+#define KEY_LEFTSHIFT 42
+#define KEY_BACKSLASH 43
+#define KEY_Z 44
+#define KEY_X 45
+#define KEY_C 46
+#define KEY_V 47
+#define KEY_B 48
+#define KEY_N 49
+#define KEY_M 50
+#define KEY_COMMA 51
+#define KEY_DOT 52
+#define KEY_SLASH 53
+#define KEY_RIGHTSHIFT 54
+#define KEY_KPASTERISK 55
+#define KEY_LEFTALT 56
+#define KEY_SPACE 57
+#define KEY_CAPSLOCK 58
+#define KEY_F1 59
+#define KEY_F2 60
+#define KEY_F3 61
+#define KEY_F4 62
+#define KEY_F5 63
+#define KEY_F6 64
+#define KEY_F7 65
+#define KEY_F8 66
+#define KEY_F9 67
+#define KEY_F10 68
+#define KEY_NUMLOCK 69
+#define KEY_SCROLLLOCK 70
+#define KEY_KP7 71
+#define KEY_KP8 72
+#define KEY_KP9 73
+#define KEY_KPMINUS 74
+#define KEY_KP4 75
+#define KEY_KP5 76
+#define KEY_KP6 77
+#define KEY_KPPLUS 78
+#define KEY_KP1 79
+#define KEY_KP2 80
+#define KEY_KP3 81
+#define KEY_KP0 82
+#define KEY_KPDOT 83
+
+#define KEY_ZENKAKUHANKAKU 85
+#define KEY_102ND 86
+#define KEY_F11 87
+#define KEY_F12 88
+#define KEY_RO 89
+#define KEY_KATAKANA 90
+#define KEY_HIRAGANA 91
+#define KEY_HENKAN 92
+#define KEY_KATAKANAHIRAGANA 93
+#define KEY_MUHENKAN 94
+#define KEY_KPJPCOMMA 95
+#define KEY_KPENTER 96
+#define KEY_RIGHTCTRL 97
+#define KEY_KPSLASH 98
+#define KEY_SYSRQ 99
+#define KEY_RIGHTALT 100
+#define KEY_LINEFEED 101
+#define KEY_HOME 102
+#define KEY_UP 103
+#define KEY_PAGEUP 104
+#define KEY_LEFT 105
+#define KEY_RIGHT 106
+#define KEY_END 107
+#define KEY_DOWN 108
+#define KEY_PAGEDOWN 109
+#define KEY_INSERT 110
+#define KEY_DELETE 111
+#define KEY_MACRO 112
+#define KEY_MUTE 113
+#define KEY_VOLUMEDOWN 114
+#define KEY_VOLUMEUP 115
+#define KEY_POWER 116 /* SC System Power Down */
+#define KEY_KPEQUAL 117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE 119
+#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */
+
+#define KEY_KPCOMMA 121
+#define KEY_HANGEUL 122
+#define KEY_HANGUEL KEY_HANGEUL
+#define KEY_HANJA 123
+#define KEY_YEN 124
+#define KEY_LEFTMETA 125
+#define KEY_RIGHTMETA 126
+#define KEY_COMPOSE 127
+
+#define ROW_NUM 14
+#define COLUMN_NUM 8
+#define ROW_INIT 0x3FFF
+#define KEY_MAX_NUMBER 128
+#define S5PC1XX_KEYIF_REG_MEM_SIZE 0x14
+
+struct keymap {
+ int column;
+ int row;
+};
+
+typedef struct S5pc1xxKeyIFState {
+ SysBusDevice busdev;
+
+ /* Specifies the KEYPAD interface control register */
+ union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct keyifcon_bits {
+ unsigned int_f_en : 1;
+ unsigned int_r_en : 1;
+ unsigned df_en : 1;
+ unsigned fc_en : 1;
+ unsigned wakeupen : 1;
+ } b;
+ } keyifcon;
+
+ /* Specifies the KEYPAD interface status and clear register */
+ union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct keyifstsclr_bits {
+ unsigned p_int : 14;
+ unsigned reserved14_15 : 2;
+ unsigned r_int : 14;
+ } b;
+ } keyifstsclr;
+
+ /* Specifies the KEYPAD interface column data output register */
+ union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct keyifcol_bits {
+ unsigned keyifcol : 8;
+ unsigned keyifcolen : 8;
+ } b;
+ } keyifcol;
+
+ /* Specifies the KEYPAD interface row data input register */
+ uint32_t keyifrow;
+
+ /* Specifies the KEYPAD interface debouncing filter clock
+ * division register */
+ uint32_t keyiffc;
+
+ qemu_irq irq_keypad;
+
+ /* The keypad supports 14 rows and 8 columns */
+ uint16_t keypad[COLUMN_NUM];
+
+ /* S5PC110 may have a shift of KEYIFCOL register values */
+ uint32_t shift;
+
+ /* Name of the keymap used */
+ char *keymapname;
+
+ /* Mapping from QEMU keycodes to keypad row-column; filled at device init */
+ struct keymap map[KEY_MAX_NUMBER];
+} S5pc1xxKeyIFState;
+
+
+/* Mapping variants for QEMU keycodes */
+static int msm_keycode[COLUMN_NUM][ROW_NUM] = {
+ {1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_KP4 /*KEY_LEFT*/, 64, 65, 66, 67, 68, 69},
+ {9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_KP6 /*KEY_RIGHT*/, 16, 70, 71, 72, 73, 74, 75},
+ {17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_KP8 /*KEY_UP*/, 76, 77, 78, 79, 80, 81},
+ {25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32, 82, 83, 84, 85, 86, 87},
+ {33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_KP2 /*KEY_DOWN*/, KEY_BACKSPACE, 88, 89, 90, 91, 92, 93},
+ {KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48, 94, 95, 96, 97, 98, 99},
+ {KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER, 100, 101, 102, 103, 104, 105},
+ {KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA, 106, 107, 108, 109, 110, 111}
+};
+
+static int int_keycode[COLUMN_NUM][ROW_NUM] = {
+ {1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_KP4 /*KEY_LEFT*/},
+ {9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_KP6 /*KEY_RIGHT*/, 16},
+ {17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_KP8 /*KEY_UP*/},
+ {25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32},
+ {33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_KP2 /*KEY_DOWN*/, KEY_BACKSPACE},
+ {KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48},
+ {KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER},
+ {KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA}
+};
+
+static int aquila_keycode[COLUMN_NUM][ROW_NUM] = {
+ {KEY_TAB /*KEY_CAMERA*/, KEY_ESC /*KEY_CONFIG*/},
+ {KEY_EQUAL /*KEY_VOLUMEUP*/, KEY_MINUS /*KEY_VOLUMEDOWN*/}
+};
+
+
+static void s5pc1xx_keyif_reset(DeviceState *d)
+{
+ S5pc1xxKeyIFState *s =
+ FROM_SYSBUS(S5pc1xxKeyIFState, sysbus_from_qdev(d));
+ int i = 0;
+
+ s->keyifcon.v = 0x00000000;
+ s->keyifstsclr.v = 0x00000000;
+ s->keyifcol.v = 0x00000000;
+ s->keyifrow = 0x00000000;
+ s->keyiffc = 0x00000000;
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ s->keypad[i] = ROW_INIT;
+ }
+}
+
+static int s5pc1xx_keypad_event(void *opaque, int keycode)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ struct keymap k = {0, 0};
+ int rel = 0;
+
+ rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
+ keycode &= ~(0x80); /* strip qemu key release bit */
+
+ assert(keycode < KEY_MAX_NUMBER);
+
+ k = s->map[keycode];
+
+ /* don't report unknown keypress */
+ if (k.column < 0 || k.row < 0) {
+ return -1;
+ }
+
+ if (rel) {
+ s->keypad[k.column] |= 1 << k.row;
+ } else {
+ s->keypad[k.column] &= ~(1 << k.row);
+ }
+
+ if (rel && s->keyifcon.b.int_r_en) {
+ qemu_irq_raise(s->irq_keypad);
+ s->keyifstsclr.b.r_int = 1;
+ }
+
+ if (!rel && s->keyifcon.b.int_f_en) {
+ qemu_irq_raise(s->irq_keypad);
+ s->keyifstsclr.b.p_int = 1;
+ }
+
+ return 0;
+}
+
+/* Read KEYIF by GPIO */
+static uint32_t s5pc1xx_keyif_gpio_read(void *opaque,
+ int io_index)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ int i;
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ if (io_index == GPIO_KP_COL(i)) {
+ return s->keyifcol.v >> i;
+ }
+ }
+
+ for (i = 0; i < ROW_NUM; i++) {
+ if (io_index == GPIO_KP_ROW(i)) {
+ return s->keyifrow >> i;
+ }
+ }
+
+ return 0;
+}
+
+/* Write KEYIF by GPIO */
+static void s5pc1xx_keyif_gpio_write(void *opaque,
+ int io_index,
+ uint32_t value)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ int i;
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ if (io_index == GPIO_KP_COL(i)) {
+ s->keyifcol.v |= value << i;
+ }
+ }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_keyif_gpio_readfn = s5pc1xx_keyif_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_keyif_gpio_writefn = s5pc1xx_keyif_gpio_write;
+
+static uint32_t s5pc1xx_keyif_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+
+ switch (offset) {
+ case 0x00: /* KEYIFCON */
+ return s->keyifcon.v;
+ case 0x04: /* KEYIFSTSCLR */
+ return s->keyifstsclr.v;
+ case 0x08: /* KEYIFCOL */
+ return s->keyifcol.v << s->shift;
+ case 0x0C: /* KEYIFROW */
+ return s->keyifrow;
+ case 0x10: /* KEYIFFC */
+ return s->keyiffc;
+ default:
+ hw_error("s5pc1xx.keyif: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_keyif_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ struct keyifstsclr_bits* v = NULL;
+ int i = 0;
+
+ switch (offset) {
+ case 0x00: /* KEYIFCON */
+ s->keyifcon.v = val;
+ break;
+ case 0x04: /* KEYIFSTSCLR */
+ v = (struct keyifstsclr_bits*)&val;
+ if (v->p_int) {
+ s->keyifstsclr.b.p_int = 0;
+ qemu_irq_lower(s->irq_keypad);
+ }
+ if (v->r_int) {
+ s->keyifstsclr.b.r_int = 0;
+ qemu_irq_lower(s->irq_keypad);
+ }
+ break;
+ case 0x08: /* KEYIFCOL */
+ s->keyifcol.v = (val >> s->shift) & ~0xFF00;
+ s->keyifrow = ROW_INIT; /* 14 bit */
+ /* FIXME: implement keyifcolen handling */
+ for (i = 0; i < COLUMN_NUM; i++) {
+ if (!(s->keyifcol.b.keyifcol & (1 << i))) {
+ s->keyifrow &= s->keypad[i];
+ }
+ }
+ break;
+ case 0x0C: /* KEYIFROW */
+ /* Read-only */
+ break;
+ case 0x10: /* KEYIFFC */
+ s->keyiffc = val;
+ break;
+ default:
+ hw_error("s5pc1xx.keyif: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_keyif_mm_read[] = {
+ s5pc1xx_keyif_read,
+ s5pc1xx_keyif_read,
+ s5pc1xx_keyif_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_keyif_mm_write[] = {
+ s5pc1xx_keyif_write,
+ s5pc1xx_keyif_write,
+ s5pc1xx_keyif_write
+};
+
+static void s5pc1xx_keyif_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->keyifcon.v);
+ qemu_put_be32s(f, &s->keyifstsclr.v);
+ qemu_put_be32s(f, &s->keyifcol.v);
+
+ qemu_put_be32s(f, &s->keyifrow);
+ qemu_put_be32s(f, &s->keyiffc);
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ qemu_put_be16s(f, &s->keypad[i]);
+ }
+}
+
+static int s5pc1xx_keyif_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxKeyIFState *s = (S5pc1xxKeyIFState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->keyifcon.v);
+ qemu_get_be32s(f, &s->keyifstsclr.v);
+ qemu_get_be32s(f, &s->keyifcol.v);
+
+ qemu_get_be32s(f, &s->keyifrow);
+ qemu_get_be32s(f, &s->keyiffc);
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ qemu_get_be16s(f, &s->keypad[i]);
+ }
+
+ return 0;
+}
+
+static void s5pc1xx_init_keymap(S5pc1xxKeyIFState *s)
+{
+ int i, j;
+ struct keymap init = {-1, -1};
+ int (*keypad_keycode)[COLUMN_NUM][ROW_NUM];
+
+ for (i = 0; i < KEY_MAX_NUMBER; i++) {
+ s->map[i] = init;
+ }
+
+ /* Look for the keymap with corresponding name */
+ if (s->keymapname == NULL || !strcmp(s->keymapname, "msm")) {
+ /* Default one */
+ keypad_keycode = &msm_keycode;
+ } else if (!strcmp(s->keymapname, "int")) {
+ keypad_keycode = &int_keycode;
+ } else if (!strcmp(s->keymapname, "aquila")) {
+ keypad_keycode = &aquila_keycode;
+ } else {
+ hw_error("s5pc1xx.keyif: unknown keymap '%s'", s->keymapname);
+ }
+
+ for (i = 0; i < COLUMN_NUM; i++) {
+ for (j = 0; j < ROW_NUM; j++) {
+ struct keymap k = {i, j};
+ s->map[(*keypad_keycode)[i][j]] = k;
+ }
+ }
+}
+
+DeviceState *s5pc1xx_keyif_init(target_phys_addr_t base, qemu_irq irq,
+ const char *keymapname, uint32_t shift)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.keyif");
+
+ qdev_prop_set_uint32(dev, "shift", shift);
+ qdev_prop_set_string(dev, "keymap", (char *)keymapname);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+static int s5pc1xx_keyif_init1(SysBusDevice *dev)
+{
+ S5pc1xxKeyIFState *s = FROM_SYSBUS(S5pc1xxKeyIFState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq_keypad);
+ iomemtype = cpu_register_io_memory(s5pc1xx_keyif_mm_read,
+ s5pc1xx_keyif_mm_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_KEYIF_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_KEYIF, 0,
+ s5pc1xx_keyif_gpio_readfn,
+ s5pc1xx_keyif_gpio_writefn, NULL, s);
+ s5pc1xx_init_keymap(s);
+ qemu_add_kbd_event_handler(s5pc1xx_keypad_event, s, "Samsung Keypad");
+
+ s5pc1xx_keyif_reset(&s->busdev.qdev);
+
+ register_savevm(&dev->qdev, "s5pc1xx.keyif", -1, 1,
+ s5pc1xx_keyif_save, s5pc1xx_keyif_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_keyif_info = {
+ .init = s5pc1xx_keyif_init1,
+ .qdev.name = "s5pc1xx.keyif",
+ .qdev.size = sizeof(S5pc1xxKeyIFState),
+ .qdev.reset = s5pc1xx_keyif_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_STRING("keymap", S5pc1xxKeyIFState, keymapname),
+ DEFINE_PROP_UINT32("shift", S5pc1xxKeyIFState, shift, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_keyif_register(void)
+{
+ sysbus_register_withprop(&s5pc1xx_keyif_info);
+}
+
+device_init(s5pc1xx_keyif_register)
--- /dev/null
+/*
+ * S5PC1XX LCD Controller
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+/*
+ * Known issues:
+ * multiple windows blending - implemented but not tested
+ * shadow registers - not implemented
+ * i80 indirect interface - not implemented
+ * dithering - not implemented
+ * RTQoS - not implemented
+ */
+
+#include "console.h"
+#include "pixel_ops.h"
+#include "s5pc1xx.h"
+#include "sysbus.h"
+
+
+#define BISWP 0x8
+#define BYSWP 0x4
+#define HWSWP 0x2
+#define WSWP 0x1
+
+
+typedef struct {
+ uint8_t r, g, b;
+ uint32_t a;
+} rgba;
+
+struct DrawConfig;
+
+typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
+typedef void draw_line_func(struct DrawConfig *cfg, uint8_t *src,
+ uint8_t *dst, uint8_t *ifb);
+typedef uint32_t coef_func(const struct DrawConfig *cfg, rgba pa, rgba pb);
+
+typedef struct DrawConfig {
+ pixel_to_rgb_func *pixel_to_rgb;
+ draw_line_func *draw_line;
+ int (*put_pixel)(rgba p, uint8_t *pixel);
+ int (*get_pixel)(uint8_t *src, rgba *p);
+ void (*blend)(struct DrawConfig *cfg, rgba p_old, rgba p_new, rgba *p);
+ coef_func *coef_p, *coef_q, *coef_a, *coef_b;
+ uint8_t is_palletized;
+ uint32_t bg_alpha[2], fg_alpha[2];
+ uint32_t color_key, color_mask, color_ctl;
+ uint8_t fg_alpha_pix, bg_alpha_pix;
+ int width;
+ int bpp;
+ uint32_t *palette;
+ uint8_t swap;
+ uint8_t fg_pixel_blending, bg_pixel_blending;
+ uint8_t fg_alpha_sel, bg_alpha_sel;
+} DrawConfig;
+
+typedef struct S5pc1xxLcdWindow {
+ uint32_t wincon;
+ uint32_t vidosd[4];
+ uint32_t buf_start[2];
+ uint32_t buf_end[2];
+ uint32_t buf_size;
+ uint32_t keycon[2];
+ uint32_t winmap;
+ uint32_t vidw_alpha[2];
+ uint32_t blendeq;
+ uint32_t palette[256];
+} S5pc1xxLcdWindow;
+
+typedef struct {
+ SysBusDevice busdev;
+
+ uint32_t shadowcon;
+ uint32_t vidcon[3];
+ uint32_t prtcon;
+ uint32_t vidtcon[3];
+ uint32_t vp1tcon[2];
+ uint32_t vidintcon[2];
+ uint32_t dithcon;
+ uint32_t wpalcon[2];
+ uint32_t trigcon;
+ uint32_t ituifcon;
+ uint32_t i80ifcon[4];
+ uint32_t ldi_cmdcon[2];
+ uint32_t sifccon[3];
+ uint32_t blendcon;
+ uint32_t ldi_cmd[12];
+ uint32_t dualrgb;
+
+ S5pc1xxLcdWindow window[5];
+ uint8_t *ifb;
+ uint8_t *valid_line;
+ uint8_t *valid_line_prev;
+ DisplayState *console;
+ uint8_t invalidate;
+ qemu_irq irq[3];
+} S5pc1xxLcdState;
+
+
+/* Palette/pixel to RGB conversion */
+
+#define DEF_PIXEL_TO_RGB(N,R,G,B,A) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+ p->b = (pixel & ((1 << (B)) - 1)) << (8 - (B)); \
+ pixel >>= (B); \
+ p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)); \
+ pixel >>= (G); \
+ p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)); \
+ pixel >>= (R); \
+ if (1 == (A)) { \
+ p->a = pixel & 1; \
+ } else if (8 == (A)) { \
+ p->a = pixel & 0xFF; \
+ p->a = (p->a << 16) | (p->a << 8) | p->a; \
+ } else { \
+ p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)); \
+ } \
+}
+
+DEF_PIXEL_TO_RGB(pixel_a232_to_rgb, 2, 3, 2, 1)
+DEF_PIXEL_TO_RGB(pixel_a444_to_rgb, 4, 4, 4, 1)
+DEF_PIXEL_TO_RGB(pixel_4444_to_rgb, 4, 4, 4, 4)
+DEF_PIXEL_TO_RGB(pixel_565_to_rgb, 5, 6, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_a555_to_rgb, 5, 5, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_555_to_rgb, 5, 5, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_666_to_rgb, 6, 6, 6, 0)
+DEF_PIXEL_TO_RGB(pixel_a666_to_rgb, 6, 6, 6, 1)
+DEF_PIXEL_TO_RGB(pixel_a665_to_rgb, 6, 6, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_888_to_rgb, 8, 8, 8, 0)
+DEF_PIXEL_TO_RGB(pixel_a888_to_rgb, 8, 8, 8, 1)
+DEF_PIXEL_TO_RGB(pixel_a887_to_rgb, 8, 8, 7, 1)
+DEF_PIXEL_TO_RGB(pixel_8888_to_rgb, 8, 8, 8, 8)
+
+/* Special case for (5+1,5+1,5+1) mode */
+static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
+{
+ uint8_t u = (pixel >> 15) & 1;
+ p->b = (((pixel & 0x1F) << 1) | u) << 2;
+ pixel >>= 5;
+ p->g = (((pixel & 0x3F) << 1) | u) << 2;
+ pixel >>= 6;
+ p->r = (((pixel & 0x1F) << 1) | u) << 2;
+}
+
+
+/* Write RGB to QEMU's GraphicConsole framebuffer */
+
+static int put_pixel8(rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
+ *(uint8_t *)d = pixel;
+ return 1;
+}
+
+static int put_pixel15(rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_pixel16(rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_pixel24(rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint8_t *)d++ = (pixel >> 0) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 8) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
+ return 3;
+}
+
+static int put_pixel32(rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint32_t *)d = pixel;
+ return 4;
+}
+
+
+/* Put/get pixel to/from internal LCD Controller framebuffer */
+
+static int put_rgba(rgba p, uint8_t *d)
+{
+ *(uint8_t *)d++ = p.r;
+ *(uint8_t *)d++ = p.g;
+ *(uint8_t *)d++ = p.b;
+ *(uint32_t *)d = p.a;
+ return 7;
+}
+
+static int get_rgba(uint8_t *s, rgba *p)
+{
+ p->r = *(uint8_t *)s++;
+ p->g = *(uint8_t *)s++;
+ p->b = *(uint8_t *)s++;
+ p->a = *(uint32_t *)s;
+ return 7;
+}
+
+
+/* Perform byte/halfword/word swap of data accrding to config */
+
+static inline uint64_t swap_data(const DrawConfig *cfg, uint64_t x)
+{
+ int i;
+ uint64_t res;
+
+ return x;
+
+ if (cfg->swap & BISWP) {
+ res = 0;
+ for (i = 0; i < 64; i++) {
+ if (x & (1ULL << (64 - i))) {
+ res |= (1ULL << i);
+ }
+ }
+ x = res;
+ }
+ if (cfg->swap & BYSWP) {
+ x = ((x & 0x00000000000000FFULL) << 56) |
+ ((x & 0x000000000000FF00ULL) << 40) |
+ ((x & 0x0000000000FF0000ULL) << 24) |
+ ((x & 0x00000000FF000000ULL) << 8) |
+ ((x & 0x000000FF00000000ULL) >> 8) |
+ ((x & 0x0000FF0000000000ULL) >> 24) |
+ ((x & 0x00FF000000000000ULL) >> 40) |
+ ((x & 0xFF00000000000000ULL) >> 56);
+ }
+ if (cfg->swap & HWSWP) {
+ x = ((x & 0x000000000000FFFFULL) << 48) |
+ ((x & 0x00000000FFFF0000ULL) << 16) |
+ ((x & 0x0000FFFF00000000ULL) >> 16) |
+ ((x & 0xFFFF000000000000ULL) >> 48);
+ }
+ if (cfg->swap & WSWP) {
+ x = ((x & 0x00000000FFFFFFFFULL) << 32) |
+ ((x & 0xFFFFFFFF00000000ULL) >> 32);
+ }
+ return x;
+}
+
+
+/* Coefficient extraction functions */
+
+static uint32_t coef_zero(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0;
+}
+
+static uint32_t coef_one(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF;
+}
+
+static uint32_t coef_alphaa(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->fg_pixel_blending) {
+ pa.a = cfg->fg_alpha_sel;
+ }
+ if (cfg->fg_alpha_pix) {
+ return pa.a;
+ } else {
+ return cfg->fg_alpha[pa.a];
+ }
+}
+
+static uint32_t coef_one_minus_alphaa(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->fg_pixel_blending) {
+ pa.a = cfg->fg_alpha_sel;
+ }
+ if (cfg->fg_alpha_pix) {
+ return 0xFFFFFF - pa.a;
+ } else {
+ return 0xFFFFFF - cfg->fg_alpha[pa.a];
+ }
+}
+
+static uint32_t coef_alphab(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->bg_pixel_blending) {
+ pb.a = cfg->bg_alpha_sel;
+ }
+ if (cfg->bg_alpha_pix) {
+ return pb.a;
+ } else {
+ return cfg->bg_alpha[pb.a];
+ }
+}
+
+static uint32_t coef_one_minus_alphab(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->bg_pixel_blending) {
+ pb.a = cfg->bg_alpha_sel;
+ }
+ if (cfg->bg_alpha_pix) {
+ return 0xFFFFFF - pb.a;
+ } else {
+ return 0xFFFFFF - cfg->bg_alpha[pb.a];
+ }
+}
+
+static uint32_t coef_a(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return (pa.r << 16) | (pa.g << 8) | pa.b;
+}
+
+static uint32_t coef_one_minus_a(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF - ((pa.r << 16) | (pa.g << 8) | pa.b);
+}
+
+static uint32_t coef_b(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return (pb.r << 16) | (pb.g << 8) | pb.b;
+}
+
+static uint32_t coef_one_minus_b(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF - ((pb.r << 16) | (pb.g << 8) | pb.b);
+}
+
+
+/* Blending functions */
+
+static void blend_alpha(const DrawConfig *cfg,
+ rgba p_bg, rgba p_fg, rgba *res)
+{
+ uint32_t pl, ql, al, bl;
+ uint32_t p, q, a, b, fg, bg, fga, bga;
+
+ pl = cfg->coef_p(cfg, p_fg, p_bg);
+ ql = cfg->coef_q(cfg, p_fg, p_bg);
+ al = cfg->coef_a(cfg, p_fg, p_bg);
+ bl = cfg->coef_b(cfg, p_fg, p_bg);
+ res->a = 0;
+ /* B */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.b;
+ bg = p_bg.b;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = p_fg.a & 0xFF;
+ } else {
+ fga = cfg->fg_alpha[p_fg.a] & 0xFF;
+ }
+ } else {
+ fga = cfg->fg_alpha[cfg->fg_alpha_sel] & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = p_bg.a & 0xFF;
+ } else {
+ bga = cfg->bg_alpha[p_bg.a] & 0xFF;
+ }
+ } else {
+ bga = cfg->bg_alpha[cfg->bg_alpha_sel] & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->b = 0xFF;
+ } else {
+ res->b = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF;
+ } else {
+ res->a |= bga;
+ }
+ /* G */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.g;
+ bg = p_bg.g;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = (p_fg.a >> 8) & 0xFF;
+ } else {
+ fga = (cfg->fg_alpha[p_fg.a] >> 8) & 0xFF;
+ }
+ } else {
+ fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 8) & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = (p_bg.a >> 8) & 0xFF;
+ } else {
+ bga = (cfg->bg_alpha[p_bg.a] >> 8) & 0xFF;
+ }
+ } else {
+ bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 8) & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->g = 0xFF;
+ } else {
+ res->g = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF << 8;
+ } else {
+ res->a |= bga << 8;
+ }
+ /* R */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.r;
+ bg = p_bg.r;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = (p_fg.a >> 16) & 0xFF;
+ } else {
+ fga = (cfg->fg_alpha[p_fg.a] >> 16) & 0xFF;
+ }
+ } else {
+ fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 16) & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = (p_bg.a >> 16) & 0xFF;
+ } else {
+ bga = (cfg->bg_alpha[p_bg.a] >> 16) & 0xFF;
+ }
+ } else {
+ bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 16) & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->r = 0xFF;
+ } else {
+ res->r = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF << 16;
+ } else {
+ res->a |= bga << 16;
+ }
+}
+
+static void blend_colorkey(DrawConfig *cfg,
+ rgba p_bg, rgba p_fg, rgba *p)
+{
+ uint8_t r, g, b;
+
+ if (cfg->color_ctl & 2) {
+ blend_alpha(cfg, p_bg, p_fg, p);
+ return ;
+ }
+ r = ((cfg->color_key & ~cfg->color_mask) >> 16) & 0xFF;
+ g = ((cfg->color_key & ~cfg->color_mask) >> 8) & 0xFF;
+ b = ((cfg->color_key & ~cfg->color_mask) >> 0) & 0xFF;
+ if (cfg->color_ctl & 1) {
+ if ((p_fg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+ (p_fg.g & ~((cfg->color_mask >> 8) & 0xFF)) == g &&
+ (p_fg.b & ~((cfg->color_mask >> 0) & 0xFF)) == b) {
+ if (cfg->color_ctl & 4) {
+ p_fg.a = 1;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_bg;
+ }
+ } else {
+ if (cfg->color_ctl & 4) {
+ p_fg.a = 0;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_fg;
+ }
+ }
+ } else {
+ if ((p_bg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+ (p_bg.g & ~((cfg->color_mask >> 8) & 0xFF)) == g &&
+ (p_bg.b & ~((cfg->color_mask >> 0) & 0xFF)) == b) {
+ if (cfg->color_ctl & 4) {
+ p_fg.a = 1;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_fg;
+ }
+ } else {
+ if (cfg->color_ctl & 4) {
+ p_fg.a = 0;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_bg;
+ }
+ }
+ }
+}
+
+
+/* Draw line functions */
+
+#define DEF_DRAW_LINE(N) \
+static void glue(draw_line, N)(DrawConfig *cfg, uint8_t *src, \
+ uint8_t *dst, uint8_t *ifb) \
+{ \
+ rgba p, p_old; \
+ uint64_t data; \
+ int width = cfg->width; \
+ int i; \
+ do { \
+ data = ldq_raw((void *)src); \
+ src += 8; \
+ data = swap_data(cfg, data); \
+ for (i = 0; i < (64 / (N)); i++) { \
+ cfg->pixel_to_rgb(cfg->is_palletized ? \
+ cfg->palette[data & ((1ULL << (N)) - 1)] : \
+ data & ((1ULL << (N)) - 1), &p); \
+ if (cfg->blend) { \
+ ifb += cfg->get_pixel(ifb, &p_old); \
+ cfg->blend(cfg, p_old, p, &p); \
+ } \
+ dst += cfg->put_pixel(p, dst); \
+ data >>= (N); \
+ } \
+ width -= (64 / (N)); \
+ } while (width > 0); \
+}
+
+DEF_DRAW_LINE(1)
+DEF_DRAW_LINE(2)
+DEF_DRAW_LINE(4)
+DEF_DRAW_LINE(8)
+DEF_DRAW_LINE(16)
+DEF_DRAW_LINE(32)
+
+static void draw_line_copy(DrawConfig *cfg, uint8_t *src, uint8_t *dst,
+ uint8_t *ifb)
+{
+ rgba p;
+ int width = cfg->width;
+
+ do {
+ src += cfg->get_pixel(src, &p);
+ dst += cfg->put_pixel(p, dst);
+ width--;
+ } while (width > 0);
+}
+
+
+/* LCD Functions */
+
+static void s5pc1xx_lcd_update_irq(S5pc1xxLcdState *s)
+{
+ if (!(s->vidintcon[0] & 1)) {
+ qemu_irq_lower(s->irq[0]);
+ qemu_irq_lower(s->irq[1]);
+ qemu_irq_lower(s->irq[2]);
+ return;
+ }
+ if ((s->vidintcon[0] & 2) && (s->vidintcon[1] & 1)) {
+ qemu_irq_raise(s->irq[0]);
+ } else {
+ qemu_irq_lower(s->irq[0]);
+ }
+ if ((s->vidintcon[0] & (1 << 12)) && (s->vidintcon[1] & 2)) {
+ qemu_irq_raise(s->irq[1]);
+ } else {
+ qemu_irq_lower(s->irq[1]);
+ }
+ if ((s->vidintcon[0] & (1 << 17)) && (s->vidintcon[1] & 4)) {
+ qemu_irq_raise(s->irq[2]);
+ } else {
+ qemu_irq_lower(s->irq[2]);
+ }
+}
+
+static void s5pc1xx_lcd_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ int w, i;
+
+ if (offset & 3) {
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ switch (offset) {
+ case 0x000 ... 0x008:
+ s->vidcon[(offset - 0x000) >> 2] = val;
+ break;
+ case 0x00C:
+ s->prtcon = val;
+ break;
+ case 0x010 ... 0x018:
+ s->vidtcon[(offset - 0x010) >> 2] = val;
+ break;
+ case 0x020 ... 0x030:
+ s->window[(offset - 0x020) >> 2].wincon = val;
+ break;
+ case 0x034:
+ s->shadowcon = val;
+ break;
+ case 0x040 ... 0x088:
+ w = (offset - 0x040) >> 4;
+ i = ((offset - 0x040) & 0xF) >> 2;
+ if (i < 2) {
+ s->window[w].vidosd[i] = val;
+ } else if (i == 3) {
+ if (w != 1 && w != 2) {
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ } else {
+ s->window[w].vidosd[i] = val;
+ }
+ } else {
+ if (w == 0) {
+ i++;
+ }
+ s->window[w].vidosd[i] = val;
+ }
+ break;
+ case 0x0A0 ... 0x0C0:
+ w = (offset - 0x0A0) >> 3;
+ i = ((offset - 0x0A0) >> 2) & 1;
+ if (i == 1 && w >= 2) {
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ s->window[w].buf_start[i] = val;
+ break;
+ case 0x0D0 ... 0x0F0:
+ w = (offset - 0x0D0) >> 3;
+ i = ((offset - 0x0D0) >> 2) & 1;
+ if (i == 1 && w >= 2) {
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ s->window[w].buf_end[i] = val;
+ break;
+ case 0x100 ... 0x110:
+ s->window[(offset - 0x100) >> 2].buf_size = val;
+ break;
+ case 0x118 ... 0x11C:
+ s->vp1tcon[(offset - 0x118)] = val;
+ break;
+ case 0x130:
+ s->vidintcon[0] = val;
+ case 0x134:
+ s->vidintcon[1] &= ~(val & 7);
+ s5pc1xx_lcd_update_irq(s);
+ break;
+ case 0x140 ... 0x15C:
+ w = ((offset - 0x140) >> 3) + 1;
+ i = ((offset - 0x140) >> 2) & 1;
+ s->window[w].keycon[i] = val;
+ break;
+ case 0x170:
+ s->dithcon = val;
+ break;
+ case 0x180 ... 0x190:
+ s->window[(offset - 0x180) >> 2].winmap = val;
+ break;
+ case 0x19C ... 0x1A0:
+ s->wpalcon[(offset - 0x19C) >> 2] = val;
+ break;
+ case 0x1A4:
+ s->trigcon = val;
+ break;
+ case 0x1A8:
+ s->ituifcon = val;
+ break;
+ case 0x1B0 ... 0x1BC:
+ s->i80ifcon[(offset - 0x1B0) >> 2] = val;
+ break;
+ case 0x1D0 ... 0x1D4:
+ s->ldi_cmdcon[(offset - 0x1D0) >> 2] = val;
+ break;
+ case 0x1E0 ... 0x1E8:
+ i = (offset - 0x1E0) >> 2;
+ if (i == 2) {
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ s->sifccon[i] = val;
+ break;
+ case 0x200 ... 0x224:
+ w = ((offset - 0x200) >> 3);
+ i = ((offset - 0x200) >> 2) & 1;
+ s->window[w].vidw_alpha[i] = val;
+ break;
+ case 0x244 ... 0x250:
+ s->window[(offset - 0x244) >> 2].blendeq = val;
+ break;
+ case 0x260:
+ s->blendcon = val;
+ break;
+ case 0x27C:
+ s->dualrgb = val;
+ break;
+ case 0x280 ... 0x2AC:
+ s->ldi_cmd[(offset - 0x280) >> 2] = val;
+ break;
+ case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
+ w = (offset - 0x2400) >> 10;
+ i = ((offset - 0x2400) >> 2) & 0xFF;
+ s->window[w].palette[i] = val;
+ break;
+ default:
+ hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static uint32_t s5pc1xx_lcd_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ int w, i;
+
+ if (offset & 3) {
+ hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ switch (offset) {
+ case 0x000 ... 0x008:
+ return s->vidcon[(offset - 0x000) >> 2];
+ case 0x00C:
+ return s->prtcon;
+ case 0x010 ... 0x018:
+ return s->vidtcon[(offset - 0x010) >> 2];
+ case 0x020 ... 0x030:
+ return s->window[(offset - 0x020) >> 2].wincon;
+ case 0x034:
+ return s->shadowcon;
+ case 0x040 ... 0x088:
+ w = (offset - 0x040) >> 4;
+ i = ((offset - 0x040) & 0xF) >> 2;
+ if (i < 2) {
+ return s->window[w].vidosd[i];
+ } else if (i == 3) {
+ if (w != 1 && w != 2) {
+ hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ } else {
+ return s->window[w].vidosd[i];
+ }
+ } else {
+ if (w == 0) {
+ i++;
+ }
+ return s->window[w].vidosd[i];
+ }
+ case 0x0A0 ... 0x0C0:
+ w = (offset - 0x0A0) >> 3;
+ i = ((offset - 0x0A0) >> 2) & 1;
+ if (i == 1 && w >= 2) {
+ hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ return s->window[w].buf_start[i];
+ case 0x0D0 ... 0x0F0:
+ w = (offset - 0x0D0) >> 3;
+ i = ((offset - 0x0D0) >> 2) & 1;
+ if (i == 1 && w >= 2) {
+ hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ return s->window[w].buf_end[i];
+ case 0x100 ... 0x110:
+ return s->window[(offset - 0x100) >> 2].buf_size;
+ case 0x118 ... 0x11C:
+ return s->vp1tcon[(offset - 0x118)];
+ case 0x130 ... 0x134:
+ return s->vidintcon[(offset - 0x130) >> 2];
+ case 0x140 ... 0x15C:
+ w = ((offset - 0x140) >> 3) + 1;
+ i = ((offset - 0x140) >> 2) & 1;
+ return s->window[w].keycon[i];
+ case 0x170:
+ return s->dithcon;
+ case 0x180 ... 0x190:
+ return s->window[(offset - 0x180) >> 2].winmap;
+ case 0x19C ... 0x1A0:
+ return s->wpalcon[(offset - 0x19C) >> 2];
+ case 0x1A4:
+ return s->trigcon;
+ case 0x1A8:
+ return s->ituifcon;
+ case 0x1B0 ... 0x1BC:
+ return s->i80ifcon[(offset - 0x1B0) >> 2];
+ case 0x1D0 ... 0x1D4:
+ return s->ldi_cmdcon[(offset - 0x1D0) >> 2];
+ case 0x1E0 ... 0x1E8:
+ i = (offset - 0x1E0) >> 2;
+ return s->sifccon[i];
+ case 0x200 ... 0x224:
+ w = ((offset - 0x200) >> 3);
+ i = ((offset - 0x200) >> 2) & 1;
+ return s->window[w].vidw_alpha[i];
+ case 0x244 ... 0x250:
+ return s->window[(offset - 0x244) >> 2].blendeq;
+ case 0x260:
+ return s->blendcon;
+ case 0x27C:
+ return s->dualrgb;
+ case 0x280 ... 0x2AC:
+ return s->ldi_cmd[(offset - 0x280) >> 2];
+ case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
+ w = (offset - 0x2400) >> 10;
+ i = ((offset - 0x2400) >> 2) & 0xFF;
+ return s->window[w].palette[i];
+ default:
+ hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc *s5pc1xx_lcd_readfn[] = {
+ s5pc1xx_lcd_read,
+ s5pc1xx_lcd_read,
+ s5pc1xx_lcd_read
+};
+
+static CPUWriteMemoryFunc *s5pc1xx_lcd_writefn[] = {
+ s5pc1xx_lcd_write,
+ s5pc1xx_lcd_write,
+ s5pc1xx_lcd_write
+};
+
+static void s5pc1xx_update_resolution(S5pc1xxLcdState *s)
+{
+ uint32_t width, height;
+ /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
+ width = (s->vidtcon[2] & 0x7FF) + 1;
+ height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+ if (s->ifb == NULL || ds_get_width(s->console) != width ||
+ ds_get_height(s->console) != height) {
+
+ qemu_console_resize(s->console, width, height);
+ s->ifb = qemu_realloc(s->ifb, width * height * 7);
+ s->valid_line =
+ qemu_realloc(s->valid_line, (height >> 3) * sizeof(uint8_t));
+ s->valid_line_prev =
+ qemu_realloc(s->valid_line_prev, (height >> 3) * sizeof(uint8_t));
+ memset(s->ifb, 0, width * height * 7);
+ s->invalidate = 1;
+ }
+}
+
+/* Returns WxPAL for given window number WINDOW */
+static uint32_t s5pc1xx_wxpal(S5pc1xxLcdState *s, int window)
+{
+ switch (window) {
+ case 0:
+ return s->wpalcon[1] & 0x7;
+ case 1:
+ return (s->wpalcon[1] >> 3) & 0x7;
+ case 2:
+ return ((s->wpalcon[0] >> 8) & 0x6) | ((s->wpalcon[1] >> 6) & 0x1);
+ case 3:
+ return ((s->wpalcon[0] >> 12) & 0x6) | ((s->wpalcon[1] >> 7) & 0x1);
+ case 4:
+ return ((s->wpalcon[0] >> 16) & 0x6) | ((s->wpalcon[1] >> 8) & 0x1);
+ default:
+ hw_error("s5pc1xx.lcd: incorrect window number %d\n", window);
+ }
+}
+
+/* Parse BPPMODE_F bits and setup known DRAW_CONFIG fields accordingly.
+ BPPMODE_F = WINCON1[5:2] */
+static void s5pc1xx_parse_win_bppmode(S5pc1xxLcdState *s,
+ DrawConfig *cfg, int window)
+{
+ switch ((s->window[window].wincon >> 2) & 0xF) {
+ case 0:
+ cfg->draw_line = draw_line1;
+ cfg->is_palletized = 1;
+ cfg->bpp = 1;
+ break;
+ case 1:
+ cfg->draw_line = draw_line2;
+ cfg->is_palletized = 1;
+ cfg->bpp = 2;
+ break;
+ case 2:
+ cfg->draw_line = draw_line4;
+ cfg->is_palletized = 1;
+ cfg->bpp = 4;
+ break;
+ case 3:
+ cfg->draw_line = draw_line8;
+ cfg->is_palletized = 1;
+ cfg->bpp = 8;
+ break;
+ case 4:
+ cfg->draw_line = draw_line8;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a232_to_rgb;
+ cfg->bpp = 8;
+ break;
+ case 5:
+ cfg->draw_line = draw_line16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_565_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 6:
+ cfg->draw_line = draw_line16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 7:
+ cfg->draw_line = draw_line16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_1555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 8:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_666_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 9:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a665_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 10:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a666_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 11:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_888_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 12:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a887_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 13:
+ cfg->draw_line = draw_line32;
+ cfg->is_palletized = 0;
+ if ((s->window[window].wincon & (1 << 6)) &&
+ (s->window[window].wincon & 2)) {
+ cfg->pixel_to_rgb = pixel_8888_to_rgb;
+ cfg->fg_alpha_pix = 1;
+ } else {
+ cfg->pixel_to_rgb = pixel_a888_to_rgb;
+ }
+ cfg->bpp = 32;
+ break;
+ case 14:
+ cfg->draw_line = draw_line16;
+ cfg->is_palletized = 0;
+ if ((s->window[window].wincon & (1 << 6)) &&
+ (s->window[window].wincon & 2)) {
+ cfg->pixel_to_rgb = pixel_4444_to_rgb;
+ cfg->fg_alpha_pix = 1;
+ } else {
+ cfg->pixel_to_rgb = pixel_a444_to_rgb;
+ }
+ cfg->bpp = 16;
+ break;
+ case 15:
+ cfg->draw_line = draw_line16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ }
+}
+
+pixel_to_rgb_func *wxpal_to_rgb[8] = {
+ [0] = pixel_565_to_rgb,
+ [1] = pixel_a555_to_rgb,
+ [2] = pixel_666_to_rgb,
+ [3] = pixel_a665_to_rgb,
+ [4] = pixel_a666_to_rgb,
+ [5] = pixel_888_to_rgb,
+ [6] = pixel_a888_to_rgb,
+ [7] = pixel_8888_to_rgb
+};
+
+static inline uint32_t unpack_by_4(uint32_t x)
+{
+ return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
+}
+
+static coef_func *coef_decode(uint32_t x)
+{
+ switch (x) {
+ case 0:
+ return coef_zero;
+ case 1:
+ return coef_one;
+ case 2:
+ return coef_alphaa;
+ case 3:
+ return coef_one_minus_alphaa;
+ case 4:
+ return coef_alphab;
+ case 5:
+ return coef_one_minus_alphab;
+ case 10:
+ return coef_a;
+ case 11:
+ return coef_one_minus_a;
+ case 12:
+ return coef_b;
+ case 13:
+ return coef_one_minus_b;
+ default:
+ hw_error("s5pc1xx.lcd: illegal value\n");
+ }
+}
+
+static inline void putpixel_by_bpp(DrawConfig *cfg, int bpp)
+{
+ switch (bpp) {
+ case 8:
+ cfg->put_pixel = put_pixel8;
+ break;
+ case 15:
+ cfg->put_pixel = put_pixel15;
+ break;
+ case 16:
+ cfg->put_pixel = put_pixel16;
+ break;
+ case 24:
+ cfg->put_pixel = put_pixel24;
+ break;
+ case 32:
+ cfg->put_pixel = put_pixel32;
+ break;
+ default:
+ hw_error("s5pc1xx.lcd: unsupported BPP (%d)", bpp);
+ }
+}
+
+static void s5pc1xx_lcd_update(void *opaque)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ DrawConfig cfg;
+ int i, dirty[2], x;
+ int line;
+ target_phys_addr_t scanline, newline, map_len, pd, inc_size;
+ uint8_t *mapline, *startline, *valid_line_tmp;
+ int lefttop_x, lefttop_y, rightbottom_x, rightbottom_y;
+ int ext_line_size;
+ int width, height;
+ uint32_t tmp;
+ int buf_id;
+ int need_redraw;
+ int global_width, global_height;
+ int bpp;
+ uint8_t *d;
+ uint8_t is_first_window;
+
+ if (!s || !s->console || !ds_get_bits_per_pixel(s->console)) {
+ return;
+ }
+
+ if (! (s->vidcon[0] & 2)) {
+ return;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ s5pc1xx_update_resolution(s);
+
+ /* First we will mark lines of the display which need to be redrawn */
+ memset(s->valid_line, 0xFF,
+ ((((s->vidtcon[2] >> 11) & 0x7FF) + 1 + 7) >> 3) * sizeof(uint8_t));
+ for (i = 0; i < 5; i++) {
+ if (s->window[i].wincon & 1) {
+ lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
+ lefttop_y = (s->window[i].vidosd[0] >> 0) & 0x7FF;
+ rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
+ rightbottom_y = (s->window[i].vidosd[1] >> 0) & 0x7FF;
+ height = rightbottom_y - lefttop_y + 1;
+ width = rightbottom_x - lefttop_x + 1;
+ ext_line_size = s->window[i].buf_size & 0x1FFF;
+ buf_id = 0;
+ if (i <= 1) {
+ buf_id = (s->window[i].wincon >> 20) & 1;
+ }
+ /* According to documentation framebuffer is always located in
+ single bank of DRAM. Bits [31:24] of BUF_START encode bank
+ number, and [23:0] - address of the buffer in bank. We will
+ assume that DRAM Controller uses linear memory mapping so
+ BUF_START will be just address of the framebuffer. In the
+ other case framebuffer will be dispersed all over the system
+ memory so it is unclear how such system will work.
+
+ Moreover, we will ignore absence of carry bit bitween bits 23
+ and 24 while incrementing address in the hope that no
+ programmer will use such hack. */
+ scanline = s->window[i].buf_start[buf_id];
+ inc_size = (s->window[i].buf_size & 0x1FFF) +
+ ((s->window[i].buf_size >> 13) & 0x1FFF);
+ cpu_physical_sync_dirty_bitmap(scanline,
+ scanline + height * inc_size);
+ pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
+ (scanline & ~TARGET_PAGE_MASK);
+ dirty[0] = dirty[1] =
+ cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
+ cpu_physical_memory_reset_dirty(scanline, scanline, VGA_DIRTY_FLAG);
+ for (line = 0; line < height; line++) {
+ newline = scanline + ext_line_size;
+ for (x = scanline;
+ x < newline;
+ x += TARGET_PAGE_SIZE) {
+ pd = (cpu_get_physical_page_desc(x) & TARGET_PAGE_MASK) +
+ (x & ~TARGET_PAGE_MASK);
+ dirty[1] = cpu_physical_memory_get_dirty(pd, VGA_DIRTY_FLAG);
+ dirty[0] |= dirty[1];
+ }
+ if (dirty[0]) {
+ tmp = line + lefttop_y;
+ s->valid_line[tmp >> 3] &= ~(1 << (tmp & 0x7));
+ }
+ dirty[0] = dirty[1] = 0;
+ scanline += (s->window[i].buf_size & 0x1FFF) +
+ ((s->window[i].buf_size >> 13) & 0x1FFF);
+ }
+ scanline = s->window[i].buf_start[buf_id];
+ pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
+ (scanline & ~TARGET_PAGE_MASK);
+ cpu_physical_memory_reset_dirty(pd, pd + inc_size * height,
+ VGA_DIRTY_FLAG);
+ }
+ }
+
+ need_redraw = 0;
+ is_first_window = 1;
+ for (i = 0; i < 5; i++) {
+ if (s->window[i].wincon & 1) {
+ cfg.fg_alpha_pix = 0;
+ s5pc1xx_parse_win_bppmode(s, &cfg, i);
+ /* If we have mode with palletized color then we need to parse
+ palette color mode and set pixel-to-rgb conversion function
+ accordingly. */
+ if (cfg.is_palletized) {
+ tmp = s5pc1xx_wxpal(s, i);
+ /* Different windows have different mapping WxPAL to palette
+ pixel format. This transform happens to unify them all. */
+ if (i < 2 && tmp < 7) {
+ tmp = 6 - tmp;
+ }
+ cfg.pixel_to_rgb = wxpal_to_rgb[tmp];
+ if (tmp == 7) {
+ cfg.fg_alpha_pix = 1;
+ }
+ }
+ cfg.put_pixel = put_rgba;
+ cfg.get_pixel = get_rgba;
+ cfg.bg_alpha_pix = 1;
+ cfg.color_mask = s->window[i].keycon[0] & 0xFFFFFF;
+ cfg.color_key = s->window[i].keycon[1];
+ cfg.color_ctl = (s->window[i].keycon[0] >> 24) & 7;
+ if (i == 0) {
+ cfg.fg_alpha[0] = s->window[i].vidw_alpha[0];
+ cfg.fg_alpha[1] = s->window[i].vidw_alpha[1];
+ } else {
+ cfg.fg_alpha[0] =
+ unpack_by_4((s->window[i].vidosd[3] & 0xFFF000) >> 12) |
+ (s->window[i].vidw_alpha[0] & 0xF0F0F);
+ cfg.fg_alpha[1] =
+ unpack_by_4(s->window[i].vidosd[3] & 0xFFF) |
+ (s->window[i].vidw_alpha[0] & 0xF0F0F);
+ }
+ cfg.bg_pixel_blending = 1;
+ cfg.fg_pixel_blending = s->window[i].wincon & (1 << 6);
+ cfg.fg_alpha_sel = (s->window[i].wincon >> 1) & 1;
+ cfg.palette = s->window[i].palette;
+ cfg.swap = (s->window[i].wincon >> 15) & 0xF;
+ cfg.coef_q = coef_decode((s->window[i].blendeq >> 18) & 0xF);
+ cfg.coef_p = coef_decode((s->window[i].blendeq >> 12) & 0xF);
+ cfg.coef_b = coef_decode((s->window[i].blendeq >> 6) & 0xF);
+ cfg.coef_a = coef_decode((s->window[i].blendeq >> 0) & 0xF);
+ if (is_first_window) {
+ cfg.blend = NULL;
+ } else {
+ cfg.blend = blend_colorkey;
+ }
+ is_first_window = 0;
+ /* At this point CFG is fully set up except WIDTH. We can proceed
+ with drawing. */
+ lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
+ lefttop_y = (s->window[i].vidosd[0] >> 0) & 0x7FF;
+ rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
+ rightbottom_y = (s->window[i].vidosd[1] >> 0) & 0x7FF;
+ height = rightbottom_y - lefttop_y + 1;
+ width = rightbottom_x - lefttop_x + 1;
+ cfg.width = width;
+ ext_line_size = (width * cfg.bpp) >> 3;
+ buf_id = 0;
+ if (i <= 1) {
+ buf_id = (s->window[i].wincon >> 20) & 1;
+ }
+ scanline = s->window[i].buf_start[buf_id];
+ global_width = (s->vidtcon[2] & 0x7FF) + 1;
+ global_height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+ /* See comment above about DRAM Controller memory mapping. */
+ map_len = ((s->window[i].buf_size & 0x1FFF) +
+ ((s->window[i].buf_size >> 13) & 0x1FFF)) * height;
+ mapline = cpu_physical_memory_map(scanline, &map_len, 0);
+ if (!mapline) {
+ return;
+ }
+ startline = mapline;
+ for (line = 0; line < height; line++) {
+ tmp = line + lefttop_y;
+ if (s->invalidate ||
+ !(s->valid_line[tmp >> 3] & (1 << (tmp & 7))) ||
+ !(s->valid_line_prev[tmp >> 3] & (1 << (tmp & 7)))) {
+ need_redraw = 1;
+ cfg.draw_line(&cfg, mapline,
+ s->ifb + lefttop_x * 7 +
+ (lefttop_y + line) * global_width * 7,
+ s->ifb + lefttop_x * 7 +
+ (lefttop_y + line) * global_width * 7);
+ }
+ mapline += (s->window[i].buf_size & 0x1FFF) +
+ ((s->window[i].buf_size >> 13) & 0x1FFF);
+ }
+ cpu_physical_memory_unmap(startline, map_len, 0, 0);
+ }
+ }
+ /* Last pass: copy resulting image to QEMU_CONSOLE. */
+ if (need_redraw) {
+ width = (s->vidtcon[2] & 0x7FF) + 1;
+ height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+ cfg.get_pixel = get_rgba;
+ bpp = ds_get_bits_per_pixel(s->console);
+ putpixel_by_bpp(&cfg, bpp);
+ bpp = (bpp + 1) >> 3;
+ d = ds_get_data(s->console);
+ for (line = 0; line < height; line++) {
+ draw_line_copy(&cfg, s->ifb + width * line * 7,
+ d + width * line * bpp, NULL);
+ }
+ dpy_update(s->console, 0, 0, width, height);
+ }
+ valid_line_tmp = s->valid_line;
+ s->valid_line = s->valid_line_prev;
+ s->valid_line_prev = valid_line_tmp;
+ s->invalidate = 0;
+ s->vidintcon[1] |= 2;
+ s5pc1xx_lcd_update_irq(s);
+}
+
+static void s5pc1xx_lcd_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ int i, j, width, height;
+
+ width = (s->vidtcon[2] & 0x7FF) + 1;
+ height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+
+ qemu_put_be32s(f, &s->shadowcon);
+ qemu_put_be32s(f, &s->prtcon);
+ qemu_put_be32s(f, &s->dithcon);
+ qemu_put_be32s(f, &s->trigcon);
+ qemu_put_be32s(f, &s->ituifcon);
+ qemu_put_be32s(f, &s->blendcon);
+ qemu_put_be32s(f, &s->dualrgb);
+
+ for (i = 0; i < 2; i++) {
+ qemu_put_be32s(f, &s->vp1tcon[i]);
+ qemu_put_be32s(f, &s->vidintcon[i]);
+ qemu_put_be32s(f, &s->wpalcon[i]);
+ qemu_put_be32s(f, &s->ldi_cmdcon[i]);
+ }
+
+ for (i = 0; i < 3; i++) {
+ qemu_put_be32s(f, &s->vidcon[i]);
+ qemu_put_be32s(f, &s->vidtcon[i]);
+ qemu_put_be32s(f, &s->sifccon[i]);
+ }
+
+ for (i = 0; i < 4; i++) {
+ qemu_put_be32s(f, &s->i80ifcon[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_put_be32s(f, &s->ldi_cmd[i]);
+ }
+
+ //qemu_put_buffer(f, s->ifb, width * height * 7);
+
+ /* Not saving valid_line because the whole screen will be redrawn anyway */
+
+ for (i = 0; i < 5; i++) {
+ qemu_put_be32s(f, &s->window[i].wincon);
+ qemu_put_be32s(f, &s->window[i].buf_size);
+ qemu_put_be32s(f, &s->window[i].winmap);
+ qemu_put_be32s(f, &s->window[i].blendeq);
+
+ for (j = 0; j < 2; j++) {
+ qemu_put_be32s(f, &s->window[i].buf_start[j]);
+ qemu_put_be32s(f, &s->window[i].buf_end[j]);
+ qemu_put_be32s(f, &s->window[i].keycon[j]);
+ qemu_put_be32s(f, &s->window[i].vidw_alpha[j]);
+ }
+
+ for (j = 0; j < 4; j++) {
+ qemu_put_be32s(f, &s->window[i].vidosd[j]);
+ }
+
+ for (j = 0; j < 256; j++) {
+ qemu_put_be32s(f, &s->window[i].palette[j]);
+ }
+ }
+}
+
+static int s5pc1xx_lcd_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ int i, j, width, height;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->shadowcon);
+ qemu_get_be32s(f, &s->prtcon);
+ qemu_get_be32s(f, &s->dithcon);
+ qemu_get_be32s(f, &s->trigcon);
+ qemu_get_be32s(f, &s->ituifcon);
+ qemu_get_be32s(f, &s->blendcon);
+ qemu_get_be32s(f, &s->dualrgb);
+
+ for (i = 0; i < 2; i++) {
+ qemu_get_be32s(f, &s->vp1tcon[i]);
+ qemu_get_be32s(f, &s->vidintcon[i]);
+ qemu_get_be32s(f, &s->wpalcon[i]);
+ qemu_get_be32s(f, &s->ldi_cmdcon[i]);
+ }
+
+ for (i = 0; i < 3; i++) {
+ qemu_get_be32s(f, &s->vidcon[i]);
+ qemu_get_be32s(f, &s->vidtcon[i]);
+ qemu_get_be32s(f, &s->sifccon[i]);
+ }
+
+ width = (s->vidtcon[2] & 0x7FF) + 1;
+ height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
+
+ for (i = 0; i < 4; i++) {
+ qemu_get_be32s(f, &s->i80ifcon[i]);
+ }
+
+ for (i = 0; i < 12; i++) {
+ qemu_get_be32s(f, &s->ldi_cmd[i]);
+ }
+
+ //qemu_get_buffer(f, s->ifb, width * height * 7);
+
+ for (i = 0; i < 5; i++) {
+ qemu_get_be32s(f, &s->window[i].wincon);
+ qemu_get_be32s(f, &s->window[i].buf_size);
+ qemu_get_be32s(f, &s->window[i].winmap);
+ qemu_get_be32s(f, &s->window[i].blendeq);
+
+ for (j = 0; j < 2; j++) {
+ qemu_get_be32s(f, &s->window[i].buf_start[j]);
+ qemu_get_be32s(f, &s->window[i].buf_end[j]);
+ qemu_get_be32s(f, &s->window[i].keycon[j]);
+ qemu_get_be32s(f, &s->window[i].vidw_alpha[j]);
+ }
+
+ for (j = 0; j < 4; j++) {
+ qemu_get_be32s(f, &s->window[i].vidosd[j]);
+ }
+
+ for (j = 0; j < 256; j++) {
+ qemu_get_be32s(f, &s->window[i].palette[j]);
+ }
+ }
+
+ s5pc1xx_update_resolution(s);
+ /* Redraw the whole screen */
+ s->invalidate = 1;
+ return 0;
+}
+
+static void s5pc1xx_lcd_invalidate(void *opaque)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ s->invalidate = 1;
+}
+
+static void s5pc1xx_window_reset(S5pc1xxLcdWindow *s)
+{
+ memset(s, 0, sizeof(*s));
+ s->blendeq = 0xC2;
+}
+
+static void s5pc1xx_lcd_reset(void *opaque)
+{
+ S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
+ int i;
+
+ memset((uint8_t *)s + sizeof(SysBusDevice),
+ 0, offsetof(S5pc1xxLcdState, window));
+ for (i = 0; i < 5; i++) {
+ s5pc1xx_window_reset(&s->window[i]);
+ }
+ if (s->ifb != NULL) {
+ qemu_free(s->ifb);
+ }
+ s->ifb = NULL;
+ if (s->valid_line != NULL) {
+ qemu_free(s->valid_line);
+ }
+ s->valid_line = NULL;
+ if (s->valid_line_prev != NULL) {
+ qemu_free(s->valid_line_prev);
+ }
+ s->valid_line_prev = NULL;
+}
+
+static int s5pc1xx_lcd_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxLcdState *s = FROM_SYSBUS(S5pc1xxLcdState, dev);
+
+ s->ifb = NULL;
+ s->valid_line = NULL;
+ s->valid_line_prev = NULL;
+ s5pc1xx_lcd_reset(s);
+
+ sysbus_init_irq(dev, &s->irq[0]);
+ sysbus_init_irq(dev, &s->irq[1]);
+ sysbus_init_irq(dev, &s->irq[2]);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_lcd_readfn, s5pc1xx_lcd_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x3800, iomemtype);
+
+ s->console = graphic_console_init(s5pc1xx_lcd_update,
+ s5pc1xx_lcd_invalidate, NULL, NULL, s);
+
+ qemu_register_reset(s5pc1xx_lcd_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.lcd", -1, 1,
+ s5pc1xx_lcd_save, s5pc1xx_lcd_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_lcd_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.lcd", sizeof(S5pc1xxLcdState),
+ s5pc1xx_lcd_init);
+}
+
+device_init(s5pc1xx_lcd_register_devices)
--- /dev/null
+/*
+ * MMC controller for Samsung S5PC1XX-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ *
+ * Based on SMDK6400 MMC (hw/smdk6400/smdk_mmc.c)
+ */
+
+#include "hw.h"
+#include "sd.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_hsmmc_regs.h"
+#include "block_int.h"
+#include "sysbus.h"
+
+#include "qemu-timer.h"
+
+/*#define DEBUG_MMC*/
+
+#ifdef DEBUG_MMC
+#define DPRINTF(fmt, args...) \
+do { fprintf(stderr, "QEMU SD/MMC: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define CMD_RESPONSE (3 << 0)
+
+#define INSERTION_DELAY (get_ticks_per_sec())
+
+typedef struct S5pc1xxMMCState {
+ SysBusDevice busdev;
+
+ SDState *card;
+ uint8_t dma_transcpt;
+ uint32_t cmdarg;
+ uint32_t respcmd;
+ uint32_t response[4];
+ qemu_irq irq;
+
+ uint32_t sysad;
+ uint16_t blksize;
+ uint16_t blkcnt;
+ uint32_t argument;
+ uint16_t trnmod;
+ uint16_t cmdreg;
+ uint32_t prnsts;
+ uint8_t hostctl;
+ uint8_t pwrcon;
+ uint16_t clkcon;
+ uint8_t timeoutcon;
+ uint8_t swrst;
+ uint16_t norintsts;
+ uint16_t errintsts;
+ uint16_t norintstsen;
+ uint16_t errintstsen;
+ uint16_t norintsigen;
+ uint16_t errintsigen;
+ uint32_t control2;
+ uint32_t control3;
+
+ QEMUTimer *response_timer; /* command response. */
+ QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
+
+ qemu_irq eject;
+} S5pc1xxMMCState;
+
+
+static void mmc_dmaInt(S5pc1xxMMCState *s)
+{
+ if (s->dma_transcpt == 1)
+ s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+ else
+ s->norintsts |= S5C_HSMMC_NIS_DMA;
+ qemu_set_irq(s->irq, 1);
+}
+
+static void mmc_fifo_push(S5pc1xxMMCState *s, uint32_t pos, uint32_t value)
+{
+ cpu_physical_memory_write(s->sysad + pos, (uint8_t *)(&value), 4);
+}
+
+static uint32_t mmc_fifo_pop(S5pc1xxMMCState *s, uint32_t pos)
+{
+ uint32_t value = 0;
+
+ cpu_physical_memory_read(s->sysad + pos, (uint8_t *)(&value), 4);
+ return value;
+}
+
+static void s5pc1xx_mmc_raise_end_command_irq(void *opaque)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ DPRINTF("raise IRQ response\n");
+ qemu_irq_raise(s->irq);
+ s->norintsts |= S5C_HSMMC_NIS_CMDCMP;
+}
+
+static void mmc_send_command(S5pc1xxMMCState *s)
+{
+ SDRequest request;
+ uint8_t response[16];
+ int rlen;
+
+ s->errintsts = 0;
+ qemu_mod_timer(s->response_timer, qemu_get_clock(vm_clock));
+ if (!s->card)
+ return;
+
+ request.cmd = s->cmdreg >> 8;
+ request.arg = s->cmdarg;
+ DPRINTF("Command %d %08x\n", request.cmd, request.arg);
+ rlen = sd_do_command(s->card, &request, response);
+ if (rlen < 0)
+ goto error;
+ if ((s->cmdreg & CMD_RESPONSE) != 0) {
+#define RWORD(n) ((n >= 0 ? (response[n] << 24) : 0) \
+ | (response[n + 1] << 16) \
+ | (response[n + 2] << 8) \
+ | response[n + 3])
+
+ if (rlen == 0)
+ goto error;
+ if (rlen != 4 && rlen != 16)
+ goto error;
+ s->response[0] = RWORD(0);
+ if (rlen == 4) {
+ s->response[1] = s->response[2] = s->response[3] = 0;
+ } else {
+ s->response[0] = RWORD(11);
+ s->response[1] = RWORD(7);
+ s->response[2] = RWORD(3);
+ s->response[3] = RWORD(-1);
+ }
+ DPRINTF("Response received\n");
+#undef RWORD
+ } else {
+ DPRINTF("Command sent\n");
+ }
+ return;
+
+error:
+ DPRINTF("Timeout\n");
+ s->errintsts |= S5C_HSMMC_EIS_CMDTIMEOUT;
+}
+
+/* Transfer data between the card and the FIFO. This is complicated by
+ the FIFO holding 32-bit words and the card taking data in single byte
+ chunks. FIFO bytes are transferred in little-endian order. */
+static void mmc_fifo_run(S5pc1xxMMCState *s)
+{
+ uint32_t value;
+ int n;
+ uint32_t pos;
+ int is_read;
+ uint32_t datacnt, boundary_chk, boundary_count;
+ uint8_t dma_buf_boundary, dma_mask_flag;
+
+ is_read = (s->trnmod & S5C_HSMMC_TRNS_READ) != 0;
+
+ if (s->blkcnt != 0 && (!is_read || sd_data_ready(s->card))) {
+ n = 0;
+ value = 0;
+
+ if (s->blkcnt > 1) {
+ /* multi block */
+ if (s->norintstsen & 0x8)
+ dma_mask_flag = 1; /* DMA enable */
+ else
+ dma_mask_flag = 0; /* DMA disable */
+
+ dma_buf_boundary = (s->blksize & 0xf000) >> 12;
+ boundary_chk = 1 << (dma_buf_boundary+12);
+ boundary_count = boundary_chk - (s->sysad % boundary_chk);
+ while (s->blkcnt) {
+ datacnt = s->blksize & 0x0fff;
+ pos = 0;
+ while (datacnt) {
+ if (is_read) {
+ value |= (uint32_t)sd_read_data(s->card) << (n * 8);
+ n++;
+ if (n == 4) {
+ mmc_fifo_push(s, pos, value);
+ value = 0;
+ n = 0;
+ pos += 4;
+ }
+ } else {
+ if (n == 0) {
+ value = mmc_fifo_pop(s, pos);
+ n = 4;
+ pos += 4;
+ }
+ sd_write_data(s->card, value & 0xff);
+ value >>= 8;
+ n--;
+ }
+ datacnt--;
+ }
+ s->sysad += s->blksize & 0x0fff;
+ boundary_count -= s->blksize & 0x0fff;
+ s->blkcnt--;
+
+ if ((boundary_count == 0) && dma_mask_flag)
+ break;
+ }
+ if (s->blkcnt == 0)
+ s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+ else
+ s->norintsts |= S5C_HSMMC_NIS_DMA;
+ } else {
+ /* single block */
+ datacnt = s->blksize & 0x0fff;
+ pos = 0;
+ while (datacnt) {
+ if (is_read) {
+ value |=
+ (uint32_t)sd_read_data(s->card) << (n * 8);
+ n++;
+ if (n == 4) {
+ mmc_fifo_push(s, pos, value);
+ value = 0;
+ n = 0;
+ pos += 4;
+ }
+ } else {
+ if (n == 0) {
+ value = mmc_fifo_pop(s, pos);
+ n = 4;
+ pos += 4;
+ }
+ sd_write_data(s->card, value & 0xff);
+ value >>= 8;
+ n--;
+ }
+ datacnt--;
+ }
+ s->blkcnt--;
+ s->norintsts |= S5C_HSMMC_NIS_TRSCMP;
+ }
+ }
+}
+
+/* MMC read (byte) function */
+static uint32_t mmc_readb(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ switch (offset) {
+ case (S5C_HSMMC_RSPREG0 + 0x3):
+ return (uint8_t)(s->response[0] >> 24);
+ case (S5C_HSMMC_RSPREG1 + 0x3):
+ return (uint8_t)(s->response[1] >> 24);
+ case (S5C_HSMMC_RSPREG2 + 0x3):
+ return (uint8_t)(s->response[2] >> 24);
+ case S5C_HSMMC_HOSTCTL:
+ return s->hostctl;
+ case S5C_HSMMC_PWRCON:
+ return s->pwrcon;
+ case S5C_HSMMC_BLKGAP:
+ return 0;
+ case S5C_HSMMC_WAKCON:
+ return 0;
+ case S5C_HSMMC_TIMEOUTCON:
+ return s->timeoutcon;
+ case S5C_HSMMC_SWRST:
+ return 0;
+ default:
+ hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* MMC read (word) function */
+static uint32_t mmc_readw(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ switch (offset) {
+ case S5C_HSMMC_BLKSIZE:
+ return s->blksize;
+ case S5C_HSMMC_BLKCNT:
+ return s->blkcnt;
+ case S5C_HSMMC_TRNMOD:
+ return s->trnmod;
+ case S5C_HSMMC_CLKCON:
+ return s->clkcon;
+ case S5C_HSMMC_SWRST:
+ return s->swrst;
+ case S5C_HSMMC_NORINTSTS:
+ qemu_set_irq(s->irq, 0);
+ return s->norintsts;
+ case S5C_HSMMC_NORINTSTSEN:
+ return s->norintstsen;
+ case S5C_HSMMC_ACMD12ERRSTS:
+ return 0;
+ case S5C_HSMMC_SLOT_INT_STATUS:
+ return 0;
+ case S5C_HSMMC_HCVER:
+ return 0x2401;
+ default:
+ hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* MMC read (doubleword) function */
+static uint32_t mmc_readl(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ switch (offset) {
+ case S5C_HSMMC_SYSAD:
+ return s->sysad;
+ case S5C_HSMMC_ARGUMENT:
+ return s->cmdarg;
+ case S5C_HSMMC_RSPREG0:
+ return s->response[0];
+ case S5C_HSMMC_RSPREG1:
+ return s->response[1];
+ case S5C_HSMMC_RSPREG2:
+ return s->response[2];
+ case S5C_HSMMC_RSPREG3:
+ return s->response[3];
+ case S5C_HSMMC_PRNSTS:
+ return s->prnsts;
+ case S5C_HSMMC_NORINTSTS:
+ qemu_set_irq(s->irq, 0);
+ return (s->errintsts << 16) | (s->norintsts);
+ case S5C_HSMMC_NORINTSTSEN:
+ return (s->errintstsen << 16) | s->norintstsen;
+ case S5C_HSMMC_NORINTSIGEN:
+ return (s->errintsigen << 16) | s->norintsigen;
+ case S5C_HSMMC_CAPAREG:
+ return 0x05E80080;
+ case S5C_HSMMC_MAXCURR:
+ return 0;
+ case S5C_HSMMC_CONTROL2:
+ return s->control2;
+ case S5C_HSMMC_CONTROL3:
+ return s->control3;
+ default:
+ hw_error("s5pc1xx.mmc: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* MMC write (byte) function */
+static void mmc_writeb(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ switch (offset) {
+ case S5C_HSMMC_HOSTCTL:
+ s->hostctl = value;
+ break;
+ case S5C_HSMMC_PWRCON:
+ s->pwrcon = value;
+ break;
+ case S5C_HSMMC_TIMEOUTCON:
+ s->timeoutcon = value;
+ break;
+ case S5C_HSMMC_SWRST:
+ s->swrst = value;
+ break;
+ default:
+ hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* MMC write (word) function */
+static void mmc_writew(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ switch (offset) {
+ case S5C_HSMMC_BLKSIZE:
+ s->blksize = value;
+ break;
+ case S5C_HSMMC_BLKCNT:
+ s->blkcnt = value;
+ break;
+ case S5C_HSMMC_TRNMOD:
+ s->trnmod = value;
+ break;
+ case S5C_HSMMC_CMDREG: /* Command */
+ s->cmdreg = value;
+ mmc_send_command(s);
+ mmc_fifo_run(s);
+ if (s->errintsts)
+ s->norintsts |= S5C_HSMMC_NIS_ERR;
+ s->norintsts |= S5C_HSMMC_NIS_CMDCMP;
+ if (s->norintsts & S5C_HSMMC_NIS_TRSCMP)
+ s->norintsts &= ~S5C_HSMMC_NIS_CMDCMP;
+ if (s->norintsts & S5C_HSMMC_NIS_DMA)
+ s->norintsts &= ~S5C_HSMMC_NIS_CMDCMP;
+ break;
+ case S5C_HSMMC_CLKCON:
+ s->clkcon = value;
+ if (S5C_HSMMC_CLOCK_INT_EN & s->clkcon)
+ s->clkcon |= S5C_HSMMC_CLOCK_INT_STABLE;
+ else
+ s->clkcon &= ~S5C_HSMMC_CLOCK_INT_STABLE;
+ if (S5C_HSMMC_CLOCK_CARD_EN & s->clkcon)
+ s->clkcon |= S5C_HSMMC_CLOCK_EXT_STABLE;
+ else
+ s->clkcon &= ~S5C_HSMMC_CLOCK_EXT_STABLE;
+ break;
+ case S5C_HSMMC_NORINTSTS:
+ s->norintsts &= ~value;
+ s->norintsts &= ~0x8100;
+ s->norintsts |= value & 0x8100;
+ break;
+ case S5C_HSMMC_NORINTSTSEN:
+ s->norintstsen = value;
+ break;
+ case S5C_HSMMC_NORINTSIGEN:
+ s->norintsigen = value;
+ break;
+ default:
+ hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* MMC write (doubleword) function */
+static void mmc_writel(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ int datacnt;
+ int is_read;
+ int value1, n;
+ uint32_t pos;
+ uint32_t boundary_chk, boundary_count;
+ uint32_t dma_buf_boundary;
+ uint8_t dma_mask_flag;
+
+ switch (offset) {
+ case S5C_HSMMC_SYSAD:
+ s->sysad = value;
+ if (s->blkcnt != 0) {
+ n = 0;
+ value1 = 0;
+ is_read = (s->trnmod & S5C_HSMMC_TRNS_READ) != 0;
+ dma_buf_boundary = (s->blksize & 0xf000) >> 12;
+ boundary_chk = 1 << (dma_buf_boundary + 12);
+
+ if (s->norintstsen & 0x8)
+ dma_mask_flag = 1; /* DMA enable */
+ else
+ dma_mask_flag = 0; /* DMA disable */
+
+ boundary_count = boundary_chk - (s->sysad % boundary_chk);
+ while (s->blkcnt) {
+ pos = 0;
+ datacnt = s->blksize & 0x0fff;
+ while (datacnt) {
+ if (is_read) {
+ value1 |= (uint32_t)sd_read_data(s->card) << (n * 8);
+ n++;
+ if (n == 4) {
+ mmc_fifo_push(s, pos, value1);
+ value1 = 0;
+ n = 0;
+ pos += 4;
+ }
+ } else {
+ if (n == 0) {
+ value1 = mmc_fifo_pop(s, pos);
+ n = 4;
+ pos += 4;
+ }
+ sd_write_data(s->card, value1 & 0xff);
+ value1 >>= 8;
+ n--;
+ }
+ datacnt--;
+ }
+ s->sysad += s->blksize & 0x0fff;
+ boundary_count -= s->blksize & 0x0fff;
+ s->blkcnt--;
+ if ((boundary_count == 0) && dma_mask_flag)
+ break;
+ }
+ if (s->blkcnt == 0) {
+ s->dma_transcpt = 1;
+ mmc_dmaInt(s);
+ } else {
+ s->dma_transcpt = 0;
+ mmc_dmaInt(s);
+ }
+ }
+ break;
+ case S5C_HSMMC_ARGUMENT:
+ s->argument = value;
+ s->cmdarg = value;
+ break;
+ case S5C_HSMMC_NORINTSTS:
+ s->norintsts &= ~value;
+ s->norintsts &= ~0x8100;
+ s->norintsts |= value & 0x8100;
+ s->errintsts &= ~(value >> 16);
+
+ if (s->errintsts == 0) {
+ s->norintsts &= ~S5C_HSMMC_NIS_ERR; /* Error Interrupt clear */
+ }
+ break;
+ case S5C_HSMMC_NORINTSTSEN:
+ s->norintstsen = (uint16_t)value;
+ s->errintstsen = (uint16_t)(value >> 16);
+ break;
+ case S5C_HSMMC_NORINTSIGEN:
+ s->norintsigen = (uint16_t)value;
+ s->errintsigen = (uint16_t)(value >> 16);
+ break;
+ case S5C_HSMMC_CONTROL2:
+ s->control2 = value;
+ break;
+ case S5C_HSMMC_CONTROL3:
+ s->control3 = value;
+ break;
+ case S5C_HSMMC_CONTROL4:
+ /* Nothing for QENU emulation */
+ break;
+ default:
+ hw_error("s5pc1xx.mmc: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *mmc_readfn[] = {
+ mmc_readb,
+ mmc_readw,
+ mmc_readl
+};
+
+static CPUWriteMemoryFunc *mmc_writefn[] = {
+ mmc_writeb,
+ mmc_writew,
+ mmc_writel
+};
+
+static void s5pc1xx_mmc_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ int i;
+
+ /* FIXME: must the structure below be saved?
+ * SDState *card; */
+
+ qemu_put_8s (f, &s->dma_transcpt);
+ qemu_put_be32s(f, &s->cmdarg);
+ qemu_put_be32s(f, &s->respcmd);
+
+ for(i = 0; i < 4; i++) {
+ qemu_put_be32s(f, &s->response[i]);
+ }
+
+ qemu_put_be32s(f, &s->sysad);
+ qemu_put_be16s(f, &s->blksize);
+ qemu_put_be16s(f, &s->blkcnt);
+ qemu_put_be32s(f, &s->argument);
+ qemu_put_be16s(f, &s->trnmod);
+ qemu_put_be16s(f, &s->cmdreg);
+ qemu_put_be32s(f, &s->prnsts);
+ qemu_put_8s (f, &s->hostctl);
+ qemu_put_8s (f, &s->pwrcon);
+ qemu_put_be16s(f, &s->clkcon);
+ qemu_put_8s (f, &s->timeoutcon);
+ qemu_put_8s (f, &s->swrst);
+ qemu_put_be16s(f, &s->norintsts);
+ qemu_put_be16s(f, &s->errintsts);
+ qemu_put_be16s(f, &s->norintstsen);
+ qemu_put_be16s(f, &s->errintstsen);
+ qemu_put_be16s(f, &s->norintsigen);
+ qemu_put_be16s(f, &s->errintsigen);
+ qemu_put_be32s(f, &s->control2);
+ qemu_put_be32s(f, &s->control3);
+
+ qemu_put_timer(f, s->response_timer);
+ qemu_put_timer(f, s->insert_timer);
+}
+
+static int s5pc1xx_mmc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ /* FIXME: SDState *card; */
+ qemu_get_8s (f, &s->dma_transcpt);
+ qemu_get_be32s(f, &s->cmdarg);
+ qemu_get_be32s(f, &s->respcmd);
+
+ for(i = 0; i < 4; i++) {
+ qemu_get_be32s(f, &s->response[i]);
+ }
+
+ qemu_get_be32s(f, &s->sysad);
+ qemu_get_be16s(f, &s->blksize);
+ qemu_get_be16s(f, &s->blkcnt);
+ qemu_get_be32s(f, &s->argument);
+ qemu_get_be16s(f, &s->trnmod);
+ qemu_get_be16s(f, &s->cmdreg);
+ qemu_get_be32s(f, &s->prnsts);
+ qemu_get_8s (f, &s->hostctl);
+ qemu_get_8s (f, &s->pwrcon);
+ qemu_get_be16s(f, &s->clkcon);
+ qemu_get_8s (f, &s->timeoutcon);
+ qemu_get_8s (f, &s->swrst);
+ qemu_get_be16s(f, &s->norintsts);
+ qemu_get_be16s(f, &s->errintsts);
+ qemu_get_be16s(f, &s->norintstsen);
+ qemu_get_be16s(f, &s->errintstsen);
+ qemu_get_be16s(f, &s->norintsigen);
+ qemu_get_be16s(f, &s->errintsigen);
+ qemu_get_be32s(f, &s->control2);
+ qemu_get_be32s(f, &s->control3);
+
+ qemu_get_timer(f, s->response_timer);
+ qemu_get_timer(f, s->insert_timer);
+
+ return 0;
+}
+
+static void mmc_reset(void *opaque)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+
+ if (s->card) {
+ s->prnsts = 0x1ff0000;
+ s->norintsts |= 0x0040;
+ } else {
+ s->prnsts = 0x1fa0000;
+ s->norintsts = 0;
+ }
+ s->cmdarg = 0;
+ s->respcmd = 0;
+ s->response[0] = 0;
+ s->response[1] = 0;
+ s->response[2] = 0;
+ s->response[3] = 0;
+}
+
+static void s5pc1xx_mmc_raise_insertion_irq(void *opaque)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ DPRINTF("raise IRQ response\n");
+
+ if (s->norintsts & S5C_HSMMC_NIS_REMOVE) {
+ DPRINTF("s5pc1xx.mmc: raise_insertion_irq - set timer!\n");
+ qemu_mod_timer(s->insert_timer,
+ qemu_get_clock(vm_clock) + INSERTION_DELAY);
+ } else {
+ DPRINTF("s5pc1xx.mmc: raise_insertion_irq - raise irq!\n");
+ s->norintsts |= S5C_HSMMC_NIS_INSERT;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static void s5pc1xx_mmc_insert_eject(void *opaque, int irq, int level)
+{
+ S5pc1xxMMCState *s = (S5pc1xxMMCState *)opaque;
+ DPRINTF("change card state: %s!\n", level ? "insert" : "eject");
+
+ if (s->norintsts & S5C_HSMMC_NIS_REMOVE) {
+ if (level) {
+ DPRINTF("change card state: timer set!\n");
+ qemu_mod_timer(s->insert_timer,
+ qemu_get_clock(vm_clock) + INSERTION_DELAY);
+ }
+ } else {
+ s->norintsts |= level ? S5C_HSMMC_NIS_INSERT : S5C_HSMMC_NIS_REMOVE;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static int s5pc1xx_mmc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxMMCState *s = FROM_SYSBUS(S5pc1xxMMCState, dev);
+ BlockDriverState *bd;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(mmc_readfn, mmc_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5C_HSMMC_REG_SIZE, iomemtype);
+
+ bd = qdev_init_bdrv(&dev->qdev, IF_SD);
+
+ if ((bd == NULL)) {
+ s->card = NULL;
+ DPRINTF("s->card = NULL\n");
+ } else {
+ s->eject = qemu_allocate_irqs(s5pc1xx_mmc_insert_eject, s, 1)[0];
+
+ DPRINTF("name = %s, sectors = %ld\n",
+ bd->device_name, bd->total_sectors);
+
+ s->card = sd_init(bd, 0);
+ sd_set_cb(s->card, NULL, s->eject);
+ }
+
+ qemu_register_reset(mmc_reset, s);
+ mmc_reset(s);
+
+ s->response_timer =
+ qemu_new_timer(vm_clock, s5pc1xx_mmc_raise_end_command_irq, s);
+
+ s->insert_timer =
+ qemu_new_timer(vm_clock, s5pc1xx_mmc_raise_insertion_irq, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.mmc", -1, 1,
+ s5pc1xx_mmc_save, s5pc1xx_mmc_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_mmc_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.mmc", sizeof(S5pc1xxMMCState),
+ s5pc1xx_mmc_init);
+}
+
+device_init(s5pc1xx_mmc_register_devices)
--- /dev/null
+/*
+ * S5PC1XX NAND controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "hw.h"
+#include "flash.h"
+#include "sysbus.h"
+
+
+#define S5PC1XX_NAND_REG_MEM_SIZE 0x44
+
+
+typedef struct S5pc1xxNFConState {
+ SysBusDevice busdev;
+
+ uint32_t config;
+ uint32_t control;
+ /* TODO: how does this status and nand chip status corellate? */
+ uint32_t status;
+
+ NANDFlashState *flash;
+} S5pc1xxNFConState;
+
+
+static uint32_t nfcon_read32(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t x, res, i;
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ switch (offset) {
+ case 0x00: /* NFCONF */
+ return s->config;
+ case 0x04: /* NFCONT */
+ return s->control;
+ case 0x08: /* NFCMMD */
+ case 0x0C: /* NFADDR */
+ return 0x0;
+ case 0x10: /* NFDATA */
+ res = 0;
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ for (i = 0; i < 4; i++) {
+ x = nand_getio(s->flash);
+ res = res | (x << (8 * i));
+ }
+ return res;
+ case 0x14: /* NFMECCD0 */
+ case 0x18: /* NFMECCD1 */
+ case 0x1C: /* NFSECCD */
+ return 0;
+ case 0x20: /* NFSBLK */
+ case 0x24: /* NFEBLK */
+ /* TODO: implement this */
+ return 0;
+ case 0x28: /* NFSTAT */
+ return s->status;
+ case 0x2C: /* NFECCERR0 */
+ case 0x30: /* NFECCERR1 */
+ case 0x34: /* NFMECC0 */
+ case 0x38: /* NFMECC1 */
+ case 0x3C: /* NFSECC */
+ case 0x40: /* NFMLCBITPT */
+ return 0;
+ default:
+ hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static uint32_t nfcon_read16(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t x, res, i;
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ if (offset == 0x10) {
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ res = 0;
+ for (i = 0; i < 2; i++) {
+ x = nand_getio(s->flash);
+ res = res | (x << (8 * i));
+ }
+ return res;
+ } else {
+ hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static uint32_t nfcon_read8(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ if (offset == 0x10) {
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ return nand_getio(s->flash);
+ } else {
+ hw_error("s5pc1xx.nand: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void nfcon_write32(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ switch (offset) {
+ case 0x00: /* NFCONF */
+ s->config = val;
+ break;
+ case 0x04: /* NFCONT */
+ s->control = val;
+ break;
+ case 0x08: /* NFCMMD */
+ case 0x0C: /* NFADDR */
+ case 0x10: /* NFDATA */
+ switch (offset) {
+ case 0x08:
+ nand_setpins(s->flash, 1, 0, 0, 1, 0);
+ break;
+ case 0x0C:
+ nand_setpins(s->flash, 0, 1, 0, 1, 0);
+ break;
+ case 0x10:
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ break;
+ }
+ nand_setio(s->flash, (val >> 0) & 0xff);
+ nand_setio(s->flash, (val >> 8) & 0xff);
+ nand_setio(s->flash, (val >> 16) & 0xff);
+ nand_setio(s->flash, (val >> 24) & 0xff);
+ break;
+ case 0x14: /* NFMECCD0 */
+ case 0x18: /* NFMECCD1 */
+ case 0x1C: /* NFSECCD */
+ break;
+ case 0x20: /* NFSBLK */
+ case 0x24: /* NFEBLK */
+ /* TODO: implement this */
+ break;
+ case 0x28: /* NFSTAT */
+ /* Ignore written value. Documentation states that this register is
+ R/W, but it describes states of the input pins. So what does write
+ to it suppose to do? */
+ break;
+ case 0x2C: /* NFECCERR0 */
+ case 0x30: /* NFECCERR1 */
+ case 0x34: /* NFMECC0 */
+ case 0x38: /* NFMECC1 */
+ case 0x3C: /* NFSECC */
+ case 0x40: /* NFMLCBITPT */
+ break;
+ default:
+ hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void nfcon_write16(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ switch (offset) {
+ case 0x08:
+ nand_setpins(s->flash, 1, 0, 0, 1, 0);
+ break;
+ case 0x0C:
+ nand_setpins(s->flash, 0, 1, 0, 1, 0);
+ break;
+ case 0x10:
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ break;
+ default:
+ hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ nand_setio(s->flash, (val >> 0) & 0xff);
+ nand_setio(s->flash, (val >> 8) & 0xff);
+}
+
+static void nfcon_write8(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ switch (offset) {
+ case 0x08:
+ nand_setpins(s->flash, 1, 0, 0, 1, 0);
+ break;
+ case 0x0C:
+ nand_setpins(s->flash, 0, 1, 0, 1, 0);
+ break;
+ case 0x10:
+ nand_setpins(s->flash, 0, 0, 0, 1, 0);
+ break;
+ default:
+ hw_error("s5pc1xx.nand: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+ nand_setio(s->flash, (val >> 0) & 0xff);
+}
+
+static CPUReadMemoryFunc * const nfcon_readfn[] = {
+ nfcon_read8,
+ nfcon_read16,
+ nfcon_read32
+};
+
+static CPUWriteMemoryFunc * const nfcon_writefn[] = {
+ nfcon_write8,
+ nfcon_write16,
+ nfcon_write32
+};
+
+static void s5pc1xx_nand_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ qemu_put_be32s(f, &s->config);
+ qemu_put_be32s(f, &s->control);
+ qemu_put_be32s(f, &s->status);
+}
+
+static int s5pc1xx_nand_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->config);
+ qemu_get_be32s(f, &s->control);
+ qemu_get_be32s(f, &s->status);
+
+ return 0;
+}
+
+static void s5pc1xx_nand_reset(void *opaque)
+{
+ S5pc1xxNFConState *s = (S5pc1xxNFConState *)opaque;
+
+ s->config = 0x00001000;
+ s->control = 0x000100C6;
+ s->status = 0xF0800F0D;
+}
+
+static int s5pc1xx_nand_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxNFConState *s = FROM_SYSBUS(S5pc1xxNFConState, dev);
+
+ s->flash = nand_init(NAND_MFR_SAMSUNG, 0xA2);
+ s5pc1xx_nand_reset(s);
+
+ iomemtype = cpu_register_io_memory(nfcon_readfn, nfcon_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_NAND_REG_MEM_SIZE, iomemtype);
+
+ qemu_register_reset(s5pc1xx_nand_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.nand", -1, 1,
+ s5pc1xx_nand_save, s5pc1xx_nand_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_nand_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.nand", sizeof(S5pc1xxNFConState),
+ s5pc1xx_nand_init);
+}
+
+device_init(s5pc1xx_nand_register_devices)
--- /dev/null
+/*
+ * S5PC1XX OneDRAM controller.
+ *
+ * Contributed by Kefeng Li <li.kefeng@samsung.com>
+ */
+
+#include "s5pc1xx_onedram.h"
+
+#define TICK_COUNTDOWN 50
+
+uint32_t sem_retry = 50;
+
+
+/* Command handler */
+static int onedram_req_active_handler(S5pc1xxOneDRAMState *s)
+{
+ uint16_t cmd = INT_COMMAND(INT_MASK_CMD_RES_ACTIVE);
+
+ onedram_send_cmd_to_pda(s, cmd);
+ return COMMAND_SUCCESS;
+}
+
+static int onedram_smp_req_handler(S5pc1xxOneDRAMState *s)
+{
+ uint16_t cmd;
+
+ onedram_put_authority(s);
+
+ cmd = INT_COMMAND(INT_MASK_CMD_SMP_REP);
+ onedram_send_cmd_to_pda(s, cmd);
+
+ return COMMAND_SUCCESS;
+}
+
+
+/* timer for waiting the semaphore for sem_retry times try */
+static void onedram_wait_semaphore(void *opaque)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+ int64_t timeout;
+
+ if(sem_retry <= 0) {
+ fprintf(stderr, "time out to wait semaphore from AP\n");
+ qemu_del_timer(s->sem_timer);
+ sem_retry = 100;
+ } else if(onedram_read_sem(s)) {
+ sem_retry--;
+ timeout = get_ticks_per_sec();
+ qemu_mod_timer(s->sem_timer, qemu_get_clock(vm_clock) + TICK_COUNTDOWN);
+ } else {
+ sem_retry = 100;
+ qemu_del_timer(s->sem_timer);
+ onedram_fmt_send_cmd(s);
+ }
+}
+
+/* try to see if we have semaphore for sending, if not, request it from AP */
+static int onedram_fmt_try_send_cmd(S5pc1xxOneDRAMState *s)
+{
+ if(onedram_read_sem(s)) {
+ fprintf(stderr, "onedram_fmt_try_send_cmd - can't get anthority\n");
+ qemu_mod_timer(s->sem_timer, qemu_get_clock(vm_clock) + TICK_COUNTDOWN);
+ } else {
+ onedram_fmt_send_cmd(s);
+ }
+ return 1;
+}
+
+/* the real sending function of fmt */
+static int onedram_fmt_send_cmd(S5pc1xxOneDRAMState *s)
+{
+ int psrc = -1;
+ uint32_t len;
+ int ret = 0;
+
+ onedram_disable_interrupt(s);
+ onedram_disable_write(s);
+
+ do {
+ psrc = onedram_read_fmt(s, &len);
+
+ if (psrc < 0) {
+ ret = -1;
+ break;
+ }
+ if (len == 0) { /* read done */
+ ret = 0;
+ break;
+ }
+ if (!onedram_insert_socket(s, psrc, len))
+ break;
+ } while (1);
+
+ onedram_socket_push(s);
+
+ onedram_enable_interrupt(s);
+ onedram_enable_write(s);
+ return ret;
+}
+
+
+static void onedram_data_handler_fmt_autonomous(S5pc1xxOneDRAMState *s)
+{
+ uint16_t non_cmd = 0;
+ uint32_t in_head = 0, in_tail = 0;
+
+ if (onedram_can_access_shm(s)) {
+ in_head = onedram_read_inhead(s);
+ in_tail = onedram_read_intail(s);
+
+ if (in_head != in_tail) {
+ non_cmd |= INT_MASK_SEND_FMT;
+ fprintf(stderr, "formated partition has head-tail mis-match\n");
+ }
+ } else {
+ fprintf(stderr,
+ "onedram_data_handler_fmt_autonomous - can't access shm\n");
+ }
+
+ if (non_cmd & INT_MASK_SEND_FMT)
+ onedram_fmt_try_send_cmd(s);
+}
+
+/*static*/ void onedram_command_handler(S5pc1xxOneDRAMState *s,
+ uint32_t data)
+{
+ uint8_t cmd = (uint8_t) (data & 0xff);
+
+ onedram_data_handler_fmt_autonomous(s);
+
+ switch (cmd) {
+ case INT_MASK_CMD_NONE:
+ return;
+ case INT_MASK_CMD_REQ_ACTIVE:
+ onedram_req_active_handler(s);
+ return;
+ case INT_MASK_CMD_RES_ACTIVE:
+ return;
+ case INT_MASK_CMD_INIT_START:
+ return;
+ case INT_MASK_CMD_INIT_END:
+ return;
+ case INT_MASK_CMD_ERR_DISPLAY:
+ return;
+ case INT_MASK_CMD_PHONE_START:
+ return;
+ case INT_MASK_CMD_REQ_TIME_SYNC:
+ return;
+ case INT_MASK_CMD_PHONE_DEEP_SLEEP:
+ return;
+ case INT_MASK_CMD_NV_REBUILDING:
+ return;
+ case INT_MASK_CMD_EMER_DOWN:
+ return;
+ case INT_MASK_CMD_SMP_REQ:
+ onedram_smp_req_handler(s);
+ return;
+ case INT_MASK_CMD_SMP_REP:
+ return;
+ default:
+ fprintf(stderr, "command_handler: Unknown command.. %x\n", cmd);
+ return;
+ }
+}
+
+/*static*/ void onedram_data_handler(S5pc1xxOneDRAMState *s,
+ uint16_t non_cmd)
+{
+ if (non_cmd & INT_MASK_SEND_FMT)
+ onedram_fmt_try_send_cmd(s);
+}
+
+/* Shared Memory R/W */
+static uint32_t onedram_can_access_shm(S5pc1xxOneDRAMState *s)
+{
+ return !(onedram_io_readl(s, ONEDRAM_SEM));
+}
+
+static int onedram_read_shm(S5pc1xxOneDRAMState *s, uint8_t *buf,
+ uint32_t offset, uint32_t size)
+{
+ uint8_t *src_base;
+ target_phys_addr_t phy_base, src_len;
+ phy_base = ONEDRAM_SHARED_BASE + offset;
+ src_len = size;
+
+ if (!onedram_can_access_shm(s)) {
+ fprintf(stderr,
+ "onedram_read_shm : can't access to shm\n");
+ return 0;
+ }
+
+ if (size > (ONEDRAM_SHARED_SIZE - offset)){
+ fprintf(stderr,
+ "onedram_read_shm : size exceed the maximum\n");
+ return 0;
+ }
+
+ src_base = cpu_physical_memory_map(phy_base, &src_len, 0);
+
+ if (!src_base) {
+ fprintf(stderr,
+ "onedram_read_shm : src_base is NULL\n");
+ return 0;
+ }
+
+ memcpy(buf, src_base, src_len);
+
+ cpu_physical_memory_unmap(src_base, src_len, 0, src_len);
+
+ return 1;
+}
+
+static int onedram_write_shm(S5pc1xxOneDRAMState *s,
+ const uint8_t *buf, uint32_t offset,
+ uint32_t size)
+{
+ uint8_t *src_base;
+ target_phys_addr_t phy_base, src_len;
+ phy_base = ONEDRAM_SHARED_BASE + offset;
+ src_len = size;
+
+ if (!onedram_can_access_shm(s)) {
+ fprintf(stderr,
+ "onedram_write_shm : can't access to fmt\n");
+ return 0;
+ }
+
+ if (size > ONEDRAM_IN_FMT_SIZE){
+ fprintf(stderr,
+ "onedram_write_shm : size exceeds the maximum\n");
+ return 0;
+ }
+
+ src_base = cpu_physical_memory_map(phy_base, &src_len, 1);
+
+ if (!src_base) {
+ fprintf(stderr,
+ "onedram_write_shm : src_base is NULL\n");
+ return 0;
+ }
+
+ memcpy(src_base, buf, size);
+
+ cpu_physical_memory_unmap(src_base, src_len, 1, src_len);
+
+ return 1;
+}
+
+/* Formatted Shared Memory Operation */
+/*static*/ uint32_t onedram_read_outhead(S5pc1xxOneDRAMState *s)
+{
+ uint32_t head = 0;
+
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_read_shm(s, (uint8_t *)&head,
+ s->fmt_info->out_head_addr,
+ s->fmt_info->ptr_size);
+ }
+ return head;
+}
+
+/*static*/ uint32_t onedram_read_inhead(S5pc1xxOneDRAMState *s)
+{
+ uint32_t head = 0;
+
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_read_shm(s, (uint8_t *)&head,
+ s->fmt_info->in_head_addr,
+ s->fmt_info->ptr_size);
+ }
+ return head;
+}
+
+/*static*/ uint32_t onedram_read_outtail(S5pc1xxOneDRAMState *s)
+{
+ uint32_t tail = 0;
+
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_read_shm(s, (uint8_t *)&tail,
+ s->fmt_info->out_tail_addr,
+ s->fmt_info->ptr_size);
+ }
+ return tail;
+}
+
+/*static*/ uint32_t onedram_read_intail(S5pc1xxOneDRAMState *s)
+{
+ uint32_t tail = 0;
+
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_read_shm(s, (uint8_t *)&tail,
+ s->fmt_info->in_tail_addr,
+ s->fmt_info->ptr_size);
+ }
+ return tail;
+}
+
+/*static*/ uint32_t onedram_write_outhead(S5pc1xxOneDRAMState *s,
+ uint32_t head)
+{
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_write_shm(s, (uint8_t *)&head,
+ s->fmt_info->out_head_addr,
+ s->fmt_info->ptr_size);
+ }
+ return head;
+}
+
+/*static*/ uint32_t onedram_write_inhead(S5pc1xxOneDRAMState *s,
+ uint32_t head)
+{
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_write_shm(s, (uint8_t *)&head,
+ s->fmt_info->in_head_addr,
+ s->fmt_info->ptr_size);
+ }
+ return head;
+}
+
+/*static*/ uint32_t onedram_write_outtail(S5pc1xxOneDRAMState *s,
+ uint32_t tail)
+{
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_write_shm(s, (uint8_t *)&tail,
+ s->fmt_info->out_tail_addr,
+ s->fmt_info->ptr_size);
+ }
+ return tail;
+}
+
+/*static*/ uint32_t onedram_write_intail(S5pc1xxOneDRAMState *s,
+ uint32_t tail)
+{
+ if (!s->fmt_info) {
+ fprintf(stderr, "err\n");
+ } else {
+ onedram_write_shm(s, (uint8_t *)&tail,
+ s->fmt_info->in_tail_addr,
+ s->fmt_info->ptr_size);
+ }
+ return tail;
+}
+
+/*static*/ int onedram_read_fmt(S5pc1xxOneDRAMState *s,
+ uint32_t *len)
+{
+ int psrc = -1;
+ uint32_t head = 0, tail = 0;
+ uint32_t new_tail = 0;
+
+ head = onedram_read_outhead(s);
+ tail = onedram_read_outtail(s);
+
+ if (head >= s->fmt_info->out_buff_size ||
+ tail >= s->fmt_info->out_buff_size) {
+ fprintf(stderr, "head(%d) or tail(%d) is out of bound\n", head, tail);
+ goto done_onedram_read_fmt;
+ }
+
+ if (head == tail) {
+ psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+ *len = 0;
+ goto done_onedram_read_fmt;
+ }
+
+ if (head > tail) {
+ /* ------- tail ++++++++++++ head -------- */
+ psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+ *len = (head - tail);
+ } else {
+ /* +++++++ head ------------ tail ++++++++ */
+ psrc = /*(uint8_t *)*/(s->fmt_info->out_buff_addr + tail);
+ *len = (s->fmt_info->out_buff_size - tail);
+ }
+
+ /* new tail */
+ new_tail = (uint32_t)((tail + *len) % s->fmt_info->out_buff_size);
+ onedram_write_outtail(s, new_tail);
+
+done_onedram_read_fmt:
+ return psrc;
+}
+
+static int onedram_insert_socket(S5pc1xxOneDRAMState *s,
+ uint32_t psrc, uint16_t size)
+{
+ uint8_t *buf;
+
+ if ((s->socket_len + size) >= SOCKET_BUFFER_MAX_SIZE) {
+ fprintf(stderr, "the socket buffer is overflow!\n");
+
+ onedram_read_shm(s, (s->socket_buffer + s->socket_len), psrc,
+ SOCKET_BUFFER_MAX_SIZE - s->socket_len);
+ s->socket_len = SOCKET_BUFFER_MAX_SIZE;
+ return 0;
+ } else {
+ buf = s->socket_buffer + s->socket_len;
+ onedram_read_shm(s, buf, psrc, size);
+ s->socket_len += size;
+ return 1;
+ }
+}
+
+void onedram_socket_push(S5pc1xxOneDRAMState *s)
+{
+ onedram_tcp_write(s, s->socket_buffer, s->socket_len);
+ s->socket_len = 0;
+}
+
+int onedram_write_fmt(S5pc1xxOneDRAMState *s, const uint8_t *buf,
+ uint32_t len)
+{
+ int ret = FAIL;
+ uint32_t size = 0;
+ uint32_t head = 0, tail = 0;
+ uint16_t irq_mask = 0;
+
+ if (!s->fmt_info || !buf)
+ return FAIL;
+
+ onedram_disable_interrupt(s);
+ onedram_disable_write(s);
+
+ head = onedram_read_inhead(s);
+ tail = onedram_read_intail(s);
+
+ if (head < tail) {
+ /* +++++++++ head ---------- tail ++++++++++ */
+ size = tail - head - 1;
+ size = (len > size) ? size : len;
+
+ onedram_write_shm(s, (uint8_t *)buf,
+ s->fmt_info->in_buff_addr + head, size);
+ ret = size;
+ } else if (tail == 0) {
+ /* tail +++++++++++++++ head --------------- */
+ size = s->fmt_info->in_buff_size - head - 1;
+ size = (len > size) ? size : len;
+
+ onedram_write_shm(s, (uint8_t *)buf,
+ s->fmt_info->in_buff_addr + head, size);
+ ret = size;
+ } else {
+ /* ------ tail +++++++++++ head ------------ */
+ size = s->fmt_info->in_buff_size - head;
+ size = (len > size) ? size : len;
+
+ onedram_write_shm(s, (uint8_t *)buf,
+ s->fmt_info->in_buff_addr + head, size);
+ ret = (int)size;
+
+ if ((int)len > ret) {
+ size = (len - ret > tail - 1) ? tail - 1 : len - ret;
+ buf += ret;
+ onedram_write_shm(s, (uint8_t *)buf,
+ s->fmt_info->in_buff_addr, size);
+ ret += (int)size;
+ }
+ }
+
+ /* calculate new head */
+ head = (uint32_t)((head + ret) % s->fmt_info->in_buff_size);
+ onedram_write_inhead(s, head);
+
+ if (head >= s->fmt_info->in_buff_size ||
+ tail >= s->fmt_info->in_buff_size) {
+ fprintf(stderr, "head(%d) or tail(%d) is out of bound\n", head, tail);
+ goto err_onedram_write_fmt;
+ }
+
+ /* send interrupt to the phone, if.. */
+ irq_mask = INT_MASK_VALID;
+
+ if (ret > 0)
+ irq_mask |= s->fmt_info->mask_send;
+
+ if ((int)len > ret)
+ irq_mask |= s->fmt_info->mask_req_ack;
+
+ onedram_put_authority(s);
+ onedram_enable_interrupt(s);
+ onedram_send_cmd_to_pda(s, irq_mask);
+ onedram_enable_write(s);
+
+ return ret;
+
+err_onedram_write_fmt:
+ onedram_put_authority(s);
+ onedram_enable_interrupt(s);
+ onedram_enable_write(s);
+
+ return ret;
+}
+
+/* Interrupt Operation */
+static uint32_t onedram_irq_cp_raise_32(S5pc1xxOneDRAMState *s)
+{
+ uint32_t irq_mask;
+ int64_t timeout;
+
+ s->irq_onedram_int_cp_pending = 1;
+ irq_mask = onedram_io_readl(s, ONEDRAM_MBX_BA);
+
+ switch (irq_mask) {
+ case IPC_CP_IMG_LOADED:
+ onedram_io_writel(s, ONEDRAM_MBX_AB, IPC_CP_READY);
+ timeout = get_ticks_per_sec();
+ qemu_mod_timer(s->bootup_timer, qemu_get_clock(vm_clock) + timeout);
+ return IRQ_HANDLED;
+ case IPC_CP_READY_FOR_LOADING:
+ return IRQ_HANDLED;
+ default:
+ fprintf(stderr, "onedram_irq_cp_raise_32: unknown command\n");
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static uint32_t onedram_irq_cp_raise_16(S5pc1xxOneDRAMState *s)
+{
+ uint16_t irq_mask;
+ s->irq_onedram_int_cp_pending = 1;
+
+ irq_mask = (uint16_t)onedram_io_readl(s, ONEDRAM_MBX_BA);
+
+ if (!(irq_mask & INT_MASK_VALID)) {
+ fprintf(stderr, "Invalid interrupt mask: 0x%04x\n", irq_mask);
+ return IRQ_NONE;
+ }
+
+ if (irq_mask & INT_MASK_COMMAND) {
+ irq_mask &= ~(INT_MASK_VALID | INT_MASK_COMMAND);
+ onedram_command_handler(s, irq_mask);
+ } else {
+ irq_mask &= ~INT_MASK_VALID;
+ onedram_data_handler(s, irq_mask);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void onedram_disable_interrupt(S5pc1xxOneDRAMState *s)
+{
+ s->onedram_state.interruptable = 0;
+}
+
+void onedram_enable_interrupt(S5pc1xxOneDRAMState *s)
+{
+ s->onedram_state.interruptable = 1;
+}
+
+uint16_t onedram_interruptable(S5pc1xxOneDRAMState *s)
+{
+ return s->onedram_state.interruptable;
+}
+
+static void onedram_irq_cp_lower(S5pc1xxOneDRAMState *s)
+{
+ s->irq_onedram_int_cp_pending = 0;
+}
+
+static uint32_t onedram_irq_cp_pending(S5pc1xxOneDRAMState *s)
+{
+ return s->irq_onedram_int_cp_pending;
+}
+
+static void onedram_irq_ap_raise(S5pc1xxOneDRAMState *s)
+{
+ s->irq_onedram_int_ap_pending = 1;
+ qemu_irq_raise(s->irq_onedram_int_ap);
+}
+
+static void onedram_irq_ap_lower(S5pc1xxOneDRAMState *s)
+{
+ s->irq_onedram_int_ap_pending = 0;
+ qemu_irq_lower(s->irq_onedram_int_ap);
+}
+
+static uint32_t onedram_irq_ap_pending(S5pc1xxOneDRAMState *s)
+{
+ return s->irq_onedram_int_ap_pending;
+}
+
+/* Authority Operation */
+unsigned int onedram_read_sem(S5pc1xxOneDRAMState *s)
+{
+ unsigned int sem;
+
+ sem = onedram_io_readl(s, ONEDRAM_SEM);
+
+ return sem;
+}
+
+static void onedram_put_authority(S5pc1xxOneDRAMState *s)
+{
+ uint32_t sem;
+ sem = 0x1;
+ onedram_io_writel(s, ONEDRAM_SEM, sem);
+}
+
+int onedram_try_get_authority(S5pc1xxOneDRAMState *s)
+{
+ uint16_t cmd = 0;
+
+ if(!onedram_read_sem(s))
+ return TRUE;
+
+ cmd = INT_COMMAND(INT_MASK_CMD_SMP_REQ);
+ onedram_send_cmd_to_pda(s, cmd);
+
+ return FALSE;
+}
+
+void onedram_disable_write(S5pc1xxOneDRAMState *s)
+{
+ s->onedram_state.writable = 0;
+}
+
+void onedram_enable_write(S5pc1xxOneDRAMState *s)
+{
+ s->onedram_state.writable = 1;
+}
+
+uint16_t onedram_writable(S5pc1xxOneDRAMState *s)
+{
+ return s->onedram_state.writable;
+}
+
+void onedram_send_cmd_to_pda(S5pc1xxOneDRAMState *s, uint16_t val)
+{
+ uint16_t check_ab;
+
+ check_ab = (uint16_t)onedram_io_readl(s, ONEDRAM_CHECK_AB);
+ check_ab &= 0x1;
+ if (!check_ab) {
+ onedram_io_writel(s, ONEDRAM_MBX_AB, (uint32_t)val);
+ } else {
+ fprintf(stderr, "mailbox_ab has not been read by AP yet!\n");
+ }
+}
+
+/* Boot up timer */
+static void onedram_bootup(void *opaque)
+{
+ uint16_t cmd;
+
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+ if(!s->vmodem_bootup) {
+ onedram_io_writel(s, ONEDRAM_MBX_AB, IPC_CP_READY_FOR_LOADING);
+ s->vmodem_bootup = 1;
+
+ } else {
+ /* Init the in/out head/tail */
+ onedram_write_inhead(s, 0);
+ onedram_write_intail(s, 0);
+ onedram_write_outhead(s, 0);
+ onedram_write_outtail(s, 0);
+ /* put the authority to AP to let it access to shared memory */
+ onedram_put_authority(s);
+ cmd = INT_COMMAND(INT_MASK_CMD_PHONE_START|CP_CHIP_INFINEON);
+ onedram_send_cmd_to_pda(s, cmd);
+ qemu_del_timer(s->bootup_timer);
+ }
+}
+
+/* Register Modem */
+static void onedram_register_modem(S5pc1xxOneDRAMState *s,
+ ModemPlatformData *mp)
+{
+ /* fmt info */
+ s->fmt_info->in_head_addr = mp->in_fmt_base;
+ s->fmt_info->in_tail_addr = mp->in_fmt_base + mp->ptr_fmt_size;
+ s->fmt_info->in_buff_addr =
+ FMT_IN_BUF_PTR/*mp->in_fmt_base + (mp->ptr_fmt_size << 1)*/;
+ s->fmt_info->in_buff_size = mp->in_fmt_size;
+ s->fmt_info->out_head_addr = mp->out_fmt_base;
+ s->fmt_info->out_tail_addr = mp->out_fmt_base + mp->ptr_fmt_size;
+ s->fmt_info->out_buff_addr =
+ FMT_OUT_BUF_PTR/*mp->out_fmt_base + (mp->ptr_fmt_size << 1)*/;
+ s->fmt_info->out_buff_size = mp->out_fmt_size;
+ s->fmt_info->mask_req_ack = INT_MASK_REQ_ACK_FMT;
+ s->fmt_info->mask_res_ack = INT_MASK_RES_ACK_FMT;
+ s->fmt_info->mask_send = INT_MASK_SEND_FMT;
+ s->fmt_info->ptr_size = mp->ptr_fmt_size;
+}
+
+/* onedram IO R/W */
+static uint32_t onedram_io_readb(void *opaque,
+ target_phys_addr_t offset)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return (s->sem) & 0x000000FF;
+ case 0x01:
+ return ((s->sem >> 8 ) & 0x000000FF);
+ case 0x20:
+ return (s->mbx_ab) & 0x000000FF;
+ case 0x21:
+ /* If interrupt is pending, once after AP reads the mailbox_ab,
+ * we should clear the pending */
+ if (onedram_irq_ap_pending(s))
+ onedram_irq_ap_lower(s);
+ /* set check_ab to 0 means the mailbox_ab has been read by AP */
+ s->check_ab = 0;
+ return ((s->mbx_ab >> 8) & 0x000000FF);
+ /* when modem is booting, the AP will read the high two bytes of mail_box
+ * (only for new onedram driver) */
+ case 0x22:
+ return ((s->mbx_ab >> 16) & 0x000000FF);
+ case 0x23:
+ /* If interrupt is pending, once after AP reads the mailbox_ab,
+ * we should clear the pending */
+ if (onedram_irq_ap_pending(s))
+ onedram_irq_ap_lower(s);
+ /* set check_ab to 0 means the mailbox_ab has been read by AP */
+ s->check_ab = 0;
+ return ((s->mbx_ab >> 24) & 0x000000FF);
+ case 0x40:
+ return (s->mbx_ba) & 0x000000FF;
+ case 0x41:
+ if (onedram_irq_cp_pending(s))
+ onedram_irq_cp_lower(s);
+ /* set check_ba to 0 means the mailbox_ab has been read by CP */
+ s->check_ba = 0;
+ return ((s->mbx_ba >> 8 ) & 0x000000FF);
+ case 0xA0:
+ return (s->check_ab) & 0x000000FF;
+ case 0xA1:
+ return ((s->check_ab >> 8 ) & 0x000000FF);
+ case 0xC0:
+ return ((s->check_ba) & 0x000000FF);
+ case 0xC1:
+ return ((s->check_ba >> 8 ) & 0x000000FF);
+ default:
+ hw_error("onedram: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static uint32_t onedram_io_readl(void *opaque,
+ target_phys_addr_t offset)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return s->sem;
+ case 0x20:
+ /* If interrupt is pending, once AP reads the mailbox_ab,
+ * we should clear the pending */
+ if (onedram_irq_ap_pending(s))
+ onedram_irq_ap_lower(s);
+ /* set check_ab to 0 means the mailbox_ab has been read by AP */
+ s->check_ab = 0;
+ return s->mbx_ab;
+ case 0x40:
+ if (onedram_irq_cp_pending(s))
+ onedram_irq_cp_lower(s);
+ /* set check_ba to 0 means the mailbox_ab has been read by CP */
+ s->check_ba = 0;
+ return s->mbx_ba;
+ case 0xA0:
+ return s->check_ab;
+ case 0xC0:
+ return s->check_ba;
+ default:
+ hw_error("onedram: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void onedram_io_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->sem = val;
+ break;
+ case 0x20:
+ (s->mbx_ab) = (val & 0x000000FF);
+ break;
+ case 0x21:
+ (s->mbx_ab) |= ((val << 8) & 0x0000FF00) ;
+ /* set check_ab to 1 means the mailbox_ab is waiting for AP to read */
+ s->check_ab = 1;
+ /* If interrupt is not pending, raise the interrupt to AP */
+ if (!onedram_irq_ap_pending(s))
+ onedram_irq_ap_raise(s);
+ break;
+ case 0x40:
+ (s->mbx_ba) = (val & 0x000000FF);
+ break;
+ case 0x41:
+ (s->mbx_ba) |= ((val << 8) & 0x0000FF00);
+ /* set check_ab to 1 means the mailbox_ba is waiting for CP to read */
+ s->check_ba = 1;
+ /* raise an interrupt to inform CP that there is a message has come */
+ if (onedram_interruptable(s))
+ onedram_irq_cp_raise_16(s);
+ break;
+ case 0xA0:
+ (s->check_ab) = (val & 0x000000FF);
+ break;
+ case 0xA1:
+ (s->check_ab) |= ((val << 8) & 0x0000FF00);
+ break;
+ case 0xC0:
+ (s->check_ba) = (val & 0x000000FF);
+ break;
+ case 0xC1:
+ (s->check_ba) |= ((val << 8) & 0x0000FF00) ;
+ break;
+ default:
+ hw_error("onedram: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void onedram_io_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->sem = val;
+ break;
+ case 0x20:
+ s->mbx_ab = val;
+ s->check_ab = 1;
+ if (!onedram_irq_ap_pending(s))
+ onedram_irq_ap_raise(s);
+ break;
+ case 0x40:
+ s->mbx_ba = val;
+ s->check_ba = 1;
+ if (onedram_interruptable(s))
+ onedram_irq_cp_raise_32(s);
+ break;
+ case 0xA0:
+ s->check_ab = val;
+ break;
+ case 0xC0:
+ s->check_ba = val;
+ break;
+ default:
+ hw_error("onedram: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/*
+ * In mailbox there are 2 bytes CMD and 4 bytes message,
+ * but 4 bytes only for booting the phone.
+ * in other cases, only use 2 bytes read
+ */
+static CPUReadMemoryFunc * const onedram_mm_read[] = {
+ onedram_io_readb,
+ onedram_io_readl,
+ onedram_io_readl
+};
+
+static CPUWriteMemoryFunc * const onedram_mm_write[] = {
+ onedram_io_writeb,
+ onedram_io_writel,
+ onedram_io_writel
+};
+
+static void s5pc1xx_onedram_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ qemu_put_be32s(f, &s->magic_code);
+ qemu_put_be32s(f, &s->sem);
+ qemu_put_be32s(f, &s->mbx_ab);
+ qemu_put_be32s(f, &s->mbx_ba);
+ qemu_put_be32s(f, &s->check_ab);
+ qemu_put_be32s(f, &s->check_ba);
+ qemu_put_be32s(f, &s->irq_onedram_int_ap_pending);
+ qemu_put_be32s(f, &s->irq_onedram_int_cp_pending);
+
+ /* FIXME: must the structure below be saved?
+ * CharDriverState *socket; */
+ qemu_put_be32s(f, &s->vmodem_connected);
+ qemu_put_be32s(f, &s->vmodem_bootup);
+
+ qemu_put_be32s (f, &s->socket_len);
+ qemu_put_buffer(f, s->socket_buffer, s->socket_len);
+
+ qemu_put_be16s(f, &s->onedram_state.waiting_authority);
+ qemu_put_be16s(f, &s->onedram_state.waiting_sem_rep);
+ qemu_put_be16s(f, &s->onedram_state.waiting_check);
+ qemu_put_be16s(f, &s->onedram_state.non_cmd);
+ qemu_put_be16s(f, &s->onedram_state.send_cmd);
+ qemu_put_be16s(f, &s->onedram_state.interruptable);
+ qemu_put_be16s(f, &s->onedram_state.writable);
+
+ qemu_put_8s (f, &s->onedram_state.send_size);
+ qemu_put_buffer(f, s->onedram_state.send_buf, s->onedram_state.send_size);
+
+ qemu_put_timer(f, s->bootup_timer);
+ qemu_put_timer(f, s->sem_timer);
+}
+
+static int s5pc1xx_onedram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->magic_code);
+ qemu_get_be32s(f, &s->sem);
+ qemu_get_be32s(f, &s->mbx_ab);
+ qemu_get_be32s(f, &s->mbx_ba);
+ qemu_get_be32s(f, &s->check_ab);
+ qemu_get_be32s(f, &s->check_ba);
+ qemu_get_be32s(f, &s->irq_onedram_int_ap_pending);
+ qemu_get_be32s(f, &s->irq_onedram_int_cp_pending);
+
+ /* FIXME: CharDriverState *socket; */
+ qemu_get_be32s(f, &s->vmodem_connected);
+ qemu_get_be32s(f, &s->vmodem_bootup);
+
+ qemu_get_be32s(f, &s->socket_len);
+ qemu_get_buffer(f, s->socket_buffer, s->socket_len);
+
+ qemu_get_be16s(f, &s->onedram_state.waiting_authority);
+ qemu_get_be16s(f, &s->onedram_state.waiting_sem_rep);
+ qemu_get_be16s(f, &s->onedram_state.waiting_check);
+ qemu_get_be16s(f, &s->onedram_state.non_cmd);
+ qemu_get_be16s(f, &s->onedram_state.send_cmd);
+ qemu_get_be16s(f, &s->onedram_state.interruptable);
+ qemu_get_be16s(f, &s->onedram_state.writable);
+
+ qemu_get_8s(f, &s->onedram_state.send_size);
+ qemu_get_buffer(f, s->onedram_state.send_buf, s->onedram_state.send_size);
+
+ qemu_get_timer(f, s->bootup_timer);
+ qemu_get_timer(f, s->sem_timer);
+
+ return 0;
+}
+
+/* Socket for Vmodem operation*/
+static int onedram_tcp_can_read(void *opaque)
+{
+ return MAX_BUFFER;
+}
+
+static void onedram_tcp_read(void *opaque, const uint8_t *buf,
+ int size)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+ uint32_t send_cmd;
+ int64_t timeout;
+ /* In booting stage, we need to set up the connection to
+ * Vmodem thru Socket */
+ if (!s->vmodem_connected) {
+ if (((uint32_t *)buf)[0] == IPC_CP_CONNECT_APP) {
+ send_cmd = IPC_AP_CONNECT_ACK;
+ onedram_tcp_write(s, (uint8_t *)&send_cmd, CONNECT_LENGTH);
+ s->vmodem_connected = 1;
+ /* put the anthority to AP,
+ * because AP will try to load the modem image for CP */
+ onedram_put_authority(s);
+ /* before here, the PSI has been loaded by CP already,
+ * in the new onedram driver,
+ * we have to send IPC_CP_READY_FOR_LOADING to AP
+ * rather than waiting for to be read from AP */
+ timeout = get_ticks_per_sec();
+ qemu_mod_timer(s->bootup_timer,
+ qemu_get_clock(vm_clock) + timeout/10);
+ }
+ } else {
+ /* The connection to Vmodem has been set up,
+ * so now we only exchange IPC */
+ if (onedram_writable(s)) {
+ //onedram_prepare_write_fmt(s, buf, size);
+ onedram_write_fmt(s, buf, size);
+ } else {
+ return;
+ }
+ }
+}
+
+void onedram_tcp_write(void *opaque, const uint8_t *buf, uint32_t size)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ s->socket->chr_write(s->socket, buf, size);
+}
+
+static void onedram_tcp_event(void *opaque, int event)
+{
+ /* not implemented yet */
+}
+
+static void onedram_tcp_init(void *opaque)
+{
+ S5pc1xxOneDRAMState *s = (S5pc1xxOneDRAMState *)opaque;
+
+ /* open a socket to communicate with vmodem */
+ const char *p = "tcp:localhost:7777,server,nowait";
+ s->socket= qemu_chr_open("onedram_socket", p, NULL);
+
+ if (!s->socket)
+ hw_error("onedram: could not open onedram socket\n");
+
+ qemu_chr_add_handlers(s->socket, onedram_tcp_can_read,
+ onedram_tcp_read, onedram_tcp_event,
+ s);
+}
+
+DeviceState *s5pc1xx_onedram_init(const char *name, target_phys_addr_t base,
+ qemu_irq irq_ap)
+{
+ DeviceState *dev = qdev_create(NULL, name);
+ ram_addr_t onedram_shared, onedram_ap;
+
+ qdev_init_nofail(dev);
+ onedram_ap = qemu_ram_alloc(dev, "s5pc1xx.onedram.ap", ONEDRAM_AP_SIZE);
+ cpu_register_physical_memory(base, ONEDRAM_AP_SIZE,
+ onedram_ap | IO_MEM_RAM);
+ onedram_shared =
+ qemu_ram_alloc(dev, "s5pc1xx.onedram.shared", ONEDRAM_SHARED_SIZE);
+ cpu_register_physical_memory(base + ONEDRAM_AP_SIZE, ONEDRAM_SHARED_SIZE,
+ onedram_shared | IO_MEM_RAM);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ base + ONEDRAM_AP_SIZE + ONEDRAM_SFR);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq_ap);
+ return dev;
+}
+
+static int s5pc1xx_onedram_init1(SysBusDevice *dev, ModemPlatformData *mp)
+{
+ S5pc1xxOneDRAMState *s = FROM_SYSBUS(S5pc1xxOneDRAMState, dev);
+ int onedram_io;
+
+ sysbus_init_irq(dev, &s->irq_onedram_int_ap);
+ onedram_io = cpu_register_io_memory(onedram_mm_read,
+ onedram_mm_write, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ONEDRAM_REGISTER_SIZE, onedram_io);
+
+ s->sem = 0;
+ s->mbx_ab = 0;
+ s->mbx_ba = 0;
+ s->check_ab = 0;
+ s->check_ba = 0;
+
+ s->irq_onedram_int_ap_pending = 0;
+ s->irq_onedram_int_cp_pending = 0;
+
+ s->vmodem_connected = 0;
+ s->vmodem_bootup = 0;
+ s->fmt_info = (ModemInfo *)qemu_mallocz(sizeof(ModemInfo));
+
+ s->socket_buffer = (uint8_t *)qemu_mallocz(SOCKET_BUFFER_MAX_SIZE);
+ s->socket_len = 0;
+
+ s->onedram_state.waiting_authority = FALSE;
+ s->onedram_state.non_cmd = INT_MASK_CMD_NONE;
+ s->onedram_state.send_size = 0;
+ s->onedram_state.waiting_sem_rep = 0;
+ s->onedram_state.send_buf = NULL;
+ s->onedram_state.interruptable = 1;
+ s->onedram_state.writable = 1;
+
+ onedram_register_modem(s, mp);
+ onedram_tcp_init(s);
+
+ s->bootup_timer = qemu_new_timer(vm_clock, onedram_bootup, s);
+ s->sem_timer = qemu_new_timer(vm_clock, onedram_wait_semaphore, s);
+ register_savevm(&dev->qdev, "s5pc1xx.onedram", -1, 1,
+ s5pc1xx_onedram_save, s5pc1xx_onedram_load, s);
+
+ return 0;
+}
+
+static int s5pc1xx_onedram_aquila_xmm_init(SysBusDevice *dev)
+{
+ return s5pc1xx_onedram_init1(dev, &aquila_xmm_modem_data);
+}
+static void s5pc1xx_onedram_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.onedram.aquila.xmm",
+ sizeof(S5pc1xxOneDRAMState),
+ s5pc1xx_onedram_aquila_xmm_init);
+}
+
+device_init(s5pc1xx_onedram_register_devices)
--- /dev/null
+#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
--- /dev/null
+/*
+ * S5PC1XX OneNAND controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "flash.h"
+#include "s5pc1xx.h"
+
+
+/* OneNAND Device Description:
+ EC - Samsung, 50 - device properties, 2E - version */
+#define ONENAND_DEVICE_ID 0xEC502E
+
+/* Device is mux */
+#define IS_MUX_TYPE 1
+
+#define ONENAND_CONTR_REG_BASE 0x00600000
+#define ONENAND_CONTR_REGS_SIZE 0x0000106C
+
+#define ONENAND_BUFFRES_SIZE 0x00012000
+
+#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18)
+#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17)
+#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16)
+
+
+typedef struct S5pc1xxOneNANDState {
+ SysBusDevice busdev;
+ target_phys_addr_t base;
+
+ /* OneNAND Interface Control register */
+ uint32_t onenand_if_clrt;
+ /* OneNAND Interface Async. Timing Control register */
+ uint32_t onenand_async_timing;
+ /* DMA Source Address Register */
+ uint32_t dma_src_addr;
+ /* DMA Source Configuration Register */
+ uint32_t dma_src_cfg;
+ /* DMA Destination Address Register */
+ uint32_t dma_dst_addr;
+ /* DMA Destination Configuration Register */
+ uint32_t dma_dst_cfg;
+ /* DMA Transfer Size Register */
+ uint32_t dma_trans_size;
+ /* DMA Transfer Direction Register */
+ uint32_t dma_trans_dir;
+ /* Sequencer Start Address Offset Register */
+ uint32_t sqc_sao;
+ /* Sequencer Register Control Register */
+ uint32_t sqc_reg_ctrl;
+ /* Sequencer Breakpoint Address Offset#0 Register */
+ uint32_t sqc_brpao0;
+ /* Sequencer Breakpoint Address Offset#1 Register */
+ uint32_t sqc_brpao1;
+ /* Interrupt Controller Sequencer Mask Register */
+ uint32_t intc_sqc_mask;
+ /* Interrupt Controller DMA Mask Register */
+ uint32_t intc_dma_mask;
+ /* Interrupt Controller OneNAND Mask Register */
+ uint32_t intc_onenand_mask;
+} S5pc1xxOneNANDState;
+
+
+static uint32_t s5pc1xx_onenand_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+ /* for compatibility with documentation */
+ int test_offset = offset + ONENAND_CONTR_REG_BASE;
+
+ switch (test_offset) {
+ case 0x00600100: /*RW OneNAND Interface Control register*/
+ return s->onenand_if_clrt;
+
+ case 0x00600400: /*RW DMA Source Address Register*/
+ return s->dma_src_addr;
+ case 0x00600404: /*RW DMA Source Configuration Register*/
+ return s->dma_src_cfg;
+ case 0x00600408: /*RW DMA Destination Address Register*/
+ return s->dma_dst_addr;
+ case 0x0060040C: /*RW DMA Destination Configuration Register*/
+ return s->dma_dst_cfg;
+ case 0x00600414: /*RW DMA Transfer Size Register*/
+ return s->dma_trans_size;
+ case 0x00600418: /*WO DMA Transfer Command Register*/
+ return 0;
+ case 0x0060041C: /*RO DMA Transfer Status Register*/
+ return S5PC110_DMA_TRANS_STATUS_TD;
+ case 0x00600420: /*RW DMA Transfer Direction Register*/
+ return s->dma_trans_dir;
+#if 0 /*TODO: implement support for all of these registers*/
+ case 0x00600104: /*WO OneNAND Interface Command register*/
+ return 0;
+ case 0x00600108: /*RW OneNAND Interface Async. Timing*/
+ return s->onenand_async_timing;
+ case 0x0060010C: /*RO OneNAND Interface Status Register*/
+ return 0x00FC0000;
+ case 0x00600600: /*RW Sequencer Start Address Offset Register*/
+ return s->sqc_sao;
+ case 0x00600608: /*WO Sequencer Command Register*/
+ return 0;
+ case 0x0060060C: /*RO Sequencer Status Register*/
+ case 0x00600610: /*RO Sequencer Current Address Offset*/
+ case 0x00600614: /*RW Sequencer Register Control Register*/
+ return s->sqc_reg_ctrl;
+ case 0x00600618: /*RO Sequencer Register Value Register*/
+ case 0x00600620: /*RW Sequencer Breakpoint Address Offset#0*/
+ return s->sqc_brpao0;
+ case 0x00600624: /*RW Sequencer Breakpoint Address Offset#1*/
+ return s->sqc_brpao1;
+ case 0x00601000: /*WO Interrupt Controller Sequencer Clear*/
+ return 0;
+ case 0x00601004: /*WO Interrupt Controller DMA Clear Register*/
+ return 0;
+ case 0x00601008: /*WO Interrupt Controller OneNAND Clear*/
+ return 0;
+ case 0x00601020: /*RW Interrupt Controller Sequencer Mask*/
+ return s->intc_sqc_mask;
+ case 0x00601024: /*RW Interrupt Controller DMA Mask Register*/
+ return s->intc_dma_mask;
+ case 0x00601028: /*RW Interrupt Controller OneNAND Mask*/
+ return s->intc_onenand_mask;
+ case 0x00601040: /*RO Interrupt Controller Sequencer Pending*/
+ case 0x00601044: /*RO Interrupt Controller DMA Pending*/
+ case 0x00601048: /*RO Interrupt Controller OneNAND Pending*/
+ case 0x00601060: /*RO Interrupt Controller Sequencer Status*/
+ case 0x00601064: /*RO Interrupt Controller DMA Status Register*/
+ case 0x00601068: /*RO Interrupt Controller OneNAND Status*/
+#endif
+ default:
+ hw_error("s5pc1xx.onenand: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static int is_inside_onenand(uint32_t offset, target_phys_addr_t base, uint32_t size)
+{
+ return offset >= base &&
+ offset < base + ONENAND_BUFFRES_SIZE &&
+ offset + size < base + ONENAND_BUFFRES_SIZE;
+}
+
+static void s5pc1xx_onenand_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+ uint8_t *buf = NULL;
+ /* for compatibility with documentation */
+ int test_offset = offset + ONENAND_CONTR_REG_BASE;
+
+ switch (test_offset) {
+ case 0x00600100: /*RW OneNAND Interface Control register*/
+ s->onenand_if_clrt = val;
+ break;
+ case 0x00600400: /*RW DMA Source Address Register*/
+ s->dma_src_addr = val;
+ break;
+ case 0x00600404: /*RW DMA Source Configuration Register*/
+ s->dma_src_cfg = val;
+ break;
+ case 0x00600408: /*RW DMA Destination Address Register*/
+ s->dma_dst_addr = val;
+ break;
+ case 0x0060040C: /*RW DMA Destination Configuration Register*/
+ s->dma_dst_cfg = val;
+ break;
+ case 0x00600414: /*RW DMA Transfer Size Register*/
+ s->dma_trans_size = val;
+ break;
+ case 0x00600418: /*WO DMA Transfer Command Register*/
+ if (!is_inside_onenand(s->dma_src_addr, s->base, s->dma_trans_size) &&
+ !is_inside_onenand(s->dma_dst_addr, s->base, s->dma_trans_size)) {
+ hw_error("s5pc1xx.onenand: invalide memory transfer: src = "
+ TARGET_FMT_plx ", dst = " TARGET_FMT_plx ", size = 0x%X\n",
+ (target_phys_addr_t)s->dma_src_addr,
+ (target_phys_addr_t)s->dma_dst_addr,
+ s->dma_trans_size);
+ }
+
+ buf = qemu_malloc(s->dma_trans_size);
+ cpu_physical_memory_read (s->dma_src_addr, buf, s->dma_trans_size);
+ cpu_physical_memory_write(s->dma_dst_addr, buf, s->dma_trans_size);
+ qemu_free(buf);
+ break;
+ case 0x0060041C: /*RO DMA Transfer Status Register*/
+ break;
+ case 0x00600420: /*RW DMA Transfer Direction Register*/
+ s->dma_trans_dir = val;
+ break;
+#if 0 /*TODO: implement support for all of these registers*/
+ case 0x00600104: /*WO OneNAND Interface Command register*/
+ /* TODO see WR bits */
+ break;
+ case 0x00600108: /*RW OneNAND Interface Async. Timing*/
+ s->onenand_async_timing = val;
+ break;
+ case 0x0060010C: /*RO OneNAND Interface Status Register*/
+ break;
+ case 0x00600600: /*RW Sequencer Start Address Offset Register*/
+ s->sqc_sao = val;
+ break;
+ case 0x00600608: /*WO Sequencer Command Register*/
+ break;
+ case 0x0060060C: /*RO Sequencer Status Register*/
+ break;
+ case 0x00600610: /*RO Sequencer Current Address Offset*/
+ break;
+ case 0x00600614: /*RW Sequencer Register Control Register*/
+ s->sqc_reg_ctrl = val;
+ break;
+ case 0x00600618: /*RO Sequencer Register Value Register*/
+ break;
+ case 0x00600620: /*RW Sequencer Breakpoint Address Offset#0*/
+ s->sqc_brpao0 = val;
+ break;
+ case 0x00600624: /*RW Sequencer Breakpoint Address Offset#1*/
+ s->sqc_brpao1 = val;
+ break;
+ case 0x00601000: /*WO Interrupt Controller Sequencer Clear*/
+ break;
+ case 0x00601004: /*WO Interrupt Controller DMA Clear Register*/
+ break;
+ case 0x00601008: /*WO Interrupt Controller OneNAND Clear*/
+ break;
+ case 0x00601020: /*RW Interrupt Controller Sequencer Mask*/
+ s->intc_sqc_mask = val;
+ break;
+ case 0x00601024: /*RW Interrupt Controller DMA Mask Register*/
+ s->intc_dma_mask = val;
+ break;
+ case 0x00601028: /*RW Interrupt Controller OneNAND Mask*/
+ s->intc_onenand_mask = val;
+ break;
+ case 0x00601040: /*RO Interrupt Controller Sequencer Pending*/
+ break;
+ case 0x00601044: /*RO Interrupt Controller DMA Pending*/
+ break;
+ case 0x00601048: /*RO Interrupt Controller OneNAND Pending*/
+ break;
+ case 0x00601060: /*RO Interrupt Controller Sequencer Status*/
+ break;
+ case 0x00601064: /*RO Interrupt Controller DMA Status Register*/
+ break;
+ case 0x00601068: /*RO Interrupt Controller OneNAND Status*/
+ break;
+#endif
+ default:
+ hw_error("s5pc1xx.onenand: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const onenand_config_reg_mm_read[] = {
+ s5pc1xx_onenand_read,
+ s5pc1xx_onenand_read,
+ s5pc1xx_onenand_read
+};
+
+static CPUWriteMemoryFunc * const onenand_config_reg_mm_write[] = {
+ s5pc1xx_onenand_write,
+ s5pc1xx_onenand_write,
+ s5pc1xx_onenand_write
+};
+
+static void s5pc1xx_onenand_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+ qemu_put_be32s(f, &s->onenand_if_clrt);
+ qemu_put_be32s(f, &s->onenand_async_timing);
+ qemu_put_be32s(f, &s->dma_src_addr);
+ qemu_put_be32s(f, &s->dma_src_cfg);
+ qemu_put_be32s(f, &s->dma_dst_addr);
+ qemu_put_be32s(f, &s->dma_dst_cfg);
+ qemu_put_be32s(f, &s->dma_trans_size);
+ qemu_put_be32s(f, &s->dma_trans_dir);
+ qemu_put_be32s(f, &s->sqc_sao);
+ qemu_put_be32s(f, &s->sqc_reg_ctrl);
+ qemu_put_be32s(f, &s->sqc_brpao0);
+ qemu_put_be32s(f, &s->sqc_brpao1);
+ qemu_put_be32s(f, &s->intc_sqc_mask);
+ qemu_put_be32s(f, &s->intc_dma_mask);
+ qemu_put_be32s(f, &s->intc_onenand_mask);
+}
+
+static int s5pc1xx_onenand_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->onenand_if_clrt);
+ qemu_get_be32s(f, &s->onenand_async_timing);
+ qemu_get_be32s(f, &s->dma_src_addr);
+ qemu_get_be32s(f, &s->dma_src_cfg);
+ qemu_get_be32s(f, &s->dma_dst_addr);
+ qemu_get_be32s(f, &s->dma_dst_cfg);
+ qemu_get_be32s(f, &s->dma_trans_size);
+ qemu_get_be32s(f, &s->dma_trans_dir);
+ qemu_get_be32s(f, &s->sqc_sao);
+ qemu_get_be32s(f, &s->sqc_reg_ctrl);
+ qemu_get_be32s(f, &s->sqc_brpao0);
+ qemu_get_be32s(f, &s->sqc_brpao1);
+ qemu_get_be32s(f, &s->intc_sqc_mask);
+ qemu_get_be32s(f, &s->intc_dma_mask);
+ qemu_get_be32s(f, &s->intc_onenand_mask);
+
+ return 0;
+}
+
+static void onenand_config_reg_reset(void *opaque)
+{
+ S5pc1xxOneNANDState *s = (S5pc1xxOneNANDState *)opaque;
+
+ s->onenand_if_clrt = 0x00004000 | (IS_MUX_TYPE << 31);
+ s->onenand_async_timing = 0x00003415;
+ s->dma_src_addr = 0x00000000;
+ s->dma_src_cfg = 0x00040002;
+ s->dma_dst_addr = 0x00000000;
+ s->dma_dst_cfg = 0x00040002;
+ s->dma_trans_size = 0x00000000;
+ s->dma_trans_dir = 0x00000000;
+ s->sqc_sao = 0x00000000;
+ s->sqc_reg_ctrl = 0x00000000;
+ s->sqc_brpao0 = 0x00000000;
+ s->sqc_brpao1 = 0x00000000;
+ s->intc_sqc_mask = 0x01010000;
+ s->intc_dma_mask = 0x01010000;
+ s->intc_onenand_mask = 0x000000FF;
+}
+
+DeviceState *s5pc1xx_onenand_init(target_phys_addr_t base)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ void *onenand_dev;
+
+ dev = qdev_create(NULL, "s5pc1xx.onenand");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+
+ sysbus_mmio_map(s, 0, base + ONENAND_CONTR_REG_BASE);
+
+ FROM_SYSBUS(S5pc1xxOneNANDState, s)->base = base;
+
+ onenand_dev =
+ onenand_init(ONENAND_DEVICE_ID, 1, NULL, ONENAND_4KB_PAGE, 1);
+ onenand_base_update(onenand_dev, base);
+
+ return dev;
+}
+
+static int s5pc1xx_onenand_init1(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxOneNANDState *s = FROM_SYSBUS(S5pc1xxOneNANDState, dev);
+
+ iomemtype = cpu_register_io_memory(onenand_config_reg_mm_read,
+ onenand_config_reg_mm_write, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ONENAND_CONTR_REGS_SIZE, iomemtype);
+
+ onenand_config_reg_reset(s);
+
+ qemu_register_reset(onenand_config_reg_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.onenand", -1, 1,
+ s5pc1xx_onenand_save, s5pc1xx_onenand_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_onenand_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.onenand", sizeof(S5pc1xxOneNANDState),
+ s5pc1xx_onenand_init1);
+}
+
+device_init(s5pc1xx_onenand_register_devices)
--- /dev/null
+/*
+ * PCM audio interface for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+
+#define ALL_BITS(b,a) (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the PCM Main Control 0x00000000 */
+#define PCM_CTL 0x00
+ #define TXFIFO_DIPSTICK_SHIFT 13 /* 6 bits */
+ #define RXFIFO_DIPSTICK_SHIFT 7 /* 6 bits */
+ #define PCM_TX_DMA_EN (1 << 6)
+ #define PCM_RX_DMA_EN (1 << 5)
+ #define TX_MSB_POS (1 << 4)
+ #define RX_MSB_POS (1 << 3)
+ #define PCM_TXFIFO_EN (1 << 2)
+ #define PCM_RXFIFO_EN (1 << 1)
+ #define PCM_PCM_ENABLE (1 << 0)
+
+/* R/W Specifies the PCM Clock and Shift control 0x00000000 */
+#define PCM_CLKCTL 0x04
+ #define CTL_SERCLK_EN (1 << 19)
+ #define CTL_SERCLK_SEL (1 << 18) /* TODO: now is set high as default */
+ #define SCLK_DIV_SHIFT 9
+ #define SYNC_DIV_SHIFT 0
+
+/* R/W Specifies the PCM TxFIFO write port 0x00010000 */
+#define PCM_TXFIFO 0x08
+ #define TXFIFO_DVALID (1 << 16)
+
+/* R/W Specifies the PCM RxFIFO read port 0x00010000 */
+#define PCM_RXFIFO 0x0C
+ #define RXFIFO_DVALID (1 << 16)
+
+/* R/W Specifies the PCM Interrupt Control 0x00000000 */
+#define PCM_IRQ_CTL 0x10
+ #define INT_EN(stat) (stat)
+ #define EN_IRQ_TO_ARM (1 << 14)
+
+/* R Specifies the PCM Interrupt Status 0x00000000 */
+#define PCM_IRQ_STAT 0x14
+ #define IRQ_PENDING (1 << 13)
+ #define TRANSFER_DONE (1 << 12)
+ #define TXFIFO_EMPTY (1 << 11)
+ #define TXFIFO_ALMOST_EMPTY (1 << 10)
+ #define TXFIFO_FULL (1 << 9)
+ #define TXFIFO_ALMOST_FULL (1 << 8)
+ #define TXFIFO_ERROR_STARVE (1 << 7)
+ #define TXFIFO_ERROR_OVERFLOW (1 << 6)
+ #define RXFIFO_EMPTY (1 << 5)
+ #define RXFIFO_ALMOST_EMPTY (1 << 4)
+ #define RXFIFO_FULL (1 << 3)
+ #define RXFIFO_ALMOST_FULL (1 << 2)
+ #define RXFIFO_ERROR_STARVE (1 << 1)
+ #define RXFIFO_ERROR_OVERFLOW (1 << 0)
+ #define ALL_STAT ALL_BITS(13,0)
+
+/* R Specifies the PCM FIFO Status 0x00000000 */
+#define PCM_FIFO_STAT 0x18
+ #define TXFIFO_COUNT_SHIFT 14 /* 6 bits */
+ #define TXFIFO_EMPTY_FLAG (1 << 13)
+ #define TXFIFO_ALMOST_EMPTY_FLAG (1 << 12)
+ #define TXFIFO_FULL_FLAG (1 << 11)
+ #define TXFIFO_ALMOST_FULL_FLAG (1 << 10)
+ #define RXFIFO_COUNT_SHIFT 4 /* 6 bits */
+ #define RXFIFO_EMPTY_FLAG (1 << 3)
+ #define RXFIFO_ALMOST_EMPTY_FLAG (1 << 2)
+ #define RXFIFO_FULL_FLAG (1 << 1)
+ #define RXFIFO_ALMOST_FULL_FLAG (1 << 0)
+
+/* W Specifies the PCM Interrupt Clear - */
+#define PCM_CLRINT 0x20
+
+#define S5PC1XX_PCM_REG_MEM_SIZE 0x24
+
+
+typedef struct S5pc1xxPCMState {
+
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t instance;
+
+ uint32_t ctl;
+ uint32_t clk_ctl;
+ uint32_t irq_ctl;
+ uint32_t irq_stat;
+ uint32_t fifo_stat;
+
+ struct FrameIn {
+ uint16_t fifo[32];
+
+ uint64_t write_idx;
+ uint64_t read_idx;
+
+ uint8_t fifo_dipstick;
+ uint8_t delay;
+ } rx;
+
+ struct FrameOut {
+ uint16_t fifo[32];
+
+ uint64_t write_idx;
+ uint64_t read_idx;
+
+ uint8_t fifo_dipstick;
+ uint8_t delay;
+ } tx;
+
+ QEMUTimer *pcm_timer;
+ uint16_t sync_div;
+ uint32_t sync_freq;
+ uint32_t sclk_freq;
+ uint64_t last_pcm_time;
+
+ uint8_t sclk_en;
+ uint8_t pcm_io_en;
+
+} S5pc1xxPCMState;
+
+
+/* Function for initialization and reset */
+static void s5pc1xx_pcm_reset(DeviceState *d)
+{
+ S5pc1xxPCMState *s =
+ FROM_SYSBUS(S5pc1xxPCMState, sysbus_from_qdev(d));
+
+ s->ctl = 0;
+ s->clk_ctl = 0x40000;
+ s->irq_ctl = 0;
+ s->irq_stat = 0;
+ s->fifo_stat = 0;
+
+ s->rx.read_idx = 0;
+ s->rx.write_idx = 0;
+ s->rx.delay = 0;
+
+ s->tx.read_idx = 0;
+ s->tx.write_idx = 0;
+ s->tx.delay = 0;
+
+ s->sync_div = 0;
+ s->sync_freq = 0;
+ s->sclk_freq = 0;
+ s->last_pcm_time = 0;
+
+ s->sclk_en = 0;
+ s->pcm_io_en = 0;
+}
+
+/* Interrupts handler */
+static void s5pc1xx_pcm_irq(S5pc1xxPCMState *s,
+ uint32_t stat, uint8_t clear)
+{
+ if (stat) {
+ s->irq_stat |= (IRQ_PENDING | stat);
+ if ((s->irq_ctl & EN_IRQ_TO_ARM) &&
+ (s->irq_ctl & INT_EN(stat))) /* if enabled */
+ qemu_irq_raise(s->irq);
+ }
+ if (clear) {
+ s->irq_stat &= ~(ALL_STAT); /* clear all at once */
+ qemu_irq_lower(s->irq);
+ }
+}
+
+/* Controls RXFIFO stage */
+static void s5pc1xx_pcm_rx_control(S5pc1xxPCMState *s)
+{
+ uint8_t rx_depth;
+
+ rx_depth = (s->rx.write_idx - s->rx.read_idx) % 33;
+
+ if (rx_depth == 0) {
+ s5pc1xx_pcm_irq(s, RXFIFO_EMPTY, 0);
+ s->fifo_stat |= RXFIFO_EMPTY_FLAG;
+ } else {
+ s->fifo_stat &= ~(RXFIFO_EMPTY_FLAG);
+ }
+
+ if (rx_depth == 32) {
+ s5pc1xx_pcm_irq(s, RXFIFO_FULL, 0);
+ s->fifo_stat |= RXFIFO_FULL_FLAG;
+ } else {
+ s->fifo_stat &= ~(RXFIFO_FULL_FLAG);
+ }
+
+ if (rx_depth < s->rx.fifo_dipstick) {
+ s5pc1xx_pcm_irq(s, RXFIFO_ALMOST_EMPTY, 0);
+ s->fifo_stat |= RXFIFO_ALMOST_EMPTY_FLAG;
+ } else {
+ s->fifo_stat &= ~(RXFIFO_ALMOST_EMPTY_FLAG);
+ }
+
+ if (rx_depth > (32 - s->rx.fifo_dipstick)) {
+ s5pc1xx_pcm_irq(s, RXFIFO_ALMOST_FULL, 0);
+ s->fifo_stat |= RXFIFO_ALMOST_FULL_FLAG;
+ } else {
+ s->fifo_stat &= ~(RXFIFO_ALMOST_FULL_FLAG);
+ }
+}
+
+/* Controls TXFIFO stage */
+static void s5pc1xx_pcm_tx_control(S5pc1xxPCMState *s)
+{
+ uint8_t tx_depth;
+
+ tx_depth = (s->tx.write_idx - s->tx.read_idx) % 33;
+
+ if (tx_depth == 0) {
+ s5pc1xx_pcm_irq(s, TXFIFO_EMPTY, 0);
+ s->fifo_stat |= TXFIFO_EMPTY_FLAG;
+ } else {
+ s->fifo_stat &= ~(TXFIFO_EMPTY_FLAG);
+ }
+
+ if (tx_depth == 32) {
+ s5pc1xx_pcm_irq(s, TXFIFO_FULL, 0);
+ s->fifo_stat |= TXFIFO_FULL_FLAG;
+ } else {
+ s->fifo_stat &= ~(TXFIFO_FULL_FLAG);
+ }
+
+ if (tx_depth < s->tx.fifo_dipstick) {
+ s5pc1xx_pcm_irq(s, TXFIFO_ALMOST_EMPTY, 0);
+ s->fifo_stat |= TXFIFO_ALMOST_EMPTY_FLAG;
+ } else {
+ s->fifo_stat &= ~(TXFIFO_ALMOST_EMPTY_FLAG);
+ }
+
+ if (tx_depth > (32 - s->tx.fifo_dipstick)) {
+ s5pc1xx_pcm_irq(s, TXFIFO_ALMOST_FULL, 0);
+ s->fifo_stat |= TXFIFO_ALMOST_FULL_FLAG;
+ } else {
+ s->fifo_stat &= ~(TXFIFO_ALMOST_FULL_FLAG);
+ }
+}
+
+/* Increase fifo indexes */
+static void s5pc1xx_pcm_next_frame(S5pc1xxPCMState *s)
+{
+ if (s->ctl & PCM_RXFIFO_EN) {
+ if (s->rx.write_idx < s->rx.read_idx + 32)
+ s->rx.write_idx++;
+ s5pc1xx_pcm_rx_control(s);
+ }
+
+ if (s->ctl & PCM_TXFIFO_EN) {
+ if (s->tx.read_idx < s->tx.write_idx)
+ s->tx.read_idx++;
+ s5pc1xx_pcm_tx_control(s);
+ }
+}
+
+/* Determine sclk/sync_freq and sync_div */
+static void s5pc1xx_pcm_sclk_update(S5pc1xxPCMState *s)
+{
+ S5pc1xxClk clk;
+ uint16_t sclk_div;
+
+ clk = s5pc1xx_findclk("pclk_66");
+
+ if (!(s->pcm_io_en))
+ s->clk_ctl = 0x40000;
+
+ sclk_div = 2 * ((s->clk_ctl >> SCLK_DIV_SHIFT & ALL_BITS(8, 0)) + 1);
+ s->sclk_freq = s5pc1xx_clk_getrate(clk) / sclk_div;
+
+ if (!(s->sclk_freq))
+ hw_error("s5pc1xx.pcm: timer frequency is zero\n");
+
+ s->sync_div = (s->clk_ctl >> SYNC_DIV_SHIFT & ALL_BITS(8, 0)) + 1;
+ s->sync_freq = s->sclk_freq / s->sync_div;
+}
+
+/* Sync timer */
+static void s5pc1xx_pcm_sync(void *opaque)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+ uint64_t next_pcm_time;
+
+ if (s->sclk_en) {
+ if (!(s->sync_freq))
+ s5pc1xx_pcm_sclk_update(s);
+
+ s5pc1xx_pcm_next_frame(s);
+
+ s->last_pcm_time = qemu_get_clock(vm_clock);
+ next_pcm_time =
+ s->last_pcm_time + muldiv64(1, get_ticks_per_sec(), s->sync_freq);
+ qemu_mod_timer(s->pcm_timer, next_pcm_time);
+ } else {
+ qemu_del_timer(s->pcm_timer);
+ }
+}
+
+/* SCLK x2 cycles counter */
+static uint16_t s5pc1xx_pcm_2sclk(S5pc1xxPCMState *s)
+{
+ uint32_t spent_1G;
+ uint16_t spent_2sclk;
+
+ if (s->last_pcm_time) {
+ spent_1G = qemu_get_clock(vm_clock) - s->last_pcm_time;
+ spent_2sclk =
+ muldiv64(spent_1G, (s->sclk_freq * 2), get_ticks_per_sec());
+ return spent_2sclk;
+ } else {
+ return 0;
+ }
+}
+
+/* SCLK state */
+static uint8_t s5pc1xx_pcm_sclk_s(S5pc1xxPCMState *s)
+{
+ if (s->sclk_en)
+ return ((s5pc1xx_pcm_2sclk(s) % 2) ? 0 : 1);
+ else
+ return 0;
+}
+
+/* SYNC signal state */
+static uint8_t s5pc1xx_pcm_sync_s(S5pc1xxPCMState *s)
+{
+ if (s->sclk_en)
+ return (s5pc1xx_pcm_2sclk(s) < 2);
+ else
+ return 0;
+}
+
+/* Put PCM_SIN bit */
+static void s5pc1xx_write_rxfifo(S5pc1xxPCMState *s, uint32_t sdata)
+{
+ uint16_t cur_pos;
+
+ if (!(s->sclk_en) || !(s->ctl & PCM_RXFIFO_EN))
+ return;
+
+ if (s->rx.write_idx == s->rx.read_idx + 32) {
+ s5pc1xx_pcm_irq(s, RXFIFO_ERROR_OVERFLOW, 0);
+ return;
+ }
+
+ cur_pos = (s5pc1xx_pcm_2sclk(s) / 2) % s->sync_div - s->rx.delay;
+
+ if (cur_pos < 16)
+ s->rx.fifo[s->rx.write_idx % 32] |= (sdata & 0x1) << (15 - cur_pos);
+}
+
+/* Return PCM_SOUT bit */
+static uint8_t s5pc1xx_read_txfifo(S5pc1xxPCMState *s)
+{
+ uint16_t word, cur_pos;
+
+ if (!(s->sclk_en) || !(s->ctl & PCM_TXFIFO_EN))
+ return 0;
+
+ if (s->tx.read_idx == s->tx.write_idx) {
+ s5pc1xx_pcm_irq(s, TXFIFO_ERROR_STARVE, 0);
+ return 0;
+ }
+
+ cur_pos = (s5pc1xx_pcm_2sclk(s) / 2) % s->sync_div - s->tx.delay;
+
+ if (cur_pos > 15) {
+ s5pc1xx_pcm_irq(s, TRANSFER_DONE, 0);
+ return 0;
+ }
+
+ word = s->tx.fifo[s->tx.read_idx % 32];
+ return (word >> (15 - cur_pos) & 0x1);
+}
+
+/* Read PCM by GPIO */
+static uint32_t s5pc1xx_pcm_gpio_read(void *opaque, int io_index)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+ if (!(s->pcm_io_en))
+ return 0;
+
+ if (io_index == PCM_SCLK(s->instance))
+ return s5pc1xx_pcm_sclk_s(s);
+
+ if (io_index == PCM_FSYNC(s->instance))
+ return s5pc1xx_pcm_sync_s(s);
+
+ if (io_index == PCM_SOUT(s->instance))
+ return s5pc1xx_read_txfifo(s);
+
+ return 0;
+}
+
+/* Write PCM by GPIO */
+static void s5pc1xx_pcm_gpio_write(void *opaque, int io_index, uint32_t value)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+ /* TODO: not implemented for now */
+ if (io_index == PCM_EXTCLK(s->instance))
+ return;
+
+ if (io_index == PCM_SIN(s->instance)) {
+ s5pc1xx_write_rxfifo(s, value);
+ }
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_pcm_gpio_readfn = s5pc1xx_pcm_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_pcm_gpio_writefn = s5pc1xx_pcm_gpio_write;
+
+/* Read PCM by OS */
+static uint32_t s5pc1xx_pcm_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+ uint8_t rx_depth, tx_depth;
+ uint32_t ret_val;
+
+ switch(offset) {
+ case PCM_CTL:
+ return s->ctl;
+ case PCM_CLKCTL:
+ return s->clk_ctl;
+
+ case PCM_TXFIFO:
+ if (!(s->ctl & PCM_TX_DMA_EN) || !(s->ctl & PCM_TXFIFO_EN))
+ return 0;
+
+ return (s->tx.fifo[s->tx.read_idx % 32] | TXFIFO_DVALID);
+
+ case PCM_RXFIFO:
+ if (!(s->ctl & PCM_RX_DMA_EN) || !(s->ctl & PCM_RXFIFO_EN))
+ return 0;
+
+ if (s->rx.read_idx == s->rx.write_idx) {
+ s5pc1xx_pcm_irq(s, RXFIFO_ERROR_STARVE, 0);
+ return 0;
+ }
+
+ ret_val = s->rx.fifo[s->rx.read_idx % 32] | RXFIFO_DVALID;
+
+ if (s->rx.read_idx < s->rx.write_idx)
+ s->rx.read_idx++;
+
+ s5pc1xx_pcm_rx_control(s);
+
+ return ret_val;
+
+ case PCM_IRQ_CTL:
+ return s->irq_ctl;
+ case PCM_IRQ_STAT:
+ return s->irq_stat;
+
+ case PCM_FIFO_STAT:
+ rx_depth = (s->rx.write_idx - s->rx.read_idx) % 33;
+ tx_depth = (s->tx.write_idx - s->tx.read_idx) % 33;
+ ret_val = (s->fifo_stat & ALL_BITS(3, 0)) | (rx_depth << 4) |
+ (s->fifo_stat & ALL_BITS(13, 10)) | (tx_depth << 14);
+ return ret_val;
+
+ default:
+ hw_error("s5pc1xx.pcm: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* Write PCM by OS */
+static void s5pc1xx_pcm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+
+ switch(offset) {
+ case PCM_CTL:
+ if ((value & PCM_PCM_ENABLE) < (s->ctl & PCM_PCM_ENABLE)) {
+ s->pcm_io_en = 0;
+ s5pc1xx_pcm_sclk_update(s);
+ }
+ /* the value is set low above */
+ s->pcm_io_en = (!(value & PCM_PCM_ENABLE)) ? : 1;
+
+ if ((value & PCM_RXFIFO_EN) < (s->ctl & PCM_RXFIFO_EN)) {
+ s->rx.read_idx = 0;
+ s->rx.write_idx = 0;
+ }
+ if ((value & PCM_TXFIFO_EN) < (s->ctl & PCM_TXFIFO_EN)) {
+ s->tx.read_idx = 0;
+ s->tx.write_idx = 0;
+ }
+
+ s->rx.delay = (value & RX_MSB_POS) ? 1 : 0;
+ s->rx.fifo_dipstick = value >> RXFIFO_DIPSTICK_SHIFT & ALL_BITS(6, 0);
+
+ s->tx.delay = (value & TX_MSB_POS) ? 1 : 0;
+ s->tx.fifo_dipstick = value >> TXFIFO_DIPSTICK_SHIFT & ALL_BITS(6, 0);
+
+ s->ctl = value;
+ break;
+
+ case PCM_CLKCTL:
+ if ((value & CTL_SERCLK_EN) > (s->clk_ctl & CTL_SERCLK_EN)) {
+ s->sclk_en = 1;
+ s5pc1xx_pcm_sync(s);
+ }
+ /* the value is set high above */
+ s->sclk_en = (value & CTL_SERCLK_EN) ? : 0;
+
+ if (value != s->clk_ctl)
+ s5pc1xx_pcm_sclk_update(s);
+
+ s->clk_ctl = value;
+ break;
+
+ case PCM_TXFIFO:
+ if (!(s->ctl & PCM_TX_DMA_EN) || !(s->ctl & PCM_TXFIFO_EN))
+ break;
+
+ if (s->tx.write_idx == s->tx.read_idx + 32) {
+ s5pc1xx_pcm_irq(s, TXFIFO_ERROR_OVERFLOW, 0);
+ break;
+ }
+
+ s->tx.fifo[s->tx.write_idx % 32] = value;
+
+ if (s->tx.write_idx < s->tx.read_idx + 32)
+ s->tx.write_idx++;
+
+ s5pc1xx_pcm_tx_control(s);
+
+ break;
+
+ case PCM_RXFIFO:
+ if (!(s->ctl & PCM_RX_DMA_EN) || !(s->ctl & PCM_RXFIFO_EN))
+ break;
+
+ s->rx.fifo[s->rx.write_idx % 32] = value;
+ break;
+
+ case PCM_IRQ_CTL:
+ s->irq_ctl = value;
+ break;
+
+ case PCM_CLRINT:
+ /* clear all irq stats and lower the interrupt */
+ s5pc1xx_pcm_irq(s, 0, 1);
+ break;
+
+ default:
+ hw_error("s5pc1xx.pcm: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pcm_readfn[] = {
+ s5pc1xx_pcm_read,
+ s5pc1xx_pcm_read,
+ s5pc1xx_pcm_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pcm_writefn[] = {
+ s5pc1xx_pcm_write,
+ s5pc1xx_pcm_write,
+ s5pc1xx_pcm_write
+};
+
+static void s5pc1xx_pcm_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+ uint64_t last;
+ int i;
+
+ qemu_put_be32s(f, &s->ctl);
+ qemu_put_be32s(f, &s->clk_ctl);
+ qemu_put_be32s(f, &s->irq_ctl);
+ qemu_put_be32s(f, &s->irq_stat);
+ qemu_put_be32s(f, &s->fifo_stat);
+
+ for (i = 0; i < 32; i++) {
+ qemu_put_be16s(f, &s->rx.fifo[i]);
+ }
+
+ qemu_put_be64s(f, &s->rx.write_idx);
+ qemu_put_be64s(f, &s->rx.read_idx);
+ qemu_put_8s (f, &s->rx.fifo_dipstick);
+ qemu_put_8s (f, &s->rx.delay);
+
+ for (i = 0; i < 32; i++) {
+ qemu_put_be16s(f, &s->tx.fifo[i]);
+ }
+
+ qemu_put_be64s(f, &s->tx.write_idx);
+ qemu_put_be64s(f, &s->tx.read_idx);
+ qemu_put_8s (f, &s->tx.fifo_dipstick);
+ qemu_put_8s (f, &s->tx.delay);
+
+ qemu_put_be16s(f, &s->sync_div);
+ qemu_put_be32s(f, &s->sync_freq);
+ qemu_put_be32s(f, &s->sclk_freq);
+
+ last = qemu_get_clock(vm_clock) - s->last_pcm_time;
+ qemu_put_be64s(f, &last);
+
+ qemu_put_8s(f, &s->sclk_en);
+ qemu_put_8s(f, &s->pcm_io_en);
+
+ qemu_put_timer(f, s->pcm_timer);
+}
+
+static int s5pc1xx_pcm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxPCMState *s = (S5pc1xxPCMState *)opaque;
+ uint64_t last;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->ctl);
+ qemu_get_be32s(f, &s->clk_ctl);
+ qemu_get_be32s(f, &s->irq_ctl);
+ qemu_get_be32s(f, &s->irq_stat);
+ qemu_get_be32s(f, &s->fifo_stat);
+
+ for (i = 0; i < 32; i++) {
+ qemu_get_be16s(f, &s->rx.fifo[i]);
+ }
+
+ qemu_get_be64s(f, &s->rx.write_idx);
+ qemu_get_be64s(f, &s->rx.read_idx);
+ qemu_get_8s (f, &s->rx.fifo_dipstick);
+ qemu_get_8s (f, &s->rx.delay);
+
+ for (i = 0; i < 32; i++) {
+ qemu_get_be16s(f, &s->tx.fifo[i]);
+ }
+
+ qemu_get_be64s(f, &s->tx.write_idx);
+ qemu_get_be64s(f, &s->tx.read_idx);
+ qemu_get_8s (f, &s->tx.fifo_dipstick);
+ qemu_get_8s (f, &s->tx.delay);
+
+ qemu_get_be16s(f, &s->sync_div);
+ qemu_get_be32s(f, &s->sync_freq);
+ qemu_get_be32s(f, &s->sclk_freq);
+
+ qemu_get_be64s(f, &last);
+ s->last_pcm_time = qemu_get_clock(vm_clock) - last;
+
+ qemu_get_8s(f, &s->sclk_en);
+ qemu_get_8s(f, &s->pcm_io_en);
+
+ qemu_get_timer(f, s->pcm_timer);
+
+ return 0;
+}
+
+DeviceState *s5pc1xx_pcm_init(target_phys_addr_t base, int instance,
+ qemu_irq irq)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.pcm");
+ qdev_prop_set_uint32(dev, "instance", instance);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* PCM initialization */
+static int s5pc1xx_pcm_init1(SysBusDevice *dev)
+{
+ S5pc1xxPCMState *s = FROM_SYSBUS(S5pc1xxPCMState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_pcm_readfn, s5pc1xx_pcm_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_PCM_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_PCM, s->instance,
+ s5pc1xx_pcm_gpio_readfn,
+ s5pc1xx_pcm_gpio_writefn, NULL, s);
+ s->pcm_timer = qemu_new_timer(vm_clock, s5pc1xx_pcm_sync, s);
+
+ s5pc1xx_pcm_reset(&s->busdev.qdev);
+
+ register_savevm(&dev->qdev, "s5pc1xx.pcm", s->instance, 1,
+ s5pc1xx_pcm_save, s5pc1xx_pcm_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_pcm_info = {
+ .init = s5pc1xx_pcm_init1,
+ .qdev.name = "s5pc1xx.pcm",
+ .qdev.size = sizeof(S5pc1xxPCMState),
+ .qdev.reset = s5pc1xx_pcm_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("instance", S5pc1xxPCMState, instance, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_pcm_register(void)
+{
+ sysbus_register_withprop(&s5pc1xx_pcm_info);
+}
+
+device_init(s5pc1xx_pcm_register)
--- /dev/null
+/*
+ * S5PC1XX Power Management Unit controller,
+ * Maxim MAX17040 Fuel Gauge,
+ * Maxim MAX8998 Battery Charger and Real Time Clock.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "smbus.h"
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+
+
+#define S5PC110_PMU_SFR_SIZE 0x8000
+
+
+typedef struct S5pc1xxPMUState {
+ SysBusDevice busdev;
+
+ uint32_t osc_con;
+ uint32_t rst_stat;
+ uint32_t pwr_cfg;
+ uint32_t eint_wakeup_mask;
+ uint32_t wakeup_mask;
+ uint32_t pwr_mode;
+ uint32_t normal_cfg;
+ uint32_t idle_cfg;
+ uint32_t stop_cfg;
+ uint32_t stop_mem_cfg;
+ uint32_t sleep_cfg;
+ uint32_t osc_freq;
+ uint32_t osc_stable;
+ uint32_t pwr_stable;
+ uint32_t mtc_stable;
+ uint32_t clamp_stable;
+ uint32_t wakeup_stat;
+ uint32_t blk_pwr_stat;
+ uint32_t body_bias_con;
+ uint32_t ion_skew_con;
+ uint32_t ion_skew_mon;
+ uint32_t ioff_skew_con;
+ uint32_t ioff_skew_mon;
+ uint32_t others;
+ uint32_t om_stat;
+ uint32_t mie_control;
+ uint32_t hdmi_control;
+ uint32_t usb_phy_control;
+ uint32_t dac_control;
+ uint32_t mipi_dphy_control;
+ uint32_t adc_control;
+ uint32_t ps_hold_control;
+ uint32_t inform0;
+ uint32_t inform1;
+ uint32_t inform2;
+ uint32_t inform3;
+ uint32_t inform4;
+ uint32_t inform5;
+ uint32_t inform6;
+ uint32_t inform7;
+} S5pc1xxPMUState;
+
+
+static uint32_t s5pc1xx_pmu_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+ target_phys_addr_t test_offset = offset + 0x8000;
+
+ if (offset & 3) {
+ hw_error("s5pc1xx.pmu: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ switch (test_offset) {
+ case 0x8000:
+ return s->osc_con;
+ case 0xA000:
+ return s->rst_stat;
+ case 0xC000:
+ return s->pwr_cfg;
+ case 0xC004:
+ return s->eint_wakeup_mask;
+ case 0xC008:
+ return s->wakeup_mask;
+ case 0xC00C:
+ return s->pwr_mode;
+ case 0xC010:
+ return s->normal_cfg;
+ case 0xC020:
+ return s->idle_cfg;
+ case 0xC030:
+ return s->stop_cfg;
+ case 0xC034:
+ return s->stop_mem_cfg;
+ case 0xC040:
+ return s->sleep_cfg;
+ case 0xC100:
+ return s->osc_freq;
+ case 0xC104:
+ return s->osc_stable;
+ case 0xC108:
+ return s->pwr_stable;
+ case 0xC110:
+ return s->mtc_stable;
+ case 0xC114:
+ return s->clamp_stable;
+ case 0xC200:
+ return s->wakeup_stat;
+ case 0xC204:
+ return s->blk_pwr_stat;
+ case 0xC300:
+ return s->body_bias_con;
+ case 0xC310:
+ return s->ion_skew_con;
+ case 0xC314:
+ return s->ion_skew_mon;
+ case 0xC320:
+ return s->ioff_skew_con;
+ case 0xC324:
+ return s->ioff_skew_mon;
+ case 0xE000:
+ return s->others;
+ case 0xE100:
+ return s->om_stat;
+ case 0xE800:
+ return s->mie_control;
+ case 0xE804:
+ return s->hdmi_control;
+ case 0xE80C:
+ return s->usb_phy_control;
+ case 0xE810:
+ return s->dac_control;
+ case 0xE814:
+ return s->mipi_dphy_control;
+ case 0xE818:
+ return s->adc_control;
+ case 0xE81C:
+ return s->ps_hold_control;
+ case 0xF000:
+ return s->inform0;
+ case 0xF004:
+ return s->inform1;
+ case 0xF008:
+ return s->inform2;
+ case 0xF00C:
+ return s->inform3;
+ case 0xF010:
+ return s->inform4;
+ case 0xF014:
+ return s->inform5;
+ case 0xF018:
+ return s->inform6;
+ case 0xF01C:
+ return s->inform7;
+ default:
+ hw_error("s5pc1xx.pmu: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static void s5pc1xx_pmu_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+ target_phys_addr_t test_offset = offset + 0x8000;
+
+ /* TODO: Check if writing any values should change emulation flow */
+
+ if (offset & 3) {
+ hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+
+ switch (test_offset) {
+ case 0x8000:
+ s->osc_con = val;
+ break;
+ case 0xC000:
+ s->pwr_cfg = val;
+ break;
+ case 0xC004:
+ s->eint_wakeup_mask = val;
+ break;
+ case 0xC008:
+ s->wakeup_mask = val;
+ break;
+ case 0xC00C:
+ s->pwr_mode = val;
+ break;
+ case 0xC010:
+ s->normal_cfg = val;
+ break;
+ case 0xC020:
+ s->idle_cfg = val;
+ break;
+ case 0xC030:
+ s->stop_cfg = val;
+ break;
+ case 0xC034:
+ s->stop_mem_cfg = val;
+ break;
+ case 0xC040:
+ s->sleep_cfg = val;
+ break;
+ case 0xC100:
+ s->osc_freq = val;
+ break;
+ case 0xC104:
+ s->osc_stable = val;
+ break;
+ case 0xC108:
+ s->pwr_stable = val;
+ break;
+ case 0xC110:
+ s->mtc_stable = val;
+ break;
+ case 0xC114:
+ s->clamp_stable = val;
+ break;
+ case 0xC200:
+ s->wakeup_stat = val;
+ break;
+ case 0xC300:
+ s->body_bias_con = val;
+ break;
+ case 0xC310:
+ s->ion_skew_con = val;
+ break;
+ case 0xC320:
+ s->ioff_skew_con = val;
+ break;
+ case 0xE000:
+ s->others = val;
+ break;
+ case 0xE800:
+ s->mie_control = val;
+ break;
+ case 0xE804:
+ s->hdmi_control = val;
+ break;
+ case 0xE80C:
+ s->usb_phy_control = val;
+ break;
+ case 0xE810:
+ s->dac_control = val;
+ break;
+ case 0xE814:
+ s->mipi_dphy_control = val;
+ break;
+ case 0xE818:
+ s->adc_control = val;
+ break;
+ case 0xE81C:
+ s->ps_hold_control = val;
+ if ((val & (1 << 8)) == 0) {
+ qemu_system_shutdown_request();
+ }
+ break;
+ case 0xF000:
+ s->inform0 = val;
+ break;
+ case 0xF004:
+ s->inform1 = val;
+ break;
+ case 0xF008:
+ s->inform2 = val;
+ break;
+ case 0xF00C:
+ s->inform3 = val;
+ break;
+ case 0xF010:
+ s->inform4 = val;
+ break;
+ case 0xF014:
+ s->inform5 = val;
+ break;
+ case 0xF018:
+ s->inform6 = val;
+ break;
+ case 0xF01C:
+ s->inform7 = val;
+ break;
+ case 0xA000: /* rst_stat */
+ case 0xC204: /* blk_pwr_stat */
+ case 0xC314: /* ion_skew_mon */
+ case 0xC324: /* ioff_skew_mon */
+ case 0xE100: /* om_stat */
+ hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+ break;
+ default:
+ hw_error("s5pc1xx.pmu: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pmu_readfn[] = {
+ s5pc1xx_pmu_read,
+ s5pc1xx_pmu_read,
+ s5pc1xx_pmu_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pmu_writefn[] = {
+ s5pc1xx_pmu_write,
+ s5pc1xx_pmu_write,
+ s5pc1xx_pmu_write
+};
+
+static void s5pc1xx_pmu_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+ qemu_put_be32s(f, &s->osc_con);
+ qemu_put_be32s(f, &s->rst_stat);
+ qemu_put_be32s(f, &s->pwr_cfg);
+ qemu_put_be32s(f, &s->eint_wakeup_mask);
+ qemu_put_be32s(f, &s->wakeup_mask);
+ qemu_put_be32s(f, &s->pwr_mode);
+ qemu_put_be32s(f, &s->normal_cfg);
+ qemu_put_be32s(f, &s->idle_cfg);
+ qemu_put_be32s(f, &s->stop_cfg);
+ qemu_put_be32s(f, &s->stop_mem_cfg);
+ qemu_put_be32s(f, &s->sleep_cfg);
+ qemu_put_be32s(f, &s->osc_freq);
+ qemu_put_be32s(f, &s->osc_stable);
+ qemu_put_be32s(f, &s->pwr_stable);
+ qemu_put_be32s(f, &s->mtc_stable);
+ qemu_put_be32s(f, &s->clamp_stable);
+ qemu_put_be32s(f, &s->wakeup_stat);
+ qemu_put_be32s(f, &s->blk_pwr_stat);
+ qemu_put_be32s(f, &s->body_bias_con);
+ qemu_put_be32s(f, &s->ion_skew_con);
+ qemu_put_be32s(f, &s->ion_skew_mon);
+ qemu_put_be32s(f, &s->ioff_skew_con);
+ qemu_put_be32s(f, &s->ioff_skew_mon);
+ qemu_put_be32s(f, &s->others);
+ qemu_put_be32s(f, &s->om_stat);
+ qemu_put_be32s(f, &s->mie_control);
+ qemu_put_be32s(f, &s->hdmi_control);
+ qemu_put_be32s(f, &s->usb_phy_control);
+ qemu_put_be32s(f, &s->dac_control);
+ qemu_put_be32s(f, &s->mipi_dphy_control);
+ qemu_put_be32s(f, &s->adc_control);
+ qemu_put_be32s(f, &s->ps_hold_control);
+ qemu_put_be32s(f, &s->inform0);
+ qemu_put_be32s(f, &s->inform1);
+ qemu_put_be32s(f, &s->inform2);
+ qemu_put_be32s(f, &s->inform3);
+ qemu_put_be32s(f, &s->inform4);
+ qemu_put_be32s(f, &s->inform5);
+ qemu_put_be32s(f, &s->inform6);
+ qemu_put_be32s(f, &s->inform7);
+}
+
+static int s5pc1xx_pmu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->osc_con);
+ qemu_get_be32s(f, &s->rst_stat);
+ qemu_get_be32s(f, &s->pwr_cfg);
+ qemu_get_be32s(f, &s->eint_wakeup_mask);
+ qemu_get_be32s(f, &s->wakeup_mask);
+ qemu_get_be32s(f, &s->pwr_mode);
+ qemu_get_be32s(f, &s->normal_cfg);
+ qemu_get_be32s(f, &s->idle_cfg);
+ qemu_get_be32s(f, &s->stop_cfg);
+ qemu_get_be32s(f, &s->stop_mem_cfg);
+ qemu_get_be32s(f, &s->sleep_cfg);
+ qemu_get_be32s(f, &s->osc_freq);
+ qemu_get_be32s(f, &s->osc_stable);
+ qemu_get_be32s(f, &s->pwr_stable);
+ qemu_get_be32s(f, &s->mtc_stable);
+ qemu_get_be32s(f, &s->clamp_stable);
+ qemu_get_be32s(f, &s->wakeup_stat);
+ qemu_get_be32s(f, &s->blk_pwr_stat);
+ qemu_get_be32s(f, &s->body_bias_con);
+ qemu_get_be32s(f, &s->ion_skew_con);
+ qemu_get_be32s(f, &s->ion_skew_mon);
+ qemu_get_be32s(f, &s->ioff_skew_con);
+ qemu_get_be32s(f, &s->ioff_skew_mon);
+ qemu_get_be32s(f, &s->others);
+ qemu_get_be32s(f, &s->om_stat);
+ qemu_get_be32s(f, &s->mie_control);
+ qemu_get_be32s(f, &s->hdmi_control);
+ qemu_get_be32s(f, &s->usb_phy_control);
+ qemu_get_be32s(f, &s->dac_control);
+ qemu_get_be32s(f, &s->mipi_dphy_control);
+ qemu_get_be32s(f, &s->adc_control);
+ qemu_get_be32s(f, &s->ps_hold_control);
+ qemu_get_be32s(f, &s->inform0);
+ qemu_get_be32s(f, &s->inform1);
+ qemu_get_be32s(f, &s->inform2);
+ qemu_get_be32s(f, &s->inform3);
+ qemu_get_be32s(f, &s->inform4);
+ qemu_get_be32s(f, &s->inform5);
+ qemu_get_be32s(f, &s->inform6);
+ qemu_get_be32s(f, &s->inform7);
+
+ return 0;
+}
+
+static void s5pc1xx_pmu_reset(void *opaque)
+{
+ S5pc1xxPMUState *s = (S5pc1xxPMUState *)opaque;
+
+ s->osc_con = 0x00000003;
+ s->rst_stat = 0x00000001;
+ s->normal_cfg = 0xFFFFFFBF;
+ s->idle_cfg = 0x60000000;
+ s->stop_cfg = 0x96000000;
+ s->stop_mem_cfg = 0x000000FF;
+ s->osc_freq = 0x0000000F;
+ s->osc_stable = 0x000FFFFF;
+ s->mtc_stable = 0xFFFFFFFF;
+ s->clamp_stable = 0x03FF03FF;
+ s->blk_pwr_stat = 0x000000BF;
+ s->body_bias_con = 0x00000606;
+ s->mie_control = 0x00000001;
+ s->hdmi_control = 0x00960000;
+ s->dac_control = 0x00000001;
+ s->ps_hold_control = 0x00005301;
+}
+
+static int s5pc1xx_pmu_init(SysBusDevice *dev)
+{
+ S5pc1xxPMUState *s = FROM_SYSBUS(S5pc1xxPMUState, dev);
+ int iomemtype;
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_pmu_readfn, s5pc1xx_pmu_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC110_PMU_SFR_SIZE, iomemtype);
+
+ s5pc1xx_pmu_reset(s);
+
+ qemu_register_reset(s5pc1xx_pmu_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.pmu", -1, 1,
+ s5pc1xx_pmu_save, s5pc1xx_pmu_load, s);
+
+ return 0;
+}
+
+
+/* Maxim MAX17040 Fuel Gauge */
+
+#define MAX17040_VCELL_MSB 0x02
+#define MAX17040_VCELL_LSB 0x03
+#define MAX17040_SOC_MSB 0x04
+#define MAX17040_SOC_LSB 0x05
+#define MAX17040_MODE_MSB 0x06
+#define MAX17040_MODE_LSB 0x07
+#define MAX17040_VER_MSB 0x08
+#define MAX17040_VER_LSB 0x09
+#define MAX17040_RCOMP_MSB 0x0C
+#define MAX17040_RCOMP_LSB 0x0D
+#define MAX17040_CMD_MSB 0xFE
+#define MAX17040_CMD_LSB 0xFF
+
+#define MAX17040_DELAY 1000
+#define MAX17040_BATTERY_FULL 95
+#define MAX17040_BATTERY_CHARGE 40
+
+
+typedef struct MAX17040State {
+ SMBusDevice smbusdev;
+
+ uint16_t charge;
+ uint16_t vcell;
+ uint16_t mode;
+ uint16_t ver;
+ uint16_t rcomp;
+ uint16_t cmd;
+} MAX17040State;
+
+static void max17040_reset(void *opaque)
+{
+ MAX17040State *s = (MAX17040State *)opaque;
+
+ s->charge = MAX17040_BATTERY_CHARGE << 8;
+ s->vcell = 4200;
+ s->mode = 0;
+ s->ver = 0xAA28;
+ s->rcomp = 0;
+ s->cmd = 0;
+}
+
+static void max17040_write_data(SMBusDevice *dev, uint8_t cmd,
+ uint8_t *buf, int len)
+{
+ MAX17040State *s = (MAX17040State *)dev;
+ int shift = 0, i;
+ uint16_t *reg = NULL;
+
+ switch (cmd) {
+ case MAX17040_VCELL_LSB:
+ shift = 1;
+ case MAX17040_VCELL_MSB:
+ reg = &s->vcell;
+ break;
+ case MAX17040_SOC_LSB:
+ shift = 1;
+ case MAX17040_SOC_MSB:
+ reg = &s->charge;
+ break;
+ case MAX17040_MODE_LSB:
+ shift = 1;
+ case MAX17040_MODE_MSB:
+ reg = &s->mode;
+ break;
+ case MAX17040_VER_LSB:
+ shift = 1;
+ case MAX17040_VER_MSB:
+ reg = &s->ver;
+ break;
+ case MAX17040_RCOMP_LSB:
+ shift = 1;
+ case MAX17040_RCOMP_MSB:
+ reg = &s->rcomp;
+ break;
+ case MAX17040_CMD_LSB:
+ shift = 1;
+ case MAX17040_CMD_MSB:
+ reg = &s->cmd;
+ break;
+ default:
+ hw_error("max17040: bad write offset 0x%x\n", cmd);
+ }
+
+ if (len > 2 - shift) {
+ hw_error("max17040: bad write length %d\n", len);
+ }
+
+ for (i = 0; i < len; i++) {
+ *((uint8_t *)reg + i + shift) = buf[i];
+ }
+}
+
+static uint8_t max17040_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+ MAX17040State *s = (MAX17040State *)dev;
+ int shift = 0;
+ uint16_t val;
+
+ if (n > 0) {
+ //bypass => todo check!!
+ //hw_error("max17040: bad read length %d\n", n);
+ }
+
+ switch (cmd) {
+ case MAX17040_VCELL_MSB:
+ shift = 1;
+ case MAX17040_VCELL_LSB:
+ val = s->vcell;
+ break;
+ case MAX17040_SOC_MSB:
+ shift = 1;
+ case MAX17040_SOC_LSB:
+ val = s->charge;
+ break;
+ case MAX17040_MODE_MSB:
+ shift = 1;
+ case MAX17040_MODE_LSB:
+ val = s->mode;
+ break;
+ case MAX17040_VER_MSB:
+ shift = 1;
+ case MAX17040_VER_LSB:
+ val = s->ver;
+ break;
+ case MAX17040_RCOMP_MSB:
+ shift = 1;
+ case MAX17040_RCOMP_LSB:
+ val = s->rcomp;
+ break;
+ case MAX17040_CMD_MSB:
+ shift = 1;
+ case MAX17040_CMD_LSB:
+ val = s->cmd;
+ break;
+ default:
+ hw_error("max17040: bad read offset 0x%x\n", cmd);
+ }
+
+ return ((val >> (shift * 8)) & 0xFF);
+}
+
+static void max17040_save(QEMUFile *f, void *opaque)
+{
+ MAX17040State *s = (MAX17040State *)opaque;
+
+ qemu_put_be16s(f, &s->charge);
+ qemu_put_be16s(f, &s->vcell);
+ qemu_put_be16s(f, &s->mode);
+ qemu_put_be16s(f, &s->ver);
+ qemu_put_be16s(f, &s->rcomp);
+ qemu_put_be16s(f, &s->cmd);
+}
+
+static int max17040_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MAX17040State *s = (MAX17040State *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be16s(f, &s->charge);
+ qemu_get_be16s(f, &s->vcell);
+ qemu_get_be16s(f, &s->mode);
+ qemu_get_be16s(f, &s->ver);
+ qemu_get_be16s(f, &s->rcomp);
+ qemu_get_be16s(f, &s->cmd);
+
+ return 0;
+}
+
+DeviceState *max17040_init(i2c_bus *bus, int addr)
+{
+ DeviceState *dev = qdev_create((BusState *)bus, "max17040");
+ qdev_init_nofail(dev);
+ i2c_set_slave_address((i2c_slave *)dev, addr);
+ return dev;
+}
+
+static int max17040_init1(SMBusDevice *dev)
+{
+ MAX17040State *s = (MAX17040State *) dev;
+ max17040_reset(s);
+ qemu_register_reset(max17040_reset, s);
+ register_savevm(&dev->i2c.qdev, "max17040", -1, 1,
+ max17040_save, max17040_load, s);
+ return 0;
+}
+
+static SMBusDeviceInfo max17040_info = {
+ .i2c.qdev.name = "max17040",
+ .i2c.qdev.size = sizeof(MAX17040State),
+ .init = max17040_init1,
+ .write_data = max17040_write_data,
+ .read_data = max17040_read_data
+};
+
+
+/* Maxim MAX8998 Battery Charger */
+
+#define MAX8998_REG_IRQ1 0
+#define MAX8998_REG_IRQ2 1
+#define MAX8998_REG_IRQ3 2
+#define MAX8998_REG_IRQ4 3
+#define MAX8998_REG_IRQM1 4
+#define MAX8998_REG_IRQM2 5
+#define MAX8998_REG_IRQM3 6
+#define MAX8998_REG_IRQM4 7
+#define MAX8998_REG_STATUS1 8
+#define MAX8998_REG_STATUS2 9
+#define MAX8998_REG_STATUSM1 10
+#define MAX8998_REG_STATUSM2 11
+#define MAX8998_REG_CHGR1 12
+#define MAX8998_REG_CHGR2 13
+#define MAX8998_REG_LDO_ACTIVE_DISCHARGE1 14
+#define MAX8998_REG_LDO_ACTIVE_DISCHARGE2 15
+#define MAX8998_REG_BUCK_ACTIVE_DISCHARGE3 16
+#define MAX8998_REG_ONOFF1 17
+#define MAX8998_REG_ONOFF2 18
+#define MAX8998_REG_ONOFF3 19
+#define MAX8998_REG_ONOFF4 20
+#define MAX8998_REG_BUCK1_DVSARM1 21
+#define MAX8998_REG_BUCK1_DVSARM2 22
+#define MAX8998_REG_BUCK1_DVSARM3 23
+#define MAX8998_REG_BUCK1_DVSARM4 24
+#define MAX8998_REG_BUCK2_DVSINT1 25
+#define MAX8998_REG_BUCK2_DVSINT2 26
+#define MAX8998_REG_BUCK3 27
+#define MAX8998_REG_BUCK4 28
+#define MAX8998_REG_LDO2_LDO3 29
+#define MAX8998_REG_LDO4 30
+#define MAX8998_REG_LDO5 31
+#define MAX8998_REG_LDO6 32
+#define MAX8998_REG_LDO7 33
+#define MAX8998_REG_LDO8_LDO9 34
+#define MAX8998_REG_LDO10_LDO11 35
+#define MAX8998_REG_LDO12 36
+#define MAX8998_REG_LDO13 37
+#define MAX8998_REG_LDO14 38
+#define MAX8998_REG_LDO15 39
+#define MAX8998_REG_LDO16 40
+#define MAX8998_REG_LDO17 41
+#define MAX8998_REG_BKCHR 42
+#define MAX8998_REG_LBCNFG1 43
+#define MAX8998_REG_LBCNFG2 44
+
+
+typedef struct MAX8998State {
+ SMBusDevice smbusdev;
+
+ uint32_t irq;
+ uint32_t irqm;
+ uint16_t status;
+ uint16_t statusm;
+ uint16_t chgr;
+ uint16_t ldo_active_discharge;
+ uint8_t buck_active_discharge;
+ uint32_t onoff;
+ uint32_t buck1_dvsarm;
+ uint16_t buck2_dvsint;
+ uint8_t buck3;
+ uint8_t buck4;
+ uint32_t ldo23_4_5_6;
+ uint32_t ldo7_89_1011_12;
+ uint32_t ldo13_14_15_16;
+ uint8_t ldo17;
+ uint8_t bkchr;
+ uint16_t lbcnfg;
+} MAX8998State;
+
+static void max8998_reset(void *opaque)
+{
+ MAX8998State *s = (MAX8998State *)opaque;
+
+ s->irq = 0;
+ s->irqm = 0;
+ s->status = 0;
+ s->statusm = 0xEFFF;
+ s->chgr = 0xA80;
+ s->ldo_active_discharge = 0;
+ s->buck_active_discharge = 0;
+ s->onoff = 0;
+ s->buck1_dvsarm = 0;
+ s->buck2_dvsint = 0;
+ s->buck3 = 0;
+ s->buck4 = 0;
+ s->ldo23_4_5_6 = 0;
+ s->ldo7_89_1011_12 = 0;
+ s->ldo13_14_15_16 = 0;
+ s->ldo17 = 0;
+ s->bkchr = 0;
+ s->lbcnfg = 0;
+}
+
+static uint8_t *max8998_get_reg_addr(MAX8998State *s, uint8_t cmd)
+{
+ int shift = 0;
+ uint8_t *reg = NULL;
+
+ switch (cmd) {
+ case MAX8998_REG_IRQ4:
+ case MAX8998_REG_IRQM4:
+ case MAX8998_REG_STATUS2:
+ case MAX8998_REG_STATUSM2:
+ case MAX8998_REG_CHGR2:
+ case MAX8998_REG_LDO_ACTIVE_DISCHARGE2:
+ case MAX8998_REG_BUCK_ACTIVE_DISCHARGE3:
+ case MAX8998_REG_ONOFF4:
+ case MAX8998_REG_BUCK1_DVSARM4:
+ case MAX8998_REG_BUCK2_DVSINT2:
+ case MAX8998_REG_BUCK3:
+ case MAX8998_REG_BUCK4:
+ case MAX8998_REG_LDO6:
+ case MAX8998_REG_LDO12:
+ case MAX8998_REG_LDO16:
+ case MAX8998_REG_LDO17:
+ case MAX8998_REG_BKCHR:
+ case MAX8998_REG_LBCNFG2:
+ break;
+ case MAX8998_REG_IRQ3:
+ case MAX8998_REG_IRQM3:
+ case MAX8998_REG_STATUS1:
+ case MAX8998_REG_STATUSM1:
+ case MAX8998_REG_CHGR1:
+ case MAX8998_REG_LDO_ACTIVE_DISCHARGE1:
+ case MAX8998_REG_ONOFF3:
+ case MAX8998_REG_BUCK1_DVSARM3:
+ case MAX8998_REG_BUCK2_DVSINT1:
+ case MAX8998_REG_LDO5:
+ case MAX8998_REG_LDO10_LDO11:
+ case MAX8998_REG_LDO15:
+ case MAX8998_REG_LBCNFG1:
+ shift = 1;
+ break;
+ case MAX8998_REG_IRQ2:
+ case MAX8998_REG_IRQM2:
+ case MAX8998_REG_ONOFF2:
+ case MAX8998_REG_BUCK1_DVSARM2:
+ case MAX8998_REG_LDO4:
+ case MAX8998_REG_LDO8_LDO9:
+ case MAX8998_REG_LDO14:
+ shift = 2;
+ break;
+ case MAX8998_REG_IRQ1:
+ case MAX8998_REG_IRQM1:
+ case MAX8998_REG_ONOFF1:
+ case MAX8998_REG_BUCK1_DVSARM1:
+ case MAX8998_REG_LDO2_LDO3:
+ case MAX8998_REG_LDO7:
+ case MAX8998_REG_LDO13:
+ shift = 3;
+ break;
+ default:
+ hw_error("max8998: bad write offset 0x%x\n", cmd);
+ }
+
+ switch (cmd) {
+ case MAX8998_REG_IRQ1:
+ case MAX8998_REG_IRQ2:
+ case MAX8998_REG_IRQ3:
+ case MAX8998_REG_IRQ4:
+ reg = (uint8_t *)&s->irq;
+ break;
+ case MAX8998_REG_IRQM1:
+ case MAX8998_REG_IRQM2:
+ case MAX8998_REG_IRQM3:
+ case MAX8998_REG_IRQM4:
+ reg = (uint8_t *)&s->irqm;
+ break;
+ case MAX8998_REG_STATUS1:
+ case MAX8998_REG_STATUS2:
+ reg = (uint8_t *)&s->status;
+ break;
+ case MAX8998_REG_STATUSM1:
+ case MAX8998_REG_STATUSM2:
+ reg = (uint8_t *)&s->statusm;
+ break;
+ case MAX8998_REG_CHGR1:
+ case MAX8998_REG_CHGR2:
+ reg = (uint8_t *)&s->chgr;
+ break;
+ case MAX8998_REG_LDO_ACTIVE_DISCHARGE1:
+ case MAX8998_REG_LDO_ACTIVE_DISCHARGE2:
+ reg = (uint8_t *)&s->ldo_active_discharge;
+ break;
+ case MAX8998_REG_BUCK_ACTIVE_DISCHARGE3:
+ reg = (uint8_t *)&s->buck_active_discharge;
+ break;
+ case MAX8998_REG_ONOFF1:
+ case MAX8998_REG_ONOFF2:
+ case MAX8998_REG_ONOFF3:
+ case MAX8998_REG_ONOFF4:
+ reg = (uint8_t *)&s->onoff;
+ break;
+ case MAX8998_REG_BUCK1_DVSARM1:
+ case MAX8998_REG_BUCK1_DVSARM2:
+ case MAX8998_REG_BUCK1_DVSARM3:
+ case MAX8998_REG_BUCK1_DVSARM4:
+ reg = (uint8_t *)&s->buck1_dvsarm;
+ break;
+ case MAX8998_REG_BUCK2_DVSINT1:
+ case MAX8998_REG_BUCK2_DVSINT2:
+ reg = (uint8_t *)&s->buck2_dvsint;
+ break;
+ case MAX8998_REG_BUCK3:
+ reg = (uint8_t *)&s->buck3;
+ break;
+ case MAX8998_REG_BUCK4:
+ reg = (uint8_t *)&s->buck4;
+ break;
+ case MAX8998_REG_LDO2_LDO3:
+ case MAX8998_REG_LDO4:
+ case MAX8998_REG_LDO5:
+ case MAX8998_REG_LDO6:
+ reg = (uint8_t *)&s->ldo23_4_5_6;
+ break;
+ case MAX8998_REG_LDO7:
+ case MAX8998_REG_LDO8_LDO9:
+ case MAX8998_REG_LDO10_LDO11:
+ case MAX8998_REG_LDO12:
+ reg = (uint8_t *)&s->ldo7_89_1011_12;
+ break;
+ case MAX8998_REG_LDO13:
+ case MAX8998_REG_LDO14:
+ case MAX8998_REG_LDO15:
+ case MAX8998_REG_LDO16:
+ reg = (uint8_t *)&s->ldo13_14_15_16;
+ break;
+ case MAX8998_REG_LDO17:
+ reg = (uint8_t *)&s->ldo17;
+ break;
+ case MAX8998_REG_BKCHR:
+ reg = (uint8_t *)&s->bkchr;
+ break;
+ case MAX8998_REG_LBCNFG1:
+ case MAX8998_REG_LBCNFG2:
+ reg = (uint8_t *)&s->lbcnfg;
+ break;
+ default:
+ hw_error("max8998: bad write offset 0x%x\n", cmd);
+ }
+
+ return (reg + shift);
+}
+
+static void max8998_write_data(SMBusDevice *dev, uint8_t cmd,
+ uint8_t *buf, int len)
+{
+ MAX8998State *s = (MAX8998State *)dev;
+
+ if (len > 1) {
+ hw_error("max8998: bad write length %d\n", len);
+ }
+
+ *(max8998_get_reg_addr(s, cmd)) = buf[0];
+}
+
+static uint8_t max8998_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+ MAX8998State *s = (MAX8998State *)dev;
+
+ if (n > 0 && cmd != MAX8998_REG_IRQ1) {
+ hw_error("max8998: bad read length %d\n", n);
+ }
+
+ return *(max8998_get_reg_addr(s, cmd) + n);
+}
+
+static void max8998_save(QEMUFile *f, void *opaque)
+{
+ MAX8998State *s = (MAX8998State *)opaque;
+
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_be32s(f, &s->irqm);
+ qemu_put_be16s(f, &s->status);
+ qemu_put_be16s(f, &s->statusm);
+ qemu_put_be16s(f, &s->chgr);
+ qemu_put_be16s(f, &s->ldo_active_discharge);
+ qemu_put_8s (f, &s->buck_active_discharge);
+ qemu_put_be32s(f, &s->onoff);
+ qemu_put_be32s(f, &s->buck1_dvsarm);
+ qemu_put_be16s(f, &s->buck2_dvsint);
+ qemu_put_8s (f, &s->buck3);
+ qemu_put_8s (f, &s->buck4);
+ qemu_put_be32s(f, &s->ldo23_4_5_6);
+ qemu_put_be32s(f, &s->ldo7_89_1011_12);
+ qemu_put_be32s(f, &s->ldo13_14_15_16);
+ qemu_put_8s (f, &s->ldo17);
+ qemu_put_8s (f, &s->bkchr);
+ qemu_put_be16s(f, &s->lbcnfg);
+}
+
+static int max8998_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MAX8998State *s = (MAX8998State *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_be32s(f, &s->irqm);
+ qemu_get_be16s(f, &s->status);
+ qemu_get_be16s(f, &s->statusm);
+ qemu_get_be16s(f, &s->chgr);
+ qemu_get_be16s(f, &s->ldo_active_discharge);
+ qemu_get_8s (f, &s->buck_active_discharge);
+ qemu_get_be32s(f, &s->onoff);
+ qemu_get_be32s(f, &s->buck1_dvsarm);
+ qemu_get_be16s(f, &s->buck2_dvsint);
+ qemu_get_8s (f, &s->buck3);
+ qemu_get_8s (f, &s->buck4);
+ qemu_get_be32s(f, &s->ldo23_4_5_6);
+ qemu_get_be32s(f, &s->ldo7_89_1011_12);
+ qemu_get_be32s(f, &s->ldo13_14_15_16);
+ qemu_get_8s (f, &s->ldo17);
+ qemu_get_8s (f, &s->bkchr);
+ qemu_get_be16s(f, &s->lbcnfg);
+
+ return 0;
+}
+
+DeviceState *max8998_init(i2c_bus *bus, int addr)
+{
+ DeviceState *dev = qdev_create((BusState *)bus, "max8998");
+ qdev_init_nofail(dev);
+ i2c_set_slave_address((i2c_slave *)dev, addr);
+ return dev;
+}
+
+static int max8998_init1(SMBusDevice *dev)
+{
+ MAX8998State *s = (MAX8998State *) dev;
+ max8998_reset(s);
+ qemu_register_reset(max8998_reset, s);
+ register_savevm(&dev->i2c.qdev, "max8998", -1, 1,
+ max8998_save, max8998_load, s);
+ return 0;
+}
+
+static SMBusDeviceInfo max8998_info = {
+ .i2c.qdev.name = "max8998",
+ .i2c.qdev.size = sizeof(MAX8998State),
+ .init = max8998_init1,
+ .write_data = max8998_write_data,
+ .read_data = max8998_read_data
+};
+
+
+/* Maxim MAX8998 Real Time Clock */
+
+/* MX8998 RTC I2C MAP */
+#define RTC_SEC 0x0 /* second 00-59 */
+#define RTC_MIN 0x1 /* minute 00-59 */
+#define RTC_HR 0x2 /* hour AM/PM 1-12 or 00-23 */
+#define RTC_DAY 0x3 /* weekday 1-7 */
+#define RTC_DATE 0x4 /* date 01-31 */
+#define RTC_MT 0x5 /* month 01-12 */
+#define RTC_YEAR 0x6 /* year 00-99 */
+#define RTC_CEN 0x7 /* century 00-99 */
+
+#define RTC_CON 0x1A
+ #define RTC_EN 0x01 /* RTC control enable */
+
+#define MAX8998_REG(x, y) (0x8 * y + x)
+
+/* Time Keeper */
+#define MAX8998_RTC 0
+/* Alarm0 */
+#define MAX8998_ALRM0 1
+/* Alarm1 */
+#define MAX8998_ALRM1 2
+/* Conf */
+#define MAX8998_ALRM0_CONF 0x18
+#define MAX8998_ALRM1_CONF 0x19
+
+#define MAX8998_ALRM_ON 0x77
+#define MAX8998_ALRM_OFF 0x0
+
+
+typedef struct MAX8998RTCState {
+ SMBusDevice smbusdev;
+
+ uint8_t regs[RTC_CON + 1];
+
+ /* seconds update */
+ QEMUTimer *seconds_timer;
+ struct tm current_tm;
+} MAX8998RTCState;
+
+static void max8998_rtc_seconds_update(void *opaque);
+
+/* Set default values for all fields */
+static void max8998_rtc_reset(void *opaque)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+ short i;
+
+ /* stop seconds timer */
+ s->regs[RTC_CON] = 0x0;
+ max8998_rtc_seconds_update(s);
+
+ for (i = RTC_SEC; i <= RTC_CEN; i++) {
+ s->regs[MAX8998_REG(i, MAX8998_RTC)] = 0;
+ }
+ /* get time from host */
+ qemu_get_timedate(&s->current_tm, 0);
+
+ /* start seconds timer */
+ s->regs[RTC_CON] |= RTC_EN;
+ max8998_rtc_seconds_update(s);
+}
+
+/* Get days in month. Month is between 0 and 11 */
+static int get_days_in_month(int month, int year)
+{
+ short d;
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ if ((unsigned)month >= 12) {
+ return 31;
+ }
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0) {
+ d++;
+ }
+ }
+ return d;
+}
+
+/* Update 'struct tm' to the next second */
+static void max8998_rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 8) {
+ tm->tm_wday = 1;
+ }
+ days_in_month = get_days_in_month(tm->tm_mon, tm->tm_year);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Using of qemu_timer to increase seconds */
+static void max8998_rtc_seconds_update(void *opaque)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+ uint64_t next_seconds_time;
+
+ if (s->regs[RTC_CON] & RTC_EN) {
+ max8998_rtc_next_second(&(s->current_tm));
+ next_seconds_time = qemu_get_clock(vm_clock) + get_ticks_per_sec();
+ qemu_mod_timer(s->seconds_timer, next_seconds_time);
+ } else {
+ qemu_del_timer(s->seconds_timer);
+ }
+}
+
+/* Convert time from bcd */
+static void max8998_rtc_set_time(MAX8998RTCState *s)
+{
+ struct tm *tm = &(s->current_tm);
+
+ tm->tm_sec = from_bcd(s->regs[RTC_SEC]);
+ tm->tm_min = from_bcd(s->regs[RTC_MIN]);
+ tm->tm_hour = from_bcd(s->regs[RTC_HR] & 0x3f);
+ tm->tm_mday = from_bcd(s->regs[RTC_DATE]);
+ tm->tm_mon = from_bcd(s->regs[RTC_MT]);
+ tm->tm_year = from_bcd(s->regs[RTC_YEAR]) +
+ from_bcd(s->regs[RTC_CEN]) * 100 - 1900;
+ tm->tm_wday = s->regs[RTC_DAY]; /* one decimal digit */
+}
+
+/* Convert time to bcd */
+static void max8998_rtc_read_time(MAX8998RTCState *s)
+{
+ const struct tm *tm = &(s->current_tm);
+
+ s->regs[RTC_SEC] = to_bcd(tm->tm_sec);
+ s->regs[RTC_MIN] = to_bcd(tm->tm_min);
+ s->regs[RTC_HR] = to_bcd(tm->tm_hour);
+ s->regs[RTC_DATE] = to_bcd(tm->tm_mday);
+ s->regs[RTC_MT] = to_bcd(tm->tm_mon);
+ s->regs[RTC_YEAR] = to_bcd(tm->tm_year % 100);
+ s->regs[RTC_CEN] = to_bcd((tm->tm_year + 1900) / 100);
+ s->regs[RTC_DAY] = tm->tm_wday; /* one decimal digit */
+}
+
+/* Write RTC MAX8998 by I2C through SMBus */
+static void max8998_rtc_write(SMBusDevice *dev, uint8_t cmd,
+ uint8_t *buf, int len)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)dev;
+ uint8_t *reg = NULL;
+ int i;
+
+ switch (cmd) {
+ case MAX8998_REG(RTC_SEC, MAX8998_RTC) ...
+ MAX8998_REG(RTC_CEN, MAX8998_RTC):
+ case MAX8998_ALRM0_CONF:
+ case MAX8998_ALRM1_CONF:
+ reg = &s->regs[cmd];
+ break;
+
+ /* TODO: Implement alarm support */
+ case MAX8998_REG(RTC_SEC, MAX8998_ALRM0) ...
+ MAX8998_REG(RTC_CEN, MAX8998_ALRM1):
+ return;
+
+ default:
+ hw_error("max8998-rtc: bad write offset 0x%x\n", cmd);
+ }
+
+ for (i = 0; i < len; i++) {
+ *((uint8_t *)reg + i) = buf[i];
+ }
+
+ if (cmd <= MAX8998_REG(RTC_CEN, MAX8998_RTC)) {
+ max8998_rtc_set_time(s);
+ }
+}
+
+/* Read RTC MAX8998 by I2C through SMBus */
+static uint8_t max8998_rtc_read(SMBusDevice *dev, uint8_t cmd, int n)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)dev;
+ uint16_t val;
+
+ if (n > 0) {
+ hw_error("max8998-rtc: bad read length %d\n", n);
+ }
+
+ switch (cmd) {
+ case MAX8998_REG(RTC_SEC, MAX8998_RTC) ...
+ MAX8998_REG(RTC_CEN, MAX8998_RTC):
+ max8998_rtc_read_time(s);
+ case MAX8998_ALRM0_CONF:
+ case MAX8998_ALRM1_CONF:
+ val = s->regs[cmd];
+ break;
+
+ /* TODO: Implement alarm support */
+ case MAX8998_REG(RTC_SEC, MAX8998_ALRM0) ...
+ MAX8998_REG(RTC_CEN, MAX8998_ALRM1):
+ return 0;
+
+ default:
+ hw_error("max8998-rtc: bad read offset 0x%x\n", cmd);
+ }
+
+ return (val & 0xFF);
+}
+
+static void max8998_rtc_save(QEMUFile *f, void *opaque)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+
+ max8998_rtc_read_time(s);
+
+ qemu_put_buffer(f, s->regs, sizeof(s->regs));
+ qemu_put_timer(f, s->seconds_timer);
+}
+
+static int max8998_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_buffer(f, s->regs, sizeof(s->regs));
+
+ qemu_get_timedate(&s->current_tm, 0);
+ max8998_rtc_read_time(s);
+
+ qemu_get_timer(f, s->seconds_timer);
+
+ return 0;
+}
+
+DeviceState *max8998_rtc_init(i2c_bus *bus, int addr)
+{
+ DeviceState *dev = qdev_create((BusState *)bus, "max8998-rtc");
+ qdev_init_nofail(dev);
+ i2c_set_slave_address((i2c_slave *)dev, addr);
+ return dev;
+}
+
+static int max8998_rtc_init1(SMBusDevice *dev)
+{
+ MAX8998RTCState *s = (MAX8998RTCState *) dev;
+
+ s->seconds_timer = qemu_new_timer(vm_clock, max8998_rtc_seconds_update, s);
+
+ /* initialize values */
+ max8998_rtc_reset(s);
+
+ qemu_register_reset(max8998_rtc_reset, s);
+ register_savevm(&dev->i2c.qdev, "max8998-rtc", -1, 1,
+ max8998_rtc_save, max8998_rtc_load, s);
+
+ return 0;
+}
+
+static SMBusDeviceInfo max8998_rtc_info = {
+ .i2c.qdev.name = "max8998-rtc",
+ .i2c.qdev.size = sizeof(MAX8998RTCState),
+ .init = max8998_rtc_init1,
+ .write_data = max8998_rtc_write,
+ .read_data = max8998_rtc_read
+};
+
+static void s5pc1xx_pmu_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.pmu", sizeof(S5pc1xxPMUState), s5pc1xx_pmu_init);
+ smbus_register_device(&max17040_info);
+ smbus_register_device(&max8998_info);
+ smbus_register_device(&max8998_rtc_info);
+}
+
+device_init(s5pc1xx_pmu_register_devices)
--- /dev/null
+/*
+ * PWM Timer for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define S5C_TCFG0 (0x00) /* R/W Timer Configuration
+ * Register 0 that configures two
+ * 8-bit Prescaler and DeadZone
+ * Length */
+#define S5C_TCFG1 (0x04) /* R/W Timer Configuration
+ * Register 1 that controls
+ * 5 MUX Select Bit */
+#define S5C_TCON (0x08) /* R/W Timer Control Register */
+#define S5C_TINT_CSTAT (0x44) /* R/W Timer Interrupt Control and
+ * Status Register */
+/* TCFG0 */
+#define S5C_TCFG0_PRESCALER0_SHIFT (0)
+#define S5C_TCFG0_PRESCALER1_SHIFT (8)
+
+/* TCFG1 */
+#define S5C_TCFG1_DIV_SHIFT(tmr) (tmr * 0x04)
+#define TCLK 0x5
+
+/* TINT_CSTAT */
+#define INT_EN(tmr) (1 << tmr)
+#define INT_STAT(tmr) (1 << (tmr+5))
+
+#define S5C_TIMERREG(tmr,reg) (0x0c + reg + (0x0c * tmr))
+
+/* R/W Timer 0..4 Count Buffer Register */
+#define S5C_TCNTB(tmr) S5C_TIMERREG(tmr, 0x00)
+
+/* R/W Timer 0..4 Compare Buffer Register */
+#define S5C_TCMPB(tmr) S5C_TIMERREG(tmr, 0x04)
+
+/* R Timer 0..4 Count Observation Register */
+#define S5C_TCNTO(tmr) S5C_TIMERREG(tmr, ((tmr == 4) ? 0x04 : 0x08))
+#define ALL_STAT (INT_STAT(0) | INT_STAT(1) | INT_STAT(2) | \
+ INT_STAT(3) | INT_STAT(4))
+
+#define S5PC1XX_PWM_REG_MEM_SIZE 0x50
+
+static const uint32_t S5C_TCON_RELOAD[5] = {(1 << 3), (1 << 11), (1 << 15), (1 << 19), (1 << 22)};
+static const uint32_t S5C_TCON_INVERT[5] = {(1 << 2), (1 << 10), (1 << 14), (1 << 18), (0) };
+static const uint32_t S5C_TCON_MANUALUPD[5] = {(1 << 1), (1 << 9), (1 << 13), (1 << 17), (1 << 21)};
+static const uint32_t S5C_TCON_START[5] = {(1 << 0), (1 << 8), (1 << 12), (1 << 16), (1 << 20)};
+
+
+struct S5pc1xxPWMState;
+
+typedef struct S5pc1xxPWMTimerState {
+ qemu_irq irq_inst; /* irq instance */
+ uint8_t tag; /* control tag and timer number */
+
+ QEMUTimer *pwm_timer;
+ uint32_t freq_out;
+ uint64_t last_pwm_time;
+
+ uint32_t tcntb; /* count buffer value */
+ uint64_t tcntb_in_qemu; /* tcntb measured in 1/(9GHz) units */
+ uint32_t tcnt; /* count initial value */
+ uint64_t tcnt_in_qemu; /* tcnt measured in 1/(9GHz) units */
+ uint32_t tcmpb; /* comparison buffer value */
+ uint32_t tcmp; /* comparison current value */
+
+ struct S5pc1xxPWMState *regs;
+} S5pc1xxPWMTimerState;
+
+typedef struct S5pc1xxPWMState {
+ SysBusDevice busdev;
+ uint8_t tag; /* control tag */
+ /* control regs */
+ uint32_t tcfg0;
+ uint32_t tcfg1;
+ uint32_t tcon;
+ uint32_t tint_cstat;
+ S5pc1xxPWMTimerState tmr[5]; /* timers settings */
+} S5pc1xxPWMState;
+
+
+/* Convert term to be measured in (1/pwm_timer_freq) units */
+static uint32_t s5pc1xx_pwm_convert_term(uint64_t term, uint32_t timer_freq)
+{
+ return muldiv64(term, timer_freq, get_ticks_per_sec());
+}
+
+/* Get buffer values */
+static void s5pc1xx_pwm_timer_renew(S5pc1xxPWMTimerState *t)
+{
+ t->tcmp = t->tcmpb;
+ t->tcnt = t->tcntb;
+ t->tcnt_in_qemu = t->tcntb_in_qemu;
+}
+
+/* Set timer */
+static void s5pc1xx_pwm_timer_start(S5pc1xxPWMTimerState *t)
+{
+ t->last_pwm_time = qemu_get_clock(vm_clock);
+ qemu_mod_timer(t->pwm_timer, t->last_pwm_time + t->tcnt_in_qemu);
+}
+
+/* Stop timer */
+static void s5pc1xx_pwm_timer_stop(S5pc1xxPWMTimerState *t)
+{
+ qemu_del_timer(t->pwm_timer);
+}
+
+/* Events when timer riches zero */
+static void s5pc1xx_pwm_timer_expiry(void *opaque)
+{
+ S5pc1xxPWMTimerState *t = (S5pc1xxPWMTimerState *)opaque;
+ uint8_t num = t->tag;
+
+ if (t->tag > 4)
+ hw_error("s5pc1xx.pwm: timer_expiry - wrong param (tag = %u)\n",
+ t->tag);
+
+ /* Interrupt raising */
+ t->regs->tint_cstat |= INT_STAT(num);
+ if (t->regs->tint_cstat & INT_EN(num))
+ qemu_irq_raise(t->irq_inst);
+
+ if (t->regs->tcon & S5C_TCON_RELOAD[num]) {
+ s5pc1xx_pwm_timer_renew(t);
+ s5pc1xx_pwm_timer_start(t);
+ }
+}
+
+/* Update timer frequency */
+static void s5pc1xx_pwm_timer_freq(S5pc1xxPWMTimerState *t)
+{
+ S5pc1xxClk clk;
+ uint8_t divisor;
+ uint8_t prescaler = 0;
+
+ if (t->tag > 4)
+ hw_error("s5pc1xx.pwm: timer_freq - wrong param (tag = %u)\n",
+ t->tag);
+
+ switch (t->regs->tcfg1 >> S5C_TCFG1_DIV_SHIFT(t->tag) & 0xf) {
+ case TCLK:
+ clk = s5pc1xx_findclk("sclk_pwm");
+ prescaler = 0;
+ divisor = 1;
+ break;
+ default:
+ clk = s5pc1xx_findclk("pclk_66");
+ switch (t->tag) {
+ case 0 ... 1:
+ prescaler =
+ t->regs->tcfg0 >> S5C_TCFG0_PRESCALER0_SHIFT & 0xFF;
+ break;
+ case 2 ... 4:
+ prescaler =
+ t->regs->tcfg0 >> S5C_TCFG0_PRESCALER1_SHIFT & 0xFF;
+ break;
+ }
+ divisor = 1 << (t->regs->tcfg1 >> S5C_TCFG1_DIV_SHIFT(t->tag) & 0xF);
+ }
+ t->freq_out = s5pc1xx_clk_getrate(clk) / (prescaler + 1) / divisor;
+}
+
+/* Read PWM by GPIO */
+static uint32_t s5pc1xx_pwm_gpio_read(void *opaque,
+ int io_index)
+{
+ S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+ uint8_t i, tout = 0;
+
+ if (s->tag < 255)
+ hw_error("s5pc1xx.pwm: gpio_read - wrong param (tag = %u)\n",
+ s->tag);
+
+ for (i = 0; i < 4; i++)
+ if (io_index == GPIO_PWM_TOUT(i)) {
+ tout = (s->tcon & S5C_TCON_INVERT[i]) ? 0 : 1;
+
+ if (s5pc1xx_pwm_convert_term(qemu_get_clock(vm_clock) - s->tmr[i].last_pwm_time,
+ s->tmr[i].freq_out) < s->tmr[i].tcmp)
+ tout = (s->tcon & S5C_TCON_INVERT[i]) ? 1 : 0;
+ }
+ return tout;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_pwm_gpio_readfn = s5pc1xx_pwm_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_pwm_gpio_writefn = s5pc1xx_empty_gpio_write; /* a plug */
+
+/* Read PWM by OS */
+static uint32_t s5pc1xx_pwm_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+ S5pc1xxPWMTimerState *tmr = s->tmr;
+ uint32_t tcnto;
+ uint8_t num;
+
+ if (s->tag < 255)
+ hw_error("s5pc1xx.pwm: read - wrong param (tag = %u)\n",
+ s->tag);
+
+ switch (offset) {
+ case S5C_TCFG0:
+ return s->tcfg0;
+
+ case S5C_TCFG1:
+ return s->tcfg1;
+
+ case S5C_TCON:
+ return s->tcon;
+
+ case S5C_TINT_CSTAT:
+ return s->tint_cstat;
+
+ case S5C_TCNTB(0):
+ case S5C_TCNTB(1):
+ case S5C_TCNTB(2):
+ case S5C_TCNTB(3):
+ case S5C_TCNTB(4):
+ return tmr[(offset - S5C_TCNTB(0)) / 0x0C].tcntb;
+
+ case S5C_TCMPB(0):
+ case S5C_TCMPB(1):
+ case S5C_TCMPB(2):
+ case S5C_TCMPB(3):
+ return tmr[(offset - S5C_TCMPB(0)) / 0x0C].tcmpb;
+
+ case S5C_TCNTO(0):
+ case S5C_TCNTO(1):
+ case S5C_TCNTO(2):
+ case S5C_TCNTO(3):
+ case S5C_TCNTO(4):
+ num =
+ (offset == S5C_TCNTO(4)) ? 4 : ((offset - S5C_TCNTO(0)) / 0x0C);
+
+ if (!(s->tcon & S5C_TCON_START[num]))
+ return tmr[num].tcnt;
+
+ if (qemu_get_clock(vm_clock) - tmr[num].last_pwm_time > tmr[num].tcnt_in_qemu)
+ return 0;
+
+ tcnto =
+ tmr[num].tcnt -
+ s5pc1xx_pwm_convert_term(qemu_get_clock(vm_clock) -
+ tmr[num].last_pwm_time,
+ tmr[num].freq_out);
+ return tcnto;
+
+ default:
+ hw_error("s5pc1xx.pwm: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* Write PWM by OS */
+static void s5pc1xx_pwm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+ S5pc1xxPWMTimerState *tmr = s->tmr;
+ uint8_t num;
+
+ if (s->tag < 255)
+ hw_error("s5pc1xx.pwm: write - wrong param (tag = %u)\n",
+ s->tag);
+
+ switch (offset) {
+ case S5C_TCFG0:
+ s->tcfg0 = value;
+ for (num = 0; num < 5; num++)
+ s5pc1xx_pwm_timer_freq(&tmr[num]);
+ break;
+
+ case S5C_TCFG1:
+ s->tcfg1 = value;
+ for (num = 0; num < 5; num++)
+ s5pc1xx_pwm_timer_freq(&tmr[num]);
+ break;
+
+ case S5C_TCON:
+ /* start timer if it was stopped */
+ for (num = 0; num < 5; num++) {
+ if ((value & S5C_TCON_START[num]) >
+ (s->tcon & S5C_TCON_START[num]))
+ s5pc1xx_pwm_timer_start(&tmr[num]);
+
+ if ((value & S5C_TCON_START[num]) <
+ (s->tcon & S5C_TCON_START[num]))
+ s5pc1xx_pwm_timer_stop(&tmr[num]);
+
+ if (value & S5C_TCON_MANUALUPD[num]) {
+ s5pc1xx_pwm_timer_stop(&tmr[num]);
+ s5pc1xx_pwm_timer_renew(&tmr[num]);
+ }
+ }
+ s->tcon = value;
+ break;
+
+ case S5C_TINT_CSTAT:
+ for (num = 0; num < 5; num++)
+ if (value & INT_STAT(num))
+ qemu_irq_lower(tmr[num].irq_inst);
+ /* set TINT_CSTAT as value except *_STAT bits */
+ s->tint_cstat =
+ (s->tint_cstat & ALL_STAT) | (value & ~ALL_STAT);
+ /* clear *_STAT bits if they are set in value */
+ s->tint_cstat &= ~(value & ALL_STAT);
+ break;
+
+ case S5C_TCNTB(0):
+ case S5C_TCNTB(1):
+ case S5C_TCNTB(2):
+ case S5C_TCNTB(3):
+ case S5C_TCNTB(4):
+ num = (offset - S5C_TCNTB(0)) / 0x0C;
+ /* count buffer */
+ tmr[num].tcntb = value;
+ tmr[num].tcntb_in_qemu =
+ muldiv64(value, get_ticks_per_sec(), tmr[num].freq_out);
+ break;
+
+ case S5C_TCMPB(0):
+ case S5C_TCMPB(1):
+ case S5C_TCMPB(2):
+ case S5C_TCMPB(3):
+ /* comparison buffer */
+ tmr[(offset - S5C_TCMPB(0)) / 0x0C].tcmpb = value;
+ break;
+
+ default:
+ hw_error("s5pc1xx.pwm: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_pwm_readfn[] = {
+ s5pc1xx_pwm_read,
+ s5pc1xx_pwm_read,
+ s5pc1xx_pwm_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_pwm_writefn[] = {
+ s5pc1xx_pwm_write,
+ s5pc1xx_pwm_write,
+ s5pc1xx_pwm_write
+};
+
+static void s5pc1xx_pwm_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+ S5pc1xxPWMTimerState *tmr = s->tmr;
+ uint64_t last;
+ int num;
+
+ qemu_put_8s (f, &s->tag);
+ qemu_put_be32s(f, &s->tcfg0);
+ qemu_put_be32s(f, &s->tcfg1);
+ qemu_put_be32s(f, &s->tcon);
+ qemu_put_be32s(f, &s->tint_cstat);
+
+ for (num = 0; num < 5; num++) {
+ last = qemu_get_clock(vm_clock) - tmr[num].last_pwm_time;
+
+ qemu_put_8s (f, &tmr[num].tag);
+ qemu_put_be32s(f, &tmr[num].freq_out);
+ qemu_put_be32s(f, &tmr[num].tcntb);
+ qemu_put_be64s(f, &tmr[num].tcntb_in_qemu);
+ qemu_put_be32s(f, &tmr[num].tcnt);
+ qemu_put_be64s(f, &tmr[num].tcnt_in_qemu);
+ qemu_put_be32s(f, &tmr[num].tcmpb);
+ qemu_put_be32s(f, &tmr[num].tcmp);
+ qemu_put_be64s(f, &last);
+
+ qemu_put_timer(f, tmr[num].pwm_timer);
+ }
+}
+
+static int s5pc1xx_pwm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxPWMState *s = (S5pc1xxPWMState *)opaque;
+ S5pc1xxPWMTimerState *tmr = s->tmr;
+ uint64_t last;
+ int num;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_8s (f, &s->tag);
+ qemu_get_be32s(f, &s->tcfg0);
+ qemu_get_be32s(f, &s->tcfg1);
+ qemu_get_be32s(f, &s->tcon);
+ qemu_get_be32s(f, &s->tint_cstat);
+
+ for (num = 0; num < 5; num++) {
+ qemu_get_8s (f, &tmr[num].tag);
+ qemu_get_be32s(f, &tmr[num].freq_out);
+ qemu_get_be32s(f, &tmr[num].tcntb);
+ qemu_get_be64s(f, &tmr[num].tcntb_in_qemu);
+ qemu_get_be32s(f, &tmr[num].tcnt);
+ qemu_get_be64s(f, &tmr[num].tcnt_in_qemu);
+ qemu_get_be32s(f, &tmr[num].tcmpb);
+ qemu_get_be32s(f, &tmr[num].tcmp);
+ qemu_get_be64s(f, &last);
+
+ tmr[num].last_pwm_time = qemu_get_clock(vm_clock) - last;
+
+ qemu_get_timer(f, tmr[num].pwm_timer);
+ }
+
+ return 0;
+}
+
+/* PWM Init */
+static int s5pc1xx_pwm_init(SysBusDevice *dev)
+{
+ int i, iomemtype;
+ S5pc1xxPWMState *s = FROM_SYSBUS(S5pc1xxPWMState, dev);
+ S5pc1xxPWMTimerState *tmr = s->tmr;
+
+ s->tag = 255;
+
+ for (i = 0; i < 5; i++) {
+ tmr[i].tag = i;
+ tmr[i].regs = s;
+ sysbus_init_irq(dev, &tmr[i].irq_inst);
+ tmr[i].pwm_timer =
+ qemu_new_timer(vm_clock, s5pc1xx_pwm_timer_expiry, &tmr[i]);
+ }
+
+ s5pc1xx_pwm_write(s, S5C_TCFG0, 0x00000101);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_pwm_readfn, s5pc1xx_pwm_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_PWM_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_PWM, 0, s5pc1xx_pwm_gpio_readfn,
+ s5pc1xx_pwm_gpio_writefn, NULL, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.pwm", -1, 1,
+ s5pc1xx_pwm_save, s5pc1xx_pwm_load, s);
+ return 0;
+}
+
+static void s5pc1xx_pwm_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.pwm", sizeof(S5pc1xxPWMState),
+ s5pc1xx_pwm_init);
+}
+
+device_init(s5pc1xx_pwm_register_devices)
--- /dev/null
+/*
+ * Real Time Clock for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+
+/* Registers addresses */
+
+#define INT_PEND 0x30 /* R/W Interrupt Pending Register */
+#define RTC_CON 0x40 /* R/W RTC Control Register */
+#define TIC_CNT 0x44 /* R/W Tick Time Count Register */
+#define RTC_ALM 0x50 /* R/W RTC Alarm Control Register */
+#define ALM_SEC 0x54 /* R/W Alarm Second Data Register */
+#define ALM_MIN 0x58 /* R/W Alarm Minute Data Register */
+#define ALM_HOUR 0x5C /* R/W Alarm Hour Data Register */
+#define ALM_DAY 0x60 /* R/W Alarm Day of the month Data Register */
+#define ALM_MON 0x64 /* R/W Alarm Month Data Register */
+#define ALM_YEAR 0x68 /* R/W Alarm Year Data Register */
+#define BCD_SEC 0x70 /* R/W BCD Second Register */
+#define BCD_MIN 0x74 /* R/W BCD Minute Register */
+#define BCD_HOUR 0x78 /* R/W BCD Hour Register */
+#define BCD_DAY 0x7C /* R/W BCD value for a day of the month (1-31) */
+#define BCD_WEEKDAY 0x80 /* R/W BCD value for a day of the week (1-7) */
+#define BCD_MON 0x84 /* R/W BCD Month Register */
+#define BCD_YEAR 0x88 /* R/W BCD Year Register */
+#define CUR_TIC_CNT 0x90 /* R Current Tick Time Counter Register */
+
+/* Bit mapping for INT_PEND register */
+
+#define INT_ALM 0x02 /* Alarm interrupt pending bit */
+#define INT_TIC 0x01 /* Time TIC interrupt pending bit */
+
+/* Bit mapping for RTC_CON register */
+
+#define TIC_EN 0x100 /* Tick timer enable */
+#define TIC_CK_SEL_SHIFT (4)/* Tick timer sub clock selection */
+#define CLK_RST 0x08 /* RTC clock count reset */
+#define CNT_SEL 0x04 /* BCD count select */
+#define CLK_SEL 0x02 /* BCD clock select */
+#define RTC_EN 0x01 /* RTC control enable */
+
+/* Bit mapping for RTC_ALM register */
+
+#define ALM_EN 0x40 /* Alarm global enable */
+#define YEAR_EN 0x20 /* Year alarm enable */
+#define MON_EN 0x10 /* Month alarm enable */
+#define DAY_EN 0x08 /* Day of the month alarm enable */
+#define HOUR_EN 0x04 /* Hour alarm enable */
+#define MIN_EN 0x02 /* Minute alarm enable */
+#define SEC_EN 0x01 /* Second alarm enable */
+
+#define S5PC1XX_RTC_REG_MEM_SIZE 0x94
+
+
+typedef struct S5pc1xxRTCState {
+ SysBusDevice busdev;
+
+ uint32_t regs[BCD_YEAR + 1];
+
+ /* periodic timer */
+ QEMUTimer *periodic_timer;
+ uint32_t freq_out;
+ uint64_t last_tick;
+ uint64_t cur_tic_cnt;
+ qemu_irq tick_irq;
+
+ /* second update */
+ QEMUTimer *second_timer;
+ struct tm current_tm;
+ qemu_irq alm_irq;
+
+} S5pc1xxRTCState;
+
+
+static void s5pc1xx_rtc_periodic_tick(void *opaque);
+static void s5pc1xx_rtc_second_update(void *opaque);
+
+static void s5pc1xx_rtc_set_time(S5pc1xxRTCState *s)
+{
+ struct tm *tm = &(s->current_tm);
+
+ tm->tm_sec = from_bcd(s->regs[BCD_SEC] & 0x7F);
+ tm->tm_min = from_bcd(s->regs[BCD_MIN] & 0x7F);
+ tm->tm_hour = from_bcd(s->regs[BCD_HOUR] & 0x3F);
+ tm->tm_wday = from_bcd(s->regs[BCD_WEEKDAY] & 0x07);
+ tm->tm_mday = from_bcd(s->regs[BCD_DAY] & 0x3F);
+ /* month value in qemu is between 0 and 11 */
+ tm->tm_mon = from_bcd(s->regs[BCD_MON] & 0x1F) - 1;
+ /* year value is with base_year = 2000 (not 1900 as in qemu) */
+ tm->tm_year = 100 + from_bcd(s->regs[BCD_YEAR] & 0xFF) +
+ from_bcd((s->regs[BCD_YEAR] >> 8) & 0xF) * 100;
+}
+
+static void s5pc1xx_rtc_read_time(S5pc1xxRTCState *s)
+{
+ const struct tm *tm = &(s->current_tm);
+
+ s->regs[BCD_SEC] = to_bcd(tm->tm_sec);
+ s->regs[BCD_MIN] = to_bcd(tm->tm_min);
+ s->regs[BCD_HOUR] = to_bcd(tm->tm_hour);
+ s->regs[BCD_WEEKDAY] = to_bcd(tm->tm_wday);
+ s->regs[BCD_DAY] = to_bcd(tm->tm_mday);
+ /* month value in qemu is between 0 and 11 */
+ s->regs[BCD_MON] = to_bcd(tm->tm_mon + 1);
+ /* year value is with base_year = 2000 (not 1900 as in qemu) */
+ s->regs[BCD_YEAR] = to_bcd((tm->tm_year - 100) % 100) |
+ (to_bcd((tm->tm_year - 100) % 1000 / 100) << 8);
+}
+
+/* set default values for all fields */
+static void s5pc1xx_rtc_reset(S5pc1xxRTCState *s)
+{
+ short i;
+
+ /* stop tick timer */
+ s->regs[RTC_CON] &= ~TIC_EN;
+ s5pc1xx_rtc_periodic_tick(s);
+
+ /* stop second timer */
+ s->regs[RTC_CON] &= ~RTC_EN;
+ s5pc1xx_rtc_second_update(s);
+
+ for (i = 0x30; i < 0x90; i += 0x4) {
+ s->regs[i] = 0;
+ }
+
+ s->last_tick = 0;
+ s->freq_out = 0;
+}
+
+/* Setup next timer tick */
+static void s5pc1xx_rtc_periodic_update(S5pc1xxRTCState *s)
+{
+ uint64_t next_periodic_time, last = qemu_get_clock(vm_clock);
+ s->last_tick = last;
+ next_periodic_time =
+ last + muldiv64(s->regs[TIC_CNT], get_ticks_per_sec(), s->freq_out);
+ qemu_mod_timer(s->periodic_timer, next_periodic_time);
+}
+
+/* Send periodic interrupt */
+static void s5pc1xx_rtc_periodic_tick(void *opaque)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+ /* tick actually happens not every CUR_TIC_CNT but rather at irq time;
+ * if current ticnto value is needed it is calculated in s5pc1xx_rtc_read */
+ if (s->regs[RTC_CON] & TIC_EN) {
+ qemu_irq_raise(s->tick_irq);
+ s->regs[INT_PEND] |= INT_TIC;
+ s5pc1xx_rtc_periodic_update(s);
+ } else {
+ s->last_tick = 0;
+ qemu_del_timer(s->periodic_timer);
+ }
+}
+
+/* Update tick timer frequency */
+static void s5pc1xx_rtc_periodic_rate(S5pc1xxRTCState *s)
+{
+ short freq_code;
+
+ /* get four binary digits to determine the frequency */
+ freq_code = (s->regs[RTC_CON] >> TIC_CK_SEL_SHIFT) & 0xF;
+
+ /* tick timer frequency */
+ s->freq_out = 32768 / (1 << freq_code);
+}
+
+/* Month is between 0 and 11 */
+static int get_days_in_month(int month, int year)
+{
+ short d;
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ if ((unsigned) month >= 12) {
+ return 31;
+ }
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0) {
+ d++;
+ }
+ }
+ return d;
+}
+
+/* Update 'tm' to the next second */
+static void s5pc1xx_rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned) tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned) tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned) tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ /* TODO: check if wday value in qemu is between 1 and 7 */
+ if ((unsigned) tm->tm_wday >= 8) {
+ tm->tm_wday = 1;
+ }
+ days_in_month = get_days_in_month(tm->tm_mon, tm->tm_year);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Working with up-to-date time and check alarm */
+static void s5pc1xx_rtc_second_update(void *opaque)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+ uint64_t next_second_time;
+
+ if (s->regs[RTC_CON] & RTC_EN) {
+ /* check if the alarm is generally enabled */
+ /* then check if an alarm of at least one kind is enabled */
+ if (s->regs[RTC_ALM] & ALM_EN &&
+ (s->regs[RTC_ALM] & SEC_EN ||
+ s->regs[RTC_ALM] & MIN_EN ||
+ s->regs[RTC_ALM] & HOUR_EN ||
+ s->regs[RTC_ALM] & DAY_EN ||
+ s->regs[RTC_ALM] & MON_EN ||
+ s->regs[RTC_ALM] & YEAR_EN)) {
+
+ /* check alarm values together with corresponding permissive bits */
+ if (((s->regs[ALM_SEC] & 0x7F) ==
+ to_bcd(s->current_tm.tm_sec) ||
+ !(s->regs[RTC_ALM] & SEC_EN)) &&
+
+ ((s->regs[ALM_MIN] & 0x7F) ==
+ to_bcd(s->current_tm.tm_min) ||
+ !(s->regs[RTC_ALM] & MIN_EN)) &&
+
+ ((s->regs[ALM_HOUR] & 0x3F) ==
+ to_bcd(s->current_tm.tm_hour) ||
+ !(s->regs[RTC_ALM] & HOUR_EN)) &&
+
+ ((s->regs[ALM_DAY] & 0x3F) ==
+ to_bcd(s->current_tm.tm_mday) ||
+ !(s->regs[RTC_ALM] & DAY_EN)) &&
+
+ ((s->regs[ALM_MON] & 0x1F) ==
+ to_bcd(s->current_tm.tm_mon) ||
+ !(s->regs[RTC_ALM] & MON_EN)) &&
+
+ (((s->regs[ALM_YEAR] & 0xFF) ==
+ to_bcd((s->current_tm.tm_year - 100) % 100) &&
+ ((s->regs[ALM_YEAR] >> 8) & 0xF) ==
+ to_bcd((s->current_tm.tm_year - 100) % 1000 / 100)) ||
+ !(s->regs[RTC_ALM] & YEAR_EN))) {
+
+ qemu_irq_raise(s->alm_irq);
+ s->regs[INT_PEND] |= INT_ALM;
+ }
+ }
+
+ s5pc1xx_rtc_next_second(&s->current_tm);
+ next_second_time = qemu_get_clock(vm_clock) + get_ticks_per_sec();
+ qemu_mod_timer(s->second_timer, next_second_time);
+ } else {
+ qemu_del_timer(s->second_timer);
+ }
+}
+
+static uint32_t s5pc1xx_rtc_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+ switch (offset) {
+ case BCD_SEC:
+ case BCD_MIN:
+ case BCD_HOUR:
+ case BCD_WEEKDAY:
+ case BCD_DAY:
+ case BCD_MON:
+ case BCD_YEAR:
+ s5pc1xx_rtc_read_time(s);
+ /* fallthrough */
+ case INT_PEND:
+ case RTC_CON:
+ case TIC_CNT:
+ case RTC_ALM:
+ case ALM_SEC:
+ case ALM_MIN:
+ case ALM_HOUR:
+ case ALM_DAY:
+ case ALM_MON:
+ case ALM_YEAR:
+ return s->regs[offset];
+ case CUR_TIC_CNT:
+ if (s->freq_out && s->last_tick && s->regs[TIC_CNT]) {
+ s->cur_tic_cnt = s->regs[TIC_CNT] -
+ muldiv64(qemu_get_clock(vm_clock) - s->last_tick,
+ s->freq_out, get_ticks_per_sec()) %
+ s->regs[TIC_CNT];
+ } else {
+ s->cur_tic_cnt = 0;
+ }
+
+ return s->cur_tic_cnt;
+ default:
+ hw_error("s5pc1xx.rtc: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_rtc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+
+ switch (offset) {
+ case INT_PEND:
+ /* lower interrupts if any */
+ if (value & INT_TIC) {
+ qemu_irq_lower(s->tick_irq);
+ }
+ if (value & INT_ALM) {
+ qemu_irq_lower(s->alm_irq);
+ }
+
+ /* clear INT_* bits if they are set in value */
+ s->regs[INT_PEND] &= ~(value & (INT_TIC | INT_ALM));
+ break;
+ case RTC_CON:
+ /* reset tick counter */
+ if (value & CLK_RST) {
+ s5pc1xx_rtc_reset(s);
+ value &= ~CLK_RST;
+ }
+ /* start second timer */
+ if ((value & RTC_EN) > (s->regs[RTC_CON] & RTC_EN)) {
+ s->regs[RTC_CON] |= RTC_EN;
+ qemu_get_timedate(&s->current_tm, 0);
+ s5pc1xx_rtc_second_update(s);
+ }
+ /* start tick timer */
+ if ((value & TIC_EN) > (s->regs[RTC_CON] & TIC_EN)) {
+ s->regs[RTC_CON] = value;
+ s5pc1xx_rtc_periodic_rate(s);
+ s5pc1xx_rtc_periodic_update(s);
+ break;
+ } else if ((value & TIC_EN) < (s->regs[RTC_CON] & TIC_EN)) {
+ qemu_del_timer(s->periodic_timer);
+ }
+ s->regs[RTC_CON] = value;
+ s5pc1xx_rtc_periodic_rate(s);
+ break;
+ case TIC_CNT:
+ case RTC_ALM:
+ case ALM_SEC:
+ case ALM_MIN:
+ case ALM_HOUR:
+ case ALM_DAY:
+ case ALM_MON:
+ case ALM_YEAR:
+ s->regs[offset] = value;
+ break;
+ case BCD_SEC:
+ case BCD_MIN:
+ case BCD_HOUR:
+ case BCD_WEEKDAY:
+ case BCD_DAY:
+ case BCD_MON:
+ case BCD_YEAR:
+ s->regs[offset] = value;
+ /* if in disabled mode, do not update the time */
+ if (s->regs[RTC_CON] & RTC_EN) {
+ s5pc1xx_rtc_set_time(s);
+ }
+ break;
+ default:
+ hw_error("s5pc1xx.rtc: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* Memory mapped interface */
+static CPUReadMemoryFunc * const s5pc1xx_rtc_mm_read[] = {
+ s5pc1xx_rtc_read,
+ s5pc1xx_rtc_read,
+ s5pc1xx_rtc_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_rtc_mm_write[] = {
+ s5pc1xx_rtc_write,
+ s5pc1xx_rtc_write,
+ s5pc1xx_rtc_write
+};
+
+static void s5pc1xx_rtc_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+ int i;
+
+ s5pc1xx_rtc_read_time(s);
+ for (i = INT_PEND; i <= BCD_YEAR; i++) {
+ qemu_put_be32s(f, &s->regs[i]);
+ }
+ qemu_put_timer(f, s->second_timer);
+
+ qemu_put_be64s(f, &s->last_tick);
+ qemu_put_be32s(f, &s->freq_out);
+ qemu_put_timer(f, s->periodic_timer);
+}
+
+static int s5pc1xx_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ for (i = INT_PEND; i <= BCD_YEAR; i++) {
+ qemu_get_be32s(f, &s->regs[i]);
+ }
+
+ //s5pc1xx_rtc_set_time(s);
+ qemu_get_timedate(&s->current_tm, 0);
+ s5pc1xx_rtc_read_time(s);
+ qemu_get_timer(f, s->second_timer);
+
+ qemu_get_be64s(f, &s->last_tick);
+ qemu_get_be32s(f, &s->freq_out);
+ qemu_get_timer(f, s->periodic_timer);
+
+ return 0;
+}
+
+/* initialize and start timers */
+static int s5pc1xx_rtc_init(SysBusDevice *dev)
+{
+ int iomemory;
+ S5pc1xxRTCState *s = FROM_SYSBUS(S5pc1xxRTCState, dev);
+
+ sysbus_init_irq(dev, &s->alm_irq);
+ sysbus_init_irq(dev, &s->tick_irq);
+
+ s->periodic_timer =
+ qemu_new_timer(vm_clock, s5pc1xx_rtc_periodic_tick, s);
+ s->second_timer =
+ qemu_new_timer(vm_clock, s5pc1xx_rtc_second_update, s);
+
+ iomemory =
+ cpu_register_io_memory(s5pc1xx_rtc_mm_read, s5pc1xx_rtc_mm_write, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_RTC_REG_MEM_SIZE, iomemory);
+
+ s5pc1xx_rtc_reset(s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.rtc", -1, 1,
+ s5pc1xx_rtc_save, s5pc1xx_rtc_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_rtc_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.rtc", sizeof(S5pc1xxRTCState),
+ s5pc1xx_rtc_init);
+}
+
+device_init(s5pc1xx_rtc_register_devices)
--- /dev/null
+/*
+ * SPDIF transmitter for Samsung S5PC110-based board emulation
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "s5pc1xx_gpio_regs.h"
+#include "sysbus.h"
+
+#define ALL_BITS(b,a) (((1 << (b - a + 1)) - 1) << a)
+
+/* R/W Specifies the Clock control register 0x0000_0002 */
+#define SPDCLKCON 0x00
+
+ #define MAIN_CLK_SEL (1 << 2)
+ #define CLK_DWN_READY (1 << 1)
+ #define POWER_ON (1 << 0)
+
+/* R/W Specifies the Control register 0x0000_0000 */
+#define SPDCON 0x04
+
+ #define FIFO_LEVEL_SHIFT 22 /* 5 bits */
+ #define FIFO_LEVEL_THR_SHIFT 19 /* 3 bits */
+ #define FIFO_TRANSFER_MODE_SHIFT 17 /* 2 bits */
+ #define FIFO_LEVEL_INT_ST (1 << 16)
+ #define FIFO_LEVEL_INT_EN (1 << 15)
+ #define ENDIAN_FORMAT_SHIFT 13 /* 2 bits */
+ #define USER_DATA_ATTACH (1 << 12)
+ #define USER_DATA_INT_ST (1 << 11)
+ #define USER_DATA_INT_EN (1 << 10)
+ #define BUF_EMPTY_INT_ST (1 << 9)
+ #define BUF_EMPTY_INT_EN (1 << 8)
+ #define STREAM_END_INT_ST (1 << 7)
+ #define STREAM_END_INT_EN (1 << 6)
+ #define SOFTWARE_RESET (1 << 5)
+ #define MAIN_CLK_FREQ_SHIFT 3 /* 2 bits */
+ #define PCM_DATA_SIZE_SHIFT 1 /* 2 bits */
+ /* TODO: Stream data coding is not described in the documentation.
+ * So, this is not implemented so far. */
+ #define PCM_OR_STREAM (1 << 0)
+
+/* R/W Specifies the Burst status register 0x0000_0000 */
+#define SPDBSTAS 0x08
+
+ #define BURST_DATA_L_BIT_SHIFT 16 /* 16 bits */
+ #define BITSTREAM_NUMBER_SHIFT 13 /* 3 bits */
+ #define DATA_TYPE_DEP_INFO_SHIFT 8 /* 5 bits */
+ #define ERROR_FLAG (1 << 7)
+ #define COMPR_DATA_TYPE_SHIFT 0 /* 5 bits */
+
+/* R/W Specifies the Channel status register 0x0000_0000 */
+#define SPDCSTAS 0x0C
+
+ #define CLK_ACCURACY_SHIFT 28 /* 2 bits */
+ #define SAMP_FREQ_SHIFT 24 /* 4 bits */
+ #define CH_NUM_SHIFT 20 /* 4 bits */
+ #define SRC_NUM_SHIFT 16 /* 4 bits */
+ #define CAT_CODE_SHIFT 8 /* 8 bits */
+ #define CH_STAT_MODE_SHIFT 6 /* 2 bits */
+ #define EMPHASIS_SHIFT 3 /* 3 bits */
+ #define COPYRIGHT_ASSERTION (1 << 2)
+ #define AUDIO_WORD (1 << 1)
+ #define CH_STAT_BLOCK (1 << 0)
+
+/* W Specifies the SPDIFOUT data buffer 0x0000_0000 */
+#define SPDDAT 0x10
+
+/* R/W Specifies the Repetition count register 0x0000_0000 */
+#define SPDCNT 0x14
+
+/* R Specifies the Shadowed Burst Status Register 0x0000_0000 */
+#define SPDBSTAS_SHD 0x18
+
+/* R Specifies the Shadowed Repetition Count Register 0x0000_0000 */
+#define SPDCNT_SHD 0x1C
+
+/* R/W Specifies the Subcode Q1 ~ Q32 0x0000_0000 */
+#define USERBIT1 0x20
+
+/* R/W Specifies the Subcode Q33 ~ Q64 0x0000_0000 */
+#define USERBIT2 0x24
+
+/* R/W Specifies the Subcode Q65 ~ Q96 0x0000_0000 */
+#define USERBIT3 0x28
+
+/* R Specifies the Shadowed Register Userbit1 0x0000_0000 */
+#define USERBIT1_SHD 0x2C
+
+/* R Specifies the Shadowed Register Userbit2 0x0000_0000 */
+#define USERBIT2_SHD 0x30
+
+/* R Specifies the Shadowed Register Userbit3 0x0000_0000 */
+#define USERBIT3_SHD 0x34
+
+/* R Specifies the RTL Version Information 0x0000_000C */
+#define VERSION_INFO 0x38
+
+#define S5PC1XX_SPDIF_REG_MEM_SIZE 0x3C
+
+#define M_PREAMBLE 1
+#define B_PREAMBLE 2
+#define W_PREAMBLE 3
+
+#define FIFO_DEPTH (s->write_idx - s->read_idx)
+#define INT_EN(stat) (stat >> 1)
+#define ALL_STAT (STREAM_END_INT_ST | BUF_EMPTY_INT_ST | \
+ USER_DATA_INT_ST | FIFO_LEVEL_INT_ST)
+
+
+typedef struct S5pc1xxSpdifState {
+ SysBusDevice busdev;
+ qemu_irq irq;
+
+ uint32_t spdclkcon;
+ uint32_t spdcon;
+ uint32_t spdbstas;
+ uint32_t spdcstas;
+ uint32_t spdcnt;
+ uint32_t userbit[3];
+ uint32_t version_info;
+
+ struct shadowed_regs {
+ uint32_t spdbstas;
+ uint32_t spdcnt;
+ uint32_t userbit[3];
+ } shd;
+
+ QEMUTimer *spdif_timer;
+ uint32_t sclk_freq;
+ int64_t count; /* has signed type */
+ uint8_t updated;
+
+ uint32_t fifo[2][16];
+ uint64_t read_idx, write_idx;
+ uint8_t read_ch, write_ch;
+
+ uint32_t sub_frame;
+ int8_t fifo_thr, endian_f; /* have signed type */
+ int16_t Pa, Pb, Pc, Pd; /* have signed type */
+ uint8_t lsb_pos, non_linear_pcm, stat_bit[16];
+ uint8_t first_state, second_state;
+ uint16_t rep_period, data_sframe_num;
+} S5pc1xxSpdifState;
+
+
+/* Reset SPDIF */
+static void s5pc1xx_spdif_reset(void *opaque)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+ uint8_t i;
+
+ s->spdclkcon = 0x2;
+ s->spdcon = 0x1;
+ s->spdbstas = 0;
+ s->spdcstas = 0;
+ s->spdcnt = 0;
+ s->shd.spdbstas = 0;
+ s->shd.spdcnt = 0;
+ s->version_info = 0xC;
+
+ for (i = 0; i < 3; i++) {
+ s->userbit[i] = 0;
+ s->shd.userbit[i] = 0;
+ }
+
+ s->sclk_freq = 0;
+ s->count = -1;
+
+ s->read_idx = 0;
+ s->write_idx = 0;
+ s->read_ch = 0;
+ s->write_ch = 0;
+
+ s->fifo_thr = -1;
+ s->endian_f = -1;
+ s->lsb_pos = 0;
+ s->first_state = 0;
+ s->second_state = 0;
+
+ s->Pa = 0xF872;
+ s->Pb = 0x4E1F;
+ s->Pc = -1;
+ s->Pd = -1;
+ s->data_sframe_num = 0; /* used for non-linear PCM */
+}
+
+/* Interrupts handler */
+static void s5pc1xx_spdif_irq(S5pc1xxSpdifState *s,
+ uint32_t stat, uint8_t to_clear)
+{
+ if (to_clear) {
+ s->spdcon &= ~(stat);
+ if (!(s->spdcon & ALL_STAT)) /* if all clear */
+ qemu_irq_lower(s->irq);
+ } else {
+ s->spdcon |= stat;
+ if (s->spdcon & INT_EN(stat)) /* if enabled */
+ qemu_irq_raise(s->irq);
+ }
+}
+
+/* -=FIFO HANDLING=- */
+
+/* Determine threshold FIFO depth */
+static void s5pc1xx_spdif_fifo_thr(S5pc1xxSpdifState *s)
+{
+ uint8_t thr[8] = {0, 1, 4, 6, 10, 12, 14, 15};
+ uint8_t thr_idx = (s->spdcon >> FIFO_LEVEL_THR_SHIFT) & 0x7;
+
+ s->fifo_thr = thr[thr_idx];
+}
+
+/* Control FIFO level */
+static void s5pc1xx_spdif_fifo_control(S5pc1xxSpdifState *s)
+{
+ if (s->fifo_thr < 0)
+ s5pc1xx_spdif_fifo_thr(s);
+
+ if (FIFO_DEPTH > s->fifo_thr)
+ s5pc1xx_spdif_irq(s, FIFO_LEVEL_INT_ST, 0);
+
+ if (FIFO_DEPTH == 0)
+ s5pc1xx_spdif_irq(s, BUF_EMPTY_INT_ST, 0);
+}
+
+/* Determine endian format index */
+static void s5pc1xx_spdif_endian_idx(S5pc1xxSpdifState *s)
+{
+ s->endian_f = (s->spdcon >> ENDIAN_FORMAT_SHIFT) & 0x3;
+}
+
+/* Convert value according to current endian format */
+static uint32_t s5pc1xx_spdif_endian_format(S5pc1xxSpdifState *s,
+ uint32_t value)
+{
+ uint32_t ret_val = 0;
+
+ if (s->endian_f < 0)
+ s5pc1xx_spdif_endian_idx(s);
+
+ switch(s->endian_f) {
+ case 0:
+ ret_val = value & ALL_BITS(23, 0);
+ break;
+ case 1:
+ ret_val = (((value & ALL_BITS(15, 8)) << 8) |
+ ((value & ALL_BITS(23, 16)) >> 8) |
+ ((value & ALL_BITS(31, 24)) >> 24));
+ break;
+ case 2:
+ ret_val = (((value & ALL_BITS( 7, 0)) << 16) |
+ ((value & ALL_BITS(15, 8)) >> 0) |
+ ((value & ALL_BITS(23, 16)) >> 16));
+ break;
+ case 3:
+ ret_val = (((value & ALL_BITS( 7, 0)) << 8) |
+ ((value & ALL_BITS(15, 8)) >> 8));
+ break;
+ }
+ return ret_val;
+}
+
+/* Get value from FIFO */
+static uint32_t s5pc1xx_spdif_fifo_read(S5pc1xxSpdifState *s)
+{
+ uint32_t ret_val = 0;
+
+ if (FIFO_DEPTH > 0) {
+ ret_val = s->fifo[s->read_ch][s->read_idx % 16];
+
+ if (s->read_ch == 1)
+ s->read_idx++;
+ }
+ s->read_ch = (s->read_ch + 1) % 2;
+ s5pc1xx_spdif_fifo_control(s);
+
+ return ret_val;
+}
+
+/* Put value into FIFO */
+static void s5pc1xx_spdif_fifo_write(S5pc1xxSpdifState *s, uint32_t value)
+{
+ value = s5pc1xx_spdif_endian_format(s, value);
+
+ if (FIFO_DEPTH < 16) {
+ s->fifo[s->write_ch][s->write_idx % 16] = value;
+
+ if (s->write_ch == 1)
+ s->write_idx++;
+ }
+ s->write_ch = (s->write_ch + 1) % 2;
+ s5pc1xx_spdif_fifo_control(s);
+}
+
+/* -=SPDIF MAIN LOGIC=- */
+
+/* Update burst params (for non-linear PCM) */
+static void s5pc1xx_spdif_stream_end(S5pc1xxSpdifState *s)
+{
+ s->shd.spdbstas = s->spdbstas;
+ s->shd.spdcnt = s->spdcnt;
+
+ s->Pc = (s->shd.spdbstas >> 0) & ALL_BITS(15, 0);
+ s->Pd = (s->shd.spdbstas >> 16) & ALL_BITS(15, 0);
+ s->rep_period = (s->shd.spdcnt) & ALL_BITS(12, 0);
+
+ s5pc1xx_spdif_irq(s, STREAM_END_INT_ST, 0);
+}
+
+/* Spdif_tx block */
+static uint32_t s5pc1xx_spdif_tx_block(S5pc1xxSpdifState *s)
+{
+ uint32_t value;
+ uint16_t next_payload_size;
+
+ /* check for beginning of new payload frame */
+ if ((s->data_sframe_num > 3) && !(s->data_sframe_num % 2)) {
+ /* check if bit stream size will be past s->rep_period
+ * value after next write routine and avoid this */
+ if ((s->data_sframe_num + 2) * 16 > s->rep_period) {
+ s5pc1xx_spdif_stream_end(s);
+ s->data_sframe_num = 0;
+ }
+ }
+
+ next_payload_size =
+ (s->data_sframe_num > 3) ? (s->data_sframe_num - 4 + 2) * 16 : 0;
+
+ switch(s->data_sframe_num) {
+ case 0:
+ value = s->Pa;
+ break;
+ case 1:
+ value = s->Pb;
+ break;
+ case 2:
+ value = s->Pc;
+ break;
+ case 3:
+ value = s->Pd;
+ break;
+ default:
+ if (next_payload_size > s->Pd) {
+ value = 0;
+ } else {
+ value = s5pc1xx_spdif_fifo_read(s);
+ }
+ }
+ s->data_sframe_num++;
+
+ return value;
+}
+
+/* Determine LSB position within sub-frame */
+static void s5pc1xx_spdif_lsb(S5pc1xxSpdifState *s)
+{
+ uint8_t data_size;
+
+ if (s->non_linear_pcm) {
+ s->lsb_pos = 12;
+ return;
+ }
+ data_size = (((s->spdcon >> PCM_DATA_SIZE_SHIFT) & 0x3) + 4) << 2;
+ s->lsb_pos = 28 - data_size;
+}
+
+/* Convert audio data to SPDIF format
+ * (compose 0~31 time slots into s->sub_frame) */
+static void s5pc1xx_spdif_sub_frame(S5pc1xxSpdifState *s)
+{
+ uint32_t value;
+ uint8_t ballast, preamble, v_flag, user_bit, parity_bit;
+ uint8_t i, ones;
+ uint16_t carrier_sframe_num = (s->count / 64) % 384;
+
+ ballast = (s->second_state) ? 0 : 0x3; /* 2b'00 or 2b'11 */
+
+ /* Determine preamble */
+ if (carrier_sframe_num % 2) {
+ /* channel 2 has odd sub-frames numbers and always has W-preamble */
+ preamble = W_PREAMBLE;
+ } else {
+ /* channel 1 has even sub-frames numbers and has M-preamble except
+ * the first sub-frame */
+ if (carrier_sframe_num) {
+ preamble = M_PREAMBLE;
+ } else {
+ preamble = B_PREAMBLE;
+ }
+ }
+
+ /* Determine audio sample word */
+ if (s->non_linear_pcm) {
+ value = s5pc1xx_spdif_tx_block(s);
+ } else {
+ value = s5pc1xx_spdif_fifo_read(s);
+ }
+
+ /* Validity flag, user bit and channel status */
+ v_flag = (value) ? 0 : 1;
+ /* TODO: User bit is always 0 for linear-PCM, but PCM user data is set
+ * for non-linear PCM in the registers USERBIT1~3(_SHD). The allocation
+ * of user data between two channels is unclear in the documentation.
+ * So, user data transmission is not inplemented so far. */
+ user_bit = 0;
+
+ /* Compose sub_frame without parity_bit */
+ if (!(s->lsb_pos))
+ s5pc1xx_spdif_lsb(s);
+
+ s->sub_frame = ballast | (preamble << 2) |
+ (value << s->lsb_pos & ALL_BITS(27, s->lsb_pos)) |
+ (v_flag << 28) | (user_bit << 29) |
+ (s->stat_bit[carrier_sframe_num % 2] << 30);
+
+ /* Determine parity_bit */
+ ones = parity_bit = 0;
+ for (i = 4; i < 31; i++) {
+ if (s->sub_frame >> i & 0x1)
+ ones++;
+ }
+ if (ones % 2)
+ parity_bit = 1;
+
+ s->sub_frame |= (parity_bit << 31);
+}
+
+/* Channel coding of source signal */
+static void s5pc1xx_spdif_channel_coding(S5pc1xxSpdifState *s)
+{
+ uint8_t cur_pos = (s->count % 64) / 2;
+ uint8_t source_coding = s->sub_frame >> cur_pos & 0x1;
+
+ /* Ballast handling (ballast ensures one deference
+ * from bi-phase scheme in channel coded preamble) */
+ if (cur_pos < 2) {
+ s->first_state = s->second_state = source_coding;
+ return;
+ }
+
+ /* Preamble and data handling */
+ if (cur_pos < 3) {
+ /* the line below ensures one more deference
+ * from bi-phase scheme in channel coded preamble */
+ s->first_state = s->second_state;
+ } else {
+ s->first_state = !(s->second_state);
+ }
+
+ if (source_coding) {
+ s->second_state = !(s->first_state);
+ } else {
+ s->second_state = s->first_state;
+ }
+}
+
+/* -=TIMER HANDLING=- */
+
+/* Update timer frequency */
+static void s5pc1xx_spdif_sclk_update(S5pc1xxSpdifState *s)
+{
+ uint8_t freq_id = s->spdcstas >> SAMP_FREQ_SHIFT & 0xF;
+
+ switch (freq_id) {
+ case 0:
+ /* samplling_freq x 32 time slots x 2 channels x bi-phase mark */
+ s->sclk_freq = 44100 * 32 * 2 * 2;
+ break;
+ case 2:
+ s->sclk_freq = 48000 * 32 * 2 * 2;
+ break;
+ case 3:
+ s->sclk_freq = 32000 * 32 * 2 * 2;
+ break;
+ case 10:
+ s->sclk_freq = 96000 * 32 * 2 * 2;
+ break;
+ default:
+ hw_error("s5pc1xx.spdif: frequency id %u is not supported\n", freq_id);
+ }
+}
+
+/* Sync timer engine */
+static void s5pc1xx_spdif_sync(void *opaque)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+ uint64_t next_spdif_time;
+
+ if (s->spdclkcon & POWER_ON) {
+ if (!(s->sclk_freq))
+ s5pc1xx_spdif_sclk_update(s);
+
+ /* s->updated is used to avoid conflict between two functions
+ * in case of synchronous runs */
+ s->updated = 0;
+
+ s->count++;
+
+ if (!(s->count % 64))
+ s5pc1xx_spdif_sub_frame(s);
+
+ if (!(s->count % 2))
+ s5pc1xx_spdif_channel_coding(s);
+
+ s->updated = 1;
+
+ next_spdif_time = qemu_get_clock(vm_clock) +
+ muldiv64(1, get_ticks_per_sec(), s->sclk_freq);
+ qemu_mod_timer(s->spdif_timer, next_spdif_time);
+ } else {
+ s->spdclkcon |= CLK_DWN_READY;
+ qemu_del_timer(s->spdif_timer);
+ }
+}
+
+/* -=RELATION WITH GPIO AND OS=- */
+
+/* Read SPDIF by GPIO */
+static uint32_t s5pc1xx_spdif_gpio_read(void *opaque, int io_index)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+ if (io_index == SPDIF_0_OUT) {
+ if (s->count % 2) {
+ return s->second_state;
+ } else {
+ if (s->updated) {
+ return s->first_state;
+ } else {
+ return s->second_state;
+ }
+ }
+ }
+ return 0;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_spdif_gpio_readfn = s5pc1xx_spdif_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_spdif_gpio_writefn = s5pc1xx_empty_gpio_write;
+
+/* Read SPDIF by OS */
+static uint32_t s5pc1xx_spdif_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+ switch(offset) {
+ case SPDCLKCON:
+ return s->spdclkcon;
+ case SPDCON:
+ s->spdcon = ((FIFO_DEPTH << FIFO_LEVEL_SHIFT) & ALL_BITS(26, 22)) |
+ (s->spdcon & ALL_BITS(21 ,0));
+ return s->spdcon;
+ case SPDBSTAS:
+ return s->spdbstas;
+ case SPDCSTAS:
+ return s->spdcstas;
+ case SPDCNT:
+ return s->spdcnt;
+ case SPDBSTAS_SHD:
+ return s->shd.spdbstas;
+ case SPDCNT_SHD:
+ return s->shd.spdcnt;
+ case USERBIT1 ... USERBIT3:
+ return s->userbit[(offset - USERBIT1) / (USERBIT2 - USERBIT1)];
+ case USERBIT1_SHD ... USERBIT3_SHD:
+ return s->shd.userbit[(offset - USERBIT1_SHD) /
+ (USERBIT2_SHD - USERBIT1_SHD)];
+ case VERSION_INFO:
+ return s->version_info;
+ default:
+ hw_error("s5pc1xx.spdif: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+/* Write SPDIF by OS */
+static void s5pc1xx_spdif_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t old_val;
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+
+ switch(offset) {
+ case SPDCLKCON:
+ old_val = s->spdclkcon;
+
+ /* Note: the CLK_DWN_READY field is read-only */
+ s->spdclkcon =
+ (value & (POWER_ON | MAIN_CLK_SEL)) | (old_val & CLK_DWN_READY);
+
+ if ((value & POWER_ON) > (old_val & POWER_ON)) {
+ s5pc1xx_spdif_stream_end(s);
+ s5pc1xx_spdif_sync(s);
+ s->spdclkcon &= ~CLK_DWN_READY;
+ }
+ break;
+ case SPDCON:
+ old_val = s->spdcon;
+
+ /* Note: the 'FIFO level' field is read-only */
+ s->spdcon = (old_val & ALL_BITS(26, 22)) | (value & ALL_BITS(21, 0));
+
+ /* Check 'FIFO level threshold' field for update */
+ if ((value & ALL_BITS(21, 19)) != (old_val & ALL_BITS(21, 19)))
+ s5pc1xx_spdif_fifo_thr(s);
+
+ /* Check 'Endian format' field for update */
+ if ((value & ALL_BITS(14, 13)) != (old_val & ALL_BITS(14, 13)))
+ s5pc1xx_spdif_endian_idx(s);
+
+ /* Check 'PCM data size' field for update */
+ if ((value & ALL_BITS(2, 1)) != (old_val & ALL_BITS(2, 1)))
+ s5pc1xx_spdif_lsb(s);
+
+ /* Clear irq states if any */
+ if (value & ALL_STAT)
+ s5pc1xx_spdif_irq(s, (value & ALL_STAT), 1);
+
+ break;
+ case SPDBSTAS:
+ s->spdbstas = value;
+ break;
+ case SPDCSTAS:
+ old_val = s->spdcstas;
+ s->spdcstas = value;
+
+ /* Check 'Sampling frequency' field for update */
+ if ((value & ALL_BITS(27, 24)) != (old_val & ALL_BITS(27, 24)))
+ s5pc1xx_spdif_sclk_update(s);
+
+ s->stat_bit[(value >> CH_NUM_SHIFT) & ALL_BITS(3, 0)] = value & 0x1;
+
+ s->non_linear_pcm = (value & AUDIO_WORD) ? 1 : 0;
+
+ break;
+ case SPDCNT:
+ s->spdcnt = value;
+ break;
+ case SPDDAT:
+ s5pc1xx_spdif_fifo_write(s, value);
+ break;
+ case USERBIT1 ... USERBIT3:
+ s->userbit[(offset - USERBIT1) / (USERBIT2 - USERBIT1)] = value;
+ break;
+ default:
+ hw_error("s5pc1xx.spdif: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_spdif_readfn[] = {
+ s5pc1xx_spdif_read,
+ s5pc1xx_spdif_read,
+ s5pc1xx_spdif_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_spdif_writefn[] = {
+ s5pc1xx_spdif_write,
+ s5pc1xx_spdif_write,
+ s5pc1xx_spdif_write
+};
+
+static void s5pc1xx_spdif_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->spdclkcon);
+ qemu_put_be32s(f, &s->spdcon);
+ qemu_put_be32s(f, &s->spdbstas);
+ qemu_put_be32s(f, &s->spdcstas);
+ qemu_put_be32s(f, &s->spdcnt);
+ qemu_put_be32s(f, &s->version_info);
+
+ for (i = 0; i < 3; i++) {
+ qemu_put_be32s(f, &s->userbit[i]);
+ qemu_put_be32s(f, &s->shd.userbit[i]);
+ }
+
+ qemu_put_be32s(f, &s->shd.spdbstas);
+ qemu_put_be32s(f, &s->shd.spdcnt);
+
+ qemu_put_be32s (f, &s->sclk_freq);
+ qemu_put_sbe64s(f, &s->count);
+ qemu_put_8s (f, &s->updated);
+ qemu_put_be64s (f, &s->read_idx);
+ qemu_put_be64s (f, &s->write_idx);
+ qemu_put_8s (f, &s->read_ch);
+ qemu_put_8s (f, &s->write_ch);
+ qemu_put_be32s (f, &s->sub_frame);
+ qemu_put_s8s (f, &s->fifo_thr);
+ qemu_put_s8s (f, &s->endian_f);
+ qemu_put_sbe16s(f, &s->Pa);
+ qemu_put_sbe16s(f, &s->Pb);
+ qemu_put_sbe16s(f, &s->Pc);
+ qemu_put_sbe16s(f, &s->Pd);
+
+ for (i = 0; i < 16; i++) {
+ qemu_put_be32s(f, &s->fifo[0][i]);
+ qemu_put_be32s(f, &s->fifo[1][i]);
+ qemu_put_8s (f, &s->stat_bit[i]);
+ }
+
+ qemu_put_8s (f, &s->lsb_pos);
+ qemu_put_8s (f, &s->non_linear_pcm);
+ qemu_put_8s (f, &s->first_state);
+ qemu_put_8s (f, &s->second_state);
+ qemu_put_be16s(f, &s->rep_period);
+ qemu_put_be16s(f, &s->data_sframe_num);
+
+ qemu_put_timer(f, s->spdif_timer);
+}
+
+static int s5pc1xx_spdif_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxSpdifState *s = (S5pc1xxSpdifState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->spdclkcon);
+ qemu_get_be32s(f, &s->spdcon);
+ qemu_get_be32s(f, &s->spdbstas);
+ qemu_get_be32s(f, &s->spdcstas);
+ qemu_get_be32s(f, &s->spdcnt);
+ qemu_get_be32s(f, &s->version_info);
+
+ for (i = 0; i < 3; i++) {
+ qemu_get_be32s(f, &s->userbit[i]);
+ qemu_get_be32s(f, &s->shd.userbit[i]);
+ }
+
+ qemu_get_be32s(f, &s->shd.spdbstas);
+ qemu_get_be32s(f, &s->shd.spdcnt);
+
+ qemu_get_be32s (f, &s->sclk_freq);
+ qemu_get_sbe64s(f, &s->count);
+ qemu_get_8s (f, &s->updated);
+ qemu_get_be64s (f, &s->read_idx);
+ qemu_get_be64s (f, &s->write_idx);
+ qemu_get_8s (f, &s->read_ch);
+ qemu_get_8s (f, &s->write_ch);
+ qemu_get_be32s (f, &s->sub_frame);
+ qemu_get_s8s (f, &s->fifo_thr);
+ qemu_get_s8s (f, &s->endian_f);
+ qemu_get_sbe16s(f, &s->Pa);
+ qemu_get_sbe16s(f, &s->Pb);
+ qemu_get_sbe16s(f, &s->Pc);
+ qemu_get_sbe16s(f, &s->Pd);
+
+ for (i = 0; i < 16; i++) {
+ qemu_get_be32s(f, &s->fifo[0][i]);
+ qemu_get_be32s(f, &s->fifo[1][i]);
+ qemu_get_8s (f, &s->stat_bit[i]);
+ }
+
+ qemu_get_8s (f, &s->lsb_pos);
+ qemu_get_8s (f, &s->non_linear_pcm);
+ qemu_get_8s (f, &s->first_state);
+ qemu_get_8s (f, &s->second_state);
+ qemu_get_be16s(f, &s->rep_period);
+ qemu_get_be16s(f, &s->data_sframe_num);
+
+ qemu_get_timer(f, s->spdif_timer);
+
+ return 0;
+}
+
+/* SPDIF initialization */
+static int s5pc1xx_spdif_init(SysBusDevice *dev)
+{
+ S5pc1xxSpdifState *s = FROM_SYSBUS(S5pc1xxSpdifState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_spdif_readfn, s5pc1xx_spdif_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_SPDIF_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_SPDIF, 0,
+ s5pc1xx_spdif_gpio_readfn,
+ s5pc1xx_spdif_gpio_writefn, NULL, s);
+
+ s->spdif_timer = qemu_new_timer(vm_clock, s5pc1xx_spdif_sync, s);
+
+ s5pc1xx_spdif_reset(s);
+
+ qemu_register_reset(s5pc1xx_spdif_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.spdif", -1, 1,
+ s5pc1xx_spdif_save, s5pc1xx_spdif_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_spdif_register(void)
+{
+ sysbus_register_dev("s5pc1xx.spdif", sizeof(S5pc1xxSpdifState),
+ s5pc1xx_spdif_init);
+}
+
+device_init(s5pc1xx_spdif_register)
--- /dev/null
+/*
+ * S5PC1XX SPI Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "sysbus.h"
+
+
+#define S5PC1XX_WDT_REG_MEM_SIZE 0x30
+
+
+typedef struct S5pc1xxSPIState {
+ SysBusDevice busdev;
+
+ uint32_t ch_cfg;
+ uint32_t clk_cfg;
+ uint32_t mode_cfg;
+ uint32_t cs_reg;
+ uint32_t spi_int_en;
+ uint32_t spi_status;
+ uint32_t spi_tx_dat;
+ uint32_t spi_rx_dat;
+ uint32_t packet_cnt_reg;
+ uint32_t pending_clr_reg;
+ uint32_t swap_cfg;
+ uint32_t fb_clk_sel;
+
+ qemu_irq irq;
+} S5pc1xxSPIState;
+
+
+static uint32_t s5pc1xx_spi_mm_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return s->ch_cfg;
+ case 0x04:
+ return s->clk_cfg;
+ case 0x08:
+ return s->mode_cfg;
+ case 0x0C:
+ return s->cs_reg;
+ case 0x10:
+ return s->spi_int_en;
+ case 0x14:
+ return s->spi_status;
+ case 0x18:
+ return s->spi_tx_dat;
+ case 0x1C:
+ return s->spi_rx_dat;
+ case 0x20:
+ return s->packet_cnt_reg;
+ case 0x24:
+ return s->pending_clr_reg;
+ case 0x28:
+ return s->swap_cfg;
+ case 0x2C:
+ return s->fb_clk_sel;
+ default:
+ hw_error("s5pc1x_spi: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_spi_mm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->ch_cfg = val;
+ break;
+ case 0x04:
+ s->clk_cfg = val;
+ break;
+ case 0x08:
+ s->mode_cfg = val;
+ break;
+ case 0x0C:
+ s->cs_reg = val;
+ break;
+ case 0x10:
+ s->spi_int_en = val;
+ break;
+ case 0x14:
+ s->spi_status = val;
+ break;
+ case 0x18:
+ s->spi_tx_dat = val;
+ break;
+ case 0x1C:
+ s->spi_rx_dat = val;
+ break;
+ case 0x20:
+ s->packet_cnt_reg = val;
+ break;
+ case 0x24:
+ s->pending_clr_reg = val;
+ break;
+ case 0x28:
+ s->swap_cfg = val;
+ break;
+ case 0x2C:
+ s->fb_clk_sel = val;
+ break;
+ default:
+ hw_error("s5pc1x_spi: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+CPUReadMemoryFunc * const s5pc1xx_spi_readfn[] = {
+ s5pc1xx_spi_mm_read,
+ s5pc1xx_spi_mm_read,
+ s5pc1xx_spi_mm_read
+};
+
+CPUWriteMemoryFunc * const s5pc1xx_spi_writefn[] = {
+ s5pc1xx_spi_mm_write,
+ s5pc1xx_spi_mm_write,
+ s5pc1xx_spi_mm_write
+};
+
+static void s5pc1xx_spi_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+ qemu_put_be32s(f, &s->ch_cfg);
+ qemu_put_be32s(f, &s->clk_cfg);
+ qemu_put_be32s(f, &s->mode_cfg);
+ qemu_put_be32s(f, &s->cs_reg);
+ qemu_put_be32s(f, &s->spi_int_en);
+ qemu_put_be32s(f, &s->spi_status);
+ qemu_put_be32s(f, &s->spi_tx_dat);
+ qemu_put_be32s(f, &s->spi_rx_dat);
+ qemu_put_be32s(f, &s->packet_cnt_reg);
+ qemu_put_be32s(f, &s->pending_clr_reg);
+ qemu_put_be32s(f, &s->swap_cfg);
+ qemu_put_be32s(f, &s->fb_clk_sel);
+}
+
+static int s5pc1xx_spi_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->ch_cfg);
+ qemu_get_be32s(f, &s->clk_cfg);
+ qemu_get_be32s(f, &s->mode_cfg);
+ qemu_get_be32s(f, &s->cs_reg);
+ qemu_get_be32s(f, &s->spi_int_en);
+ qemu_get_be32s(f, &s->spi_status);
+ qemu_get_be32s(f, &s->spi_tx_dat);
+ qemu_get_be32s(f, &s->spi_rx_dat);
+ qemu_get_be32s(f, &s->packet_cnt_reg);
+ qemu_get_be32s(f, &s->pending_clr_reg);
+ qemu_get_be32s(f, &s->swap_cfg);
+ qemu_get_be32s(f, &s->fb_clk_sel);
+
+ return 0;
+}
+
+static void s5pc1xx_spi_reset(void *opaque)
+{
+ S5pc1xxSPIState *s = (S5pc1xxSPIState *)opaque;
+
+ s->ch_cfg = 0;
+ s->clk_cfg = 0;
+ s->mode_cfg = 0;
+ s->cs_reg = 1;
+ s->spi_int_en = 0;
+ s->spi_status = 0;
+ s->spi_tx_dat = 0;
+ s->spi_rx_dat = 0;
+ s->packet_cnt_reg = 0;
+ s->pending_clr_reg = 0;
+ s->swap_cfg = 0;
+ s->fb_clk_sel = 0;
+}
+
+static int s5pc1xx_spi_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxSPIState *s = FROM_SYSBUS(S5pc1xxSPIState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_spi_readfn, s5pc1xx_spi_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_WDT_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_spi_reset(s);
+
+ qemu_register_reset(s5pc1xx_spi_reset, s);
+ register_savevm(&dev->qdev, "s5pc1xx.spi", -1, 1,
+ s5pc1xx_spi_save, s5pc1xx_spi_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_spi_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.spi", sizeof(S5pc1xxSPIState),
+ s5pc1xx_spi_init);
+}
+
+device_init(s5pc1xx_spi_register_devices)
--- /dev/null
+/*
+ * S5PC1XX SROM controller.
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "s5pc1xx.h"
+
+
+typedef struct S5pc1xxSROMState {
+ SysBusDevice busdev;
+
+ /* SROM_BW - SROM Bus width & wait control */
+ uint32_t control;
+ /* SROM_BCn - SROM Bank n control register */
+ uint32_t *bank_control;
+ uint32_t num_banks;
+} S5pc1xxSROMState;
+
+
+static uint32_t s5pc1xx_srom_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+
+ if ((offset > (s->num_banks + 1) * 4) || (offset & 3)) {
+ hw_error("s5pc1xx.srom: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+
+ switch (offset) {
+ case 0x00:
+ return s->control;
+ default:
+ return s->bank_control[offset / 4 - 1];
+ }
+}
+
+static void s5pc1xx_srom_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+
+ if ((offset > (s->num_banks + 1) * 4) || (offset & 3)) {
+ hw_error("s5pc1xx.srom: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+
+ switch (offset) {
+ case 0x00:
+ s->control = val;
+ break;
+ default:
+ s->bank_control[offset / 4 - 1] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_srom_mm_read[] = {
+ s5pc1xx_srom_read,
+ s5pc1xx_srom_read,
+ s5pc1xx_srom_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_srom_mm_write[] = {
+ s5pc1xx_srom_write,
+ s5pc1xx_srom_write,
+ s5pc1xx_srom_write
+};
+
+static void s5pc1xx_srom_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->control);
+ qemu_put_be32s(f, &s->num_banks);
+
+ for (i = 0; i < s->num_banks; i++) {
+ qemu_put_be32s(f, s->bank_control + i);
+ }
+
+}
+
+static int s5pc1xx_srom_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxSROMState *s = (S5pc1xxSROMState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->control);
+ qemu_get_be32s(f, &s->num_banks);
+
+ for (i = 0; i < s->num_banks; i++) {
+ qemu_get_be32s(f, s->bank_control + i);
+ }
+
+ return 0;
+}
+
+static void s5pc1xx_srom_reset(DeviceState *d)
+{
+ S5pc1xxSROMState *s =
+ FROM_SYSBUS(S5pc1xxSROMState, sysbus_from_qdev(d));
+ int i = 0;
+
+ s->control = 0x00000008;
+ for (i = 0; i < s->num_banks; i++) {
+ s->bank_control[i] = 0x000F0000;
+ }
+}
+
+DeviceState *s5pc1xx_srom_init(target_phys_addr_t base, int num_banks)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.srom");
+
+ qdev_prop_set_uint32(dev, "num-banks", num_banks);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ return dev;
+}
+
+static int s5pc1xx_srom_init1(SysBusDevice *dev)
+{
+ S5pc1xxSROMState *s = FROM_SYSBUS(S5pc1xxSROMState, dev);
+ int iomemtype;
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_srom_mm_read, s5pc1xx_srom_mm_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, (s->num_banks + 1) * 4, iomemtype);
+ s->bank_control = qemu_mallocz(s->num_banks * sizeof(uint32_t));
+
+ s5pc1xx_srom_reset(&s->busdev.qdev);
+
+ register_savevm(&dev->qdev, "s5pc1xx.srom", -1, 1,
+ s5pc1xx_srom_save, s5pc1xx_srom_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_srom_info = {
+ .init = s5pc1xx_srom_init1,
+ .qdev.name = "s5pc1xx.srom",
+ .qdev.size = sizeof(S5pc1xxSROMState),
+ .qdev.reset = s5pc1xx_srom_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-banks", S5pc1xxSROMState, num_banks, 6),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_srom_register(void)
+{
+ sysbus_register_withprop(&s5pc1xx_srom_info);
+}
+
+device_init(s5pc1xx_srom_register)
--- /dev/null
+/*
+ * System Timer for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "s5pc1xx.h"
+
+
+#define TCFG 0x00 /* R/W Configures 8-bit-Prescaler and Clock MUX 0x0000_0000 */
+#define TCON 0x04 /* R/W Timer Control Register 0x0000_0000 */
+#define TICNTB 0x08 /* R/W Tick Integer Count Buffer Register 0x0000_0000 */
+#define TICNTO 0x0C /* R Tick Integer Count Observation Register 0x0000_0000 */
+#define TFCNTB 0x10 /* R/W Tick Fractional Count Buffer Register 0x0000_0000 */
+#define ICNTB 0x18 /* R/W Interrupt Count Buffer Register 0x0000_0000 */
+#define ICNTO 0x1C /* R Interrupt Count Observation Register 0x0000_0000 */
+#define INT_CSTAT 0x20 /* R/W Interrupt Control and Status Register 0x0000_0000 */
+
+/* TCFG */
+#define TICK_SWRST (1 << 16) /* SW reset of TICK generation logic */
+#define FDIV_SEL (1 << 15) /* Fractional divider select */
+#define TICKGEN_SEL (1 << 14) /* 0 = Integer divider 1 = Fractional divider */
+#define TCLKB_MUX (3 << 12) /* Selects clock input for TCLKB */
+#define DIV_MUX (7 << 8) /* Selects Mux input for Timer */
+#define PRESCALER (0xFF << 0) /* Prescaler value for timer 0x00 */
+
+/* TCON */
+#define INT_AUTO_RELOAD (1 << 5) /* 0 = No operation 1 = Interval mode(auto-reload) */
+#define INT_MAN_UPD (1 << 4) /* 0 = No operation 1 = Update ICNTB and One-shot mode */
+#define INT_RUN (1 << 3) /* 0 = Stop 1 = Start timer */
+#define TIMER_RUN (1 << 0) /* 0 = Stop 1 = Start timer */
+
+/* INT_CSTAT */
+#define TWIE (1 << 10) /* TCON Write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define IWIE (1 << 9) /* ICNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define TFWIE (1 << 8) /* TFCNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define TIWIE (1 << 7) /* TICNTB write Interrupt Enable / 0: Disable, 1: Enable 0x0 */
+#define ICNTEIE (1 << 6) /* Interrupt counter expired (INTCNT=0) Interrupt Enable */
+#define TCON_W_STAT (1 << 5) /* TCON Write Interrupt Status Bit */
+#define ICNTB_W_STAT (1 << 4) /* ICNTB Write Interrupt Status Bit */
+#define TFCNTB_W_STAT (1 << 3) /* TFCTNB Write Interrupt Status Bit */
+#define TICNTB_W_STAT (1 << 2) /* TICTNB Write Interrupt Status Bit */
+#define INTCNT_EXP_STAT (1 << 1) /* Interrupt counter expired (INTCNT=0) Interrupt Status Bit */
+#define INT_ENABLE (1 << 0) /* Enables Interrupt */
+#define ALL_STAT (TCON_W_STAT | ICNTB_W_STAT | TFCNTB_W_STAT | \
+ TICNTB_W_STAT | INTCNT_EXP_STAT)
+
+#define S5PC1XX_ST_REG_MEM_SIZE 0x24
+
+
+typedef struct {
+ SysBusDevice busdev;
+
+ uint32_t tcfg;
+ uint32_t tcon;
+ uint32_t ticntb;
+ uint32_t ticnto;
+ uint32_t tfcntb;
+ uint32_t icntb;
+ int32_t icnto; /* has signed type */
+ uint32_t int_cstat;
+
+ qemu_irq irq;
+
+ uint8_t divider;
+ uint8_t prescaler;
+
+ QEMUTimer *st_timer;
+ uint32_t freq_out;
+ uint64_t tick_interval;
+ uint64_t last_tick;
+ uint64_t next_planned_tick;
+ uint64_t base_time;
+} S5pc1xxSTState;
+
+
+const char *st_clks[] = { "XXTI", "XrtcXTI", "XusbXTI", "pclk_66" };
+
+static void s5pc1xx_st_tick(void *opaque);
+
+/* work with interrupts depending on permissive bits */
+static void s5pc1xx_st_irq(S5pc1xxSTState *s, uint32_t enab_mask,
+ uint32_t stat_mask)
+{
+ /* stop tick timer */
+ if ((stat_mask == TICNTB_W_STAT) || (stat_mask == TFCNTB_W_STAT)) {
+ s->tcon &= ~TIMER_RUN;
+ s5pc1xx_st_tick(s);
+ }
+
+ /* reload ICNT after manual update */
+ if ((stat_mask == ICNTB_W_STAT) && (s->tcon & INT_MAN_UPD))
+ s->icnto = s->icntb;
+
+ /* raise irq */
+ if ((s->int_cstat & INT_ENABLE) && (s->int_cstat & enab_mask)) {
+ qemu_irq_raise(s->irq);
+ }
+
+ s->int_cstat |= stat_mask;
+}
+
+static void s5pc1xx_st_set_timer(S5pc1xxSTState *s)
+{
+ uint64_t last = qemu_get_clock(vm_clock) - s->base_time;
+ /* make a tick each tick_interval'th QEMU timer cycle - this way
+ * system timer is working consistently (1 second for emulated machine
+ * corresponds to 1 second for host); otherwise due to QEMU timer interrupt
+ * handling overhead it will slowly drift towards the past */
+ s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
+ qemu_mod_timer(s->st_timer, s->next_planned_tick + s->base_time);
+ s->last_tick = last;
+}
+
+/* counter step */
+static void s5pc1xx_st_tick(void *opaque)
+{
+ S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+ /* tick actually happens not every ticnto but rather at icnto update;
+ * if current ticnto value is needed it is calculated in s5pc1xx_st_read */
+ if (s->tcon & TIMER_RUN) {
+ if (s->tcon & INT_RUN) {
+ /* reload count */
+ if (s->icnto == 0 && s->tcon & INT_AUTO_RELOAD)
+ s->icnto = s->icntb;
+
+ s->icnto--;
+
+ /* time for interrupt */
+ if (s->icnto <= 0) {
+ s->icnto = 0;
+ s5pc1xx_st_irq(s, ICNTEIE, INTCNT_EXP_STAT);
+ }
+ }
+
+ /* schedule next interrupt */
+ s5pc1xx_st_set_timer(s);
+ } else {
+ s->next_planned_tick = 0;
+ s->last_tick = 0;
+ qemu_del_timer(s->st_timer);
+ }
+}
+
+/* set default values for all fields */
+static void s5pc1xx_st_reset(S5pc1xxSTState *s)
+{
+ /* TODO: Check if reseting all counters is needed */
+ s->tcfg = 0;
+ s->tcon = 0;
+ s->ticntb = 0;
+ s->ticnto = 0;
+ s->tfcntb = 0;
+ s->icntb = 1;
+ s->icnto = 0;
+ s->int_cstat = 0;
+
+ s->last_tick = 0;
+ s->next_planned_tick = 0;
+ s->freq_out = 0;
+ s->tick_interval = 0;
+ s->divider = 1;
+ s->prescaler = 0;
+ s->base_time = qemu_get_clock(vm_clock);
+
+ qemu_del_timer(s->st_timer);
+}
+
+/* update timer frequency */
+static void s5pc1xx_st_update(S5pc1xxSTState *s)
+{
+ S5pc1xxClk clk;
+
+ s->divider = 1 << ((s->tcfg & DIV_MUX) >> 8);
+ s->prescaler = s->tcfg & PRESCALER;
+
+ clk = s5pc1xx_findclk(st_clks[(s->tcfg & TCLKB_MUX) >> 12]);
+ s->freq_out = s5pc1xx_clk_getrate(clk) / (s->prescaler + 1) / s->divider;
+ s->tick_interval =
+ muldiv64(s->ticntb, get_ticks_per_sec(), s->freq_out) +
+ (muldiv64(s->tfcntb, get_ticks_per_sec(), s->freq_out) >> 16);
+ s->next_planned_tick = 0;
+
+ if (!s->freq_out)
+ hw_error("s5pc1xx.st: timer update input frequency is zero\n");
+}
+
+/* System Timer read */
+static uint32_t s5pc1xx_st_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+ uint64_t cur_clock, tps;
+
+ switch (offset) {
+ case TCFG:
+ return s->tcfg;
+ case TCON:
+ return s->tcon;
+ case TICNTB:
+ return s->ticntb;
+ case TICNTO:
+ if (s->freq_out && s->last_tick && s->ticntb && s->next_planned_tick) {
+ cur_clock = qemu_get_clock(vm_clock) - s->base_time;
+ if (cur_clock < s->next_planned_tick) {
+ if (s->tick_interval > 0xFFFFFFFF) {
+ /* very large tick interval; muldiv64 can't be used
+ * in this case directly; avoid 64-bit difference by
+ * knowing the fact that next_planned_tick and last_tick
+ * may be represented as "next_planned_tick =
+ * K * tick_interval" and "last_tick = N * tick_interval"
+ * assuming that last_tick happened in time */
+ tps = get_ticks_per_sec();
+ s->ticnto = muldiv64(s->next_planned_tick - cur_clock,
+ s->ticntb,
+ (s->next_planned_tick - s->last_tick +
+ tps / 2) / tps) / tps;
+ } else {
+ /* tick interval is 32-bit so both differences
+ * should be 32-bit too */
+ s->ticnto = muldiv64(s->next_planned_tick - cur_clock, s->ticntb,
+ s->next_planned_tick - s->last_tick);
+ }
+ } else {
+ s->ticnto = 0;
+ }
+ } else {
+ s->ticnto = 0;
+ }
+ return s->ticnto;
+ case TFCNTB:
+ return s->tfcntb;
+ case ICNTB:
+ return s->icntb - 1;
+ case ICNTO:
+ return s->icnto;
+ case INT_CSTAT:
+ return s->int_cstat;
+ default:
+ hw_error("s5pc1xx.st: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* System Timer write */
+static void s5pc1xx_st_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+ switch (offset) {
+ case TCFG:
+ if (value & TICK_SWRST) {
+ s5pc1xx_st_reset(s);
+ break;
+ }
+ s->tcfg = value;
+ s5pc1xx_st_update(s);
+ break;
+
+ case TCON:
+ s5pc1xx_st_irq(s, TWIE, TCON_W_STAT);
+
+ if ((value & TIMER_RUN) > (s->tcon & TIMER_RUN)) {
+ s->base_time = qemu_get_clock(vm_clock);
+ s5pc1xx_st_set_timer(s);
+ } else if ((value & TIMER_RUN) < (s->tcon & TIMER_RUN)) {
+ qemu_del_timer(s->st_timer);
+ }
+ s->tcon = value;
+ break;
+
+ case TICNTB:
+ /* in this case s->ticntb is updated after interrupt raise
+ * since the timer must be stopped first */
+ s5pc1xx_st_irq(s, TIWIE, TICNTB_W_STAT);
+ s->ticntb = value;
+ s5pc1xx_st_update(s);
+ break;
+
+ case TFCNTB:
+ s5pc1xx_st_irq(s, TFWIE, TFCNTB_W_STAT);
+ s->tfcntb = value;
+ s5pc1xx_st_update(s);
+ break;
+
+ case ICNTB:
+ /* in this case s->icntb is updated before interrupt raise
+ * since the value is needed for manual update */
+ s->icntb = value + 1;
+ s5pc1xx_st_irq(s, IWIE, ICNTB_W_STAT);
+ break;
+
+ case INT_CSTAT:
+ /* set INT_CSTAT as value except *_STAT bits */
+ s->int_cstat = (s->int_cstat & ALL_STAT) | (value & ~ALL_STAT);
+ /* clear *_STAT bits if they are set in value */
+ s->int_cstat &= ~(value & ALL_STAT);
+
+ /* lower interrupt */
+ /* TODO: check if IRQ should be lowered for all cases or
+ * only when there are no more stat bits left */
+ if (!(s->int_cstat & ALL_STAT))
+ qemu_irq_lower(s->irq);
+ break;
+
+ default:
+ hw_error("s5pc1xx.st: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_st_readfn[] = {
+ s5pc1xx_st_read,
+ s5pc1xx_st_read,
+ s5pc1xx_st_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_st_writefn[] = {
+ s5pc1xx_st_write,
+ s5pc1xx_st_write,
+ s5pc1xx_st_write
+};
+
+static void s5pc1xx_st_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+ qemu_put_be32s (f, &s->tcfg);
+ qemu_put_be32s (f, &s->tcon);
+ qemu_put_be32s (f, &s->ticntb);
+ qemu_put_be32s (f, &s->ticnto);
+ qemu_put_be32s (f, &s->tfcntb);
+ qemu_put_be32s (f, &s->icntb);
+ qemu_put_sbe32s(f, &s->icnto);
+ qemu_put_be32s (f, &s->int_cstat);
+
+ qemu_put_8s (f, &s->divider);
+ qemu_put_8s (f, &s->prescaler);
+ qemu_put_be32s (f, &s->freq_out);
+
+ qemu_put_be64s (f, &s->tick_interval);
+ qemu_put_be64s (f, &s->last_tick);
+ qemu_put_be64s (f, &s->next_planned_tick);
+ qemu_put_be64s (f, &s->base_time);
+
+ qemu_put_timer (f, s->st_timer);
+}
+
+static int s5pc1xx_st_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxSTState *s = (S5pc1xxSTState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s (f, &s->tcfg);
+ qemu_get_be32s (f, &s->tcon);
+ qemu_get_be32s (f, &s->ticntb);
+ qemu_get_be32s (f, &s->ticnto);
+ qemu_get_be32s (f, &s->tfcntb);
+ qemu_get_be32s (f, &s->icntb);
+ qemu_get_sbe32s(f, &s->icnto);
+ qemu_get_be32s (f, &s->int_cstat);
+
+ qemu_get_8s (f, &s->divider);
+ qemu_get_8s (f, &s->prescaler);
+ qemu_get_be32s (f, &s->freq_out);
+
+ qemu_get_be64s (f, &s->tick_interval);
+ qemu_get_be64s (f, &s->last_tick);
+ qemu_get_be64s (f, &s->next_planned_tick);
+ qemu_get_be64s (f, &s->base_time);
+
+ qemu_get_timer (f, s->st_timer);
+
+ return 0;
+}
+
+/* System Timer init */
+static int s5pc1xx_st_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxSTState *s = FROM_SYSBUS(S5pc1xxSTState, dev);
+
+ s->st_timer = qemu_new_timer(vm_clock, s5pc1xx_st_tick, s);
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_st_readfn, s5pc1xx_st_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_ST_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_st_reset(s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.st", -1, 1,
+ s5pc1xx_st_save, s5pc1xx_st_load, s);
+ return 0;
+}
+
+static void s5pc1xx_st_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.st", sizeof(S5pc1xxSTState), s5pc1xx_st_init);
+}
+
+device_init(s5pc1xx_st_register_devices)
--- /dev/null
+/*
+ * S5PC1XX ADC & TOUCH SCREEN INTERFACE
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "console.h"
+
+
+#define QEMUMAXX 0x7FFF
+#define QEMUMAXY 0x7FFF
+
+#define WAIT_FOR_INT 3
+
+#define S5PC1XX_TSADC_REG_MEM_SIZE 0x24
+
+
+typedef union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct tsadccon_bits {
+ unsigned enable_start : 1;
+ unsigned read_start : 1;
+ unsigned standby : 1;
+ unsigned reserved3_5 : 3;
+ unsigned prscvl : 8;
+ unsigned prscen : 1;
+ unsigned ecflg : 1; /* (Read only) */
+ unsigned res : 1;
+ unsigned tssel : 1;
+ } b;
+} tsadccon_s;
+
+typedef union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct tsdat_bits {
+ /*XPDATA or YPDATA*/
+ unsigned pdata : 12;
+ unsigned xy_pst_val : 2;
+ unsigned auto_pst_val : 1;
+ unsigned updown : 1;
+ } b;
+} tsdat_s;
+
+typedef union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct tscon_bits {
+ unsigned xy_pst : 2;
+ unsigned auto_pst : 1;
+ unsigned pull_up : 1;
+ unsigned xp_sen : 1;
+ unsigned xm_sen : 1;
+ unsigned yp_sen : 1;
+ unsigned ym_sen : 1;
+ unsigned ud_sen : 1;
+ } b;
+} tscon_s;
+
+typedef union {
+ /* raw register data */
+ uint32_t v;
+
+ /* register bits */
+ struct tspenstat_bits {
+ unsigned tsc_dn : 1;
+ unsigned tsc_up : 1;
+ } b;
+} tspenstat_s;
+
+typedef struct S5pc1xxTSADCState {
+ SysBusDevice busdev;
+
+ /* R/W Specifies the TSn - ADC Control Register */
+ tsadccon_s tsadccon;
+
+ /* R/W Specifies the TSn - Touch Screen Control Register */
+ tscon_s tscon;
+
+ /* R/W Specifies the TSn - ADC Start or Interval Delay Register */
+ uint32_t tsdly;
+
+ /* R Specifies the TSn - ADC Conversion Data X Register */
+ tsdat_s tsdat0;
+
+ /* R Specifies the TSn - ADC Conversion Data Y Register */
+ tsdat_s tsdat1;
+
+ /* R/W Specifies the TSn - Penn Up or Down Status Register */
+ tspenstat_s tspenstat;
+
+ /* R/W Specifies the Analog input channel selection */
+ uint32_t adcmux;
+
+ /* Internal data */
+
+ /* ID of this device instance */
+ uint32_t id;
+ /* Current pointer coordinates ({0,0} < {x,y} < {QEMUMAXX,QEMUMAXY}) */
+ uint32_t x, y;
+ /* Boundary reported coordinates */
+ uint32_t minx, maxx, miny, maxy;
+ /* Is it a 'new' touchscreen version? */
+ int32_t new;
+ /* Touchscreen resolution, max 12 bit */
+ uint32_t resolution;
+ /* Current mouse buttons state */
+ int32_t pressure;
+ /* Currently reported touchscreen touch state */
+ int32_t report_pressure;
+
+ /* Interrupts */
+ qemu_irq irq_adc;
+ qemu_irq irq_pennd;
+
+ /* Timer to report conversion data periodically */
+ QEMUTimer *timer;
+ uint32_t conversion_time;
+} S5pc1xxTSADCState;
+
+
+static void s5pc1xx_tsadc_reset(DeviceState *d)
+{
+ S5pc1xxTSADCState *s =
+ FROM_SYSBUS(S5pc1xxTSADCState, sysbus_from_qdev(d));
+
+ s->tsadccon.v = 0x00003FC4;
+ s->tscon.v = 0x00000058;
+ s->tsdly = 0x000000FF;
+ s->tsdat0.v = 0x00000000;
+ s->tsdat1.v = 0x00000000;
+ s->tspenstat.v = 0x00000000;
+ s->adcmux = 0x00000000;
+
+ s->x = s->y = 0;
+ s->pressure = 0;
+ s->report_pressure = 0;
+ s->conversion_time = 0;
+}
+
+static void s5pc1xx_tsadc_conversion_start(S5pc1xxTSADCState *s,
+ unsigned int time)
+{
+ /* Do not do anything in standby mode */
+ if (!s->tsadccon.b.standby) {
+ /* Conversion is going... */
+ s->tsadccon.b.ecflg = 0;
+ /* ... and will finish in 'time' QEMU vm_clock ticks */
+ qemu_mod_timer(s->timer,
+ qemu_get_clock(vm_clock) + time);
+ }
+}
+
+static void s5pc1xx_tsadc_conversion(void *opaque)
+{
+ S5pc1xxTSADCState *s = opaque;
+
+ s->tsadccon.b.ecflg = 1;
+
+ /* Generate IRQ_ADC for any mode except 'Waiting for interrupt' */
+ if (s->tscon.b.xy_pst != WAIT_FOR_INT) {
+ qemu_irq_raise(s->irq_adc);
+ } else {
+ /* If mouse buttons state changed recently - report it */
+ if (s->report_pressure != s->pressure &&
+ ((s->pressure == 1 && s->tscon.b.ud_sen == 0) ||
+ (s->pressure == 0 && s->tscon.b.ud_sen == 1))) {
+ qemu_irq_raise(s->irq_pennd);
+ s->tspenstat.b.tsc_dn |= s->pressure;
+ s->tspenstat.b.tsc_up |= !s->pressure;
+ }
+ s->report_pressure = s->pressure;
+ }
+}
+
+static uint32_t s5pc1xx_tsadc_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxTSADCState *s = (S5pc1xxTSADCState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return s->tsadccon.v;
+ case 0x04:
+ return s->tscon.v;
+ case 0x08:
+ return s->tsdly;
+ case 0x0C:
+ if (s->new) {
+ s->tsdat0.b.pdata =
+ ((1 << s->resolution) - 1) -
+ (s->miny + s->y * (s->maxy - s->miny) / QEMUMAXY);
+ } else {
+ s->tsdat0.b.pdata = s->minx + s->x * (s->maxx - s->minx) / QEMUMAXX;
+ }
+ s->tsdat0.b.updown = s->report_pressure == 0;
+ s->tsdat0.b.auto_pst_val = s->tscon.b.auto_pst;
+ s->tsdat0.b.xy_pst_val = s->tscon.b.xy_pst;
+ if (s->tsadccon.b.read_start)
+ s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+ return s->tsdat0.v;
+ case 0x10:
+ if (s->new) {
+ s->tsdat1.b.pdata =
+ ((1 << s->resolution) - 1) -
+ (s->minx + s->x * (s->maxx - s->minx) / QEMUMAXX);
+ } else {
+ s->tsdat1.b.pdata = s->miny + s->y * (s->maxy - s->miny) / QEMUMAXY;
+ }
+ s->tsdat1.b.updown = s->report_pressure == 0;
+ s->tsdat1.b.auto_pst_val = s->tscon.b.auto_pst;
+ s->tsdat1.b.xy_pst_val = s->tscon.b.xy_pst;
+ if (s->tsadccon.b.read_start)
+ s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+ return s->tsdat1.v;
+ case 0x14:
+ return s->tspenstat.v;
+ case 0x18:
+ return 0x0;
+ case 0x1C:
+ return s->adcmux;
+ case 0x20:
+ return 0x0;
+ default:
+ hw_error("s5pc1xx.tsadc: bad read offset " TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_tsadc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxTSADCState *s = (S5pc1xxTSADCState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->tsadccon.v = val;
+ if (!s->tsadccon.b.read_start && s->tsadccon.b.enable_start)
+ s5pc1xx_tsadc_conversion_start(s, s->conversion_time);
+ s->tsadccon.b.enable_start = 0;
+ /* FIXME: choose the correct clock depending on a register value */
+ s->conversion_time =
+ muldiv64(s->tsdly & 0xFFFF, get_ticks_per_sec(),
+ s5pc1xx_clk_getrate(s5pc1xx_findclk("pclk_66")) /
+ (s->tsadccon.b.prscen ? s->tsadccon.b.prscvl + 1: 1));
+ break;
+ case 0x04:
+ s->tscon.v = val;
+ if (s->report_pressure != s->pressure &&
+ s->tscon.b.xy_pst == WAIT_FOR_INT) {
+ /* Raise next IRQ for touch-up in one ms */
+ s5pc1xx_tsadc_conversion_start(s, get_ticks_per_sec() / 1000);
+ }
+ break;
+ case 0x08:
+ s->tsdly = val;
+ /* FIXME: choose the correct clock depending on a register value */
+ s->conversion_time =
+ muldiv64(s->tsdly & 0xFFFF, get_ticks_per_sec(),
+ s5pc1xx_clk_getrate(s5pc1xx_findclk("pclk_66")) /
+ (s->tsadccon.b.prscen ? s->tsadccon.b.prscvl + 1: 1));
+ break;
+ case 0x14:
+ s->tspenstat.v = val;
+ break;
+ case 0x18:
+ qemu_irq_lower(s->irq_adc);
+ break;
+ case 0x1C:
+ s->adcmux = val;
+ break;
+ case 0x20:
+ qemu_irq_lower(s->irq_pennd);
+ break;
+ default:
+ hw_error("s5pc1xx.tsadc: bad write offset " TARGET_FMT_plx "\n",
+ offset);
+ break;
+ }
+}
+
+static void s5pc1xx_touchscreen_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ S5pc1xxTSADCState *s = opaque;
+
+ if (buttons_state) {
+ s->x = x;
+ s->y = y;
+ }
+
+ if (s->pressure == !buttons_state) {
+ s->pressure = !!buttons_state;
+
+ /* Report button state change momentarily if it happens in
+ * 'Waiting for interrupt' mode */
+ if (s->tscon.b.xy_pst == WAIT_FOR_INT && !s->tsadccon.b.standby) {
+ s5pc1xx_tsadc_conversion(s);
+ }
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_tsadc_mm_read[] = {
+ s5pc1xx_tsadc_read,
+ s5pc1xx_tsadc_read,
+ s5pc1xx_tsadc_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_tsadc_mm_write[] = {
+ s5pc1xx_tsadc_write,
+ s5pc1xx_tsadc_write,
+ s5pc1xx_tsadc_write
+};
+
+static void s5pc1xx_tsadc_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxTSADCState *s = opaque;
+
+ qemu_put_be32s (f, &s->tsadccon.v);
+ qemu_put_be32s (f, &s->tscon.v);
+ qemu_put_be32s (f, &s->tsdat0.v);
+ qemu_put_be32s (f, &s->tsdat1.v);
+ qemu_put_be32s (f, &s->tspenstat.v);
+
+ qemu_put_be32s (f, &s->tsdly);
+ qemu_put_be32s (f, &s->adcmux);
+ qemu_put_be32s (f, &s->x);
+ qemu_put_be32s (f, &s->y);
+ qemu_put_sbe32s(f, &s->pressure);
+ qemu_put_sbe32s(f, &s->report_pressure);
+ qemu_put_be32s (f, &s->conversion_time);
+ qemu_put_timer (f, s->timer);
+}
+
+static int s5pc1xx_tsadc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxTSADCState *s = opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s (f, &s->tsadccon.v);
+ qemu_get_be32s (f, &s->tscon.v);
+ qemu_get_be32s (f, &s->tsdat0.v);
+ qemu_get_be32s (f, &s->tsdat1.v);
+ qemu_get_be32s (f, &s->tspenstat.v);
+
+ qemu_get_be32s (f, &s->tsdly);
+ qemu_get_be32s (f, &s->adcmux);
+ qemu_get_be32s (f, &s->x);
+ qemu_get_be32s (f, &s->y);
+ qemu_get_sbe32s(f, &s->pressure);
+ qemu_get_sbe32s(f, &s->report_pressure);
+ qemu_get_be32s (f, &s->conversion_time);
+ qemu_get_timer (f, s->timer);
+
+ return 0;
+}
+
+DeviceState *s5pc1xx_tsadc_init(target_phys_addr_t base, qemu_irq irq_adc,
+ qemu_irq irq_pennd, int new, int resolution,
+ int minx, int maxx, int miny, int maxy)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, new ? "s5pc1xx.tsadc.new" : "s5pc1xx.tsadc");
+ qdev_prop_set_uint32(dev, "resolution", resolution);
+ qdev_prop_set_uint32(dev, "minx", minx);
+ qdev_prop_set_uint32(dev, "miny", miny);
+ qdev_prop_set_uint32(dev, "maxx", maxx);
+ qdev_prop_set_uint32(dev, "maxy", maxy);
+
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq_adc);
+ sysbus_connect_irq(s, 1, irq_pennd);
+ sysbus_mmio_map(s, 0, base);
+
+ return dev;
+}
+
+static int s5pc1xx_tsadc_init1(SysBusDevice *dev)
+{
+ S5pc1xxTSADCState *s = FROM_SYSBUS(S5pc1xxTSADCState, dev);
+ int iomemtype;
+ /* Current number of S3C Touchscreen controllers */
+ static int s5pc1xx_tsadc_number = 0;
+ char name[30];
+
+ s->id = s5pc1xx_tsadc_number++;
+ snprintf(name, 30, "QEMU s5pc1xx Touchscreen %d", s->id);
+ iomemtype = cpu_register_io_memory(s5pc1xx_tsadc_mm_read,
+ s5pc1xx_tsadc_mm_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_TSADC_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_tsadc_reset(&s->busdev.qdev);
+
+ sysbus_init_irq(dev, &s->irq_adc);
+ sysbus_init_irq(dev, &s->irq_pennd);
+ s->timer = qemu_new_timer(vm_clock, s5pc1xx_tsadc_conversion, s);
+
+ qemu_add_mouse_event_handler(s5pc1xx_touchscreen_event, s, 1, name);
+ register_savevm(&dev->qdev, "s5pc1xx.tsadc", s->id, 1,
+ s5pc1xx_tsadc_save, s5pc1xx_tsadc_load, s);
+
+ return 0;
+}
+
+static int s5pc1xx_tsadc_new_init1(SysBusDevice *dev)
+{
+ S5pc1xxTSADCState *s = FROM_SYSBUS(S5pc1xxTSADCState, dev);
+
+ s->new = 1;
+ return s5pc1xx_tsadc_init1(dev);
+}
+
+static SysBusDeviceInfo s5pc1xx_tsadc_info = {
+ .init = s5pc1xx_tsadc_init1,
+ .qdev.name = "s5pc1xx.tsadc",
+ .qdev.size = sizeof(S5pc1xxTSADCState),
+ .qdev.reset = s5pc1xx_tsadc_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("resolution", S5pc1xxTSADCState, resolution, 12),
+ DEFINE_PROP_UINT32("minx", S5pc1xxTSADCState, minx, 0),
+ DEFINE_PROP_UINT32("miny", S5pc1xxTSADCState, miny, 0),
+ DEFINE_PROP_UINT32("maxx", S5pc1xxTSADCState, maxx, 480),
+ DEFINE_PROP_UINT32("maxy", S5pc1xxTSADCState, maxy, 800),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static SysBusDeviceInfo s5pc1xx_tsadc_new_info = {
+ .init = s5pc1xx_tsadc_new_init1,
+ .qdev.name = "s5pc1xx.tsadc.new",
+ .qdev.size = sizeof(S5pc1xxTSADCState),
+ .qdev.reset = s5pc1xx_tsadc_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("resolution", S5pc1xxTSADCState, resolution, 12),
+ DEFINE_PROP_UINT32("minx", S5pc1xxTSADCState, minx, 0),
+ DEFINE_PROP_UINT32("miny", S5pc1xxTSADCState, miny, 0),
+ DEFINE_PROP_UINT32("maxx", S5pc1xxTSADCState, maxx, 480),
+ DEFINE_PROP_UINT32("maxy", S5pc1xxTSADCState, maxy, 800),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_tsadc_register_devices(void)
+{
+ sysbus_register_withprop(&s5pc1xx_tsadc_info);
+ sysbus_register_withprop(&s5pc1xx_tsadc_new_info);
+}
+
+device_init(s5pc1xx_tsadc_register_devices)
--- /dev/null
+/*
+ * S5PC1XX UART Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "s5pc1xx.h"
+#include "s5pc1xx_gpio_regs.h"
+
+
+#define QUEUE_SIZE 257
+
+#define INT_RXD (1 << 0)
+#define INT_ERROR (1 << 1)
+#define INT_TXD (1 << 2)
+#define INT_MODEM (1 << 3)
+
+#define TRSTATUS_TRANSMITTER_READY (1 << 2)
+#define TRSTATUS_BUFFER_EMPTY (1 << 1)
+#define TRSTATUS_DATA_READY (1 << 0)
+
+#define UFSTAT_RX_FIFO_FULL (1 << 8)
+
+#define UFCON_FIFO_ENABLED (1 << 0)
+#define UFCON_TX_LEVEL_SHIFT 8
+#define UFCON_TX_LEVEL (7 << UFCON_TX_LEVEL_SHIFT)
+
+#define UFSTAT_TX_COUNT_SHIT 16
+#define UFSTAT_TX_COUNT (0xFF << UFSTAT_TX_COUNT_SHIT)
+
+#define QI(x) ((x + 1) % QUEUE_SIZE)
+#define QD(x) ((x - 1 + QUEUE_SIZE) % QUEUE_SIZE)
+
+#define S5PC1XX_UART_REG_MEM_SIZE 0x3C
+
+typedef struct UartQueue {
+ uint8_t queue[QUEUE_SIZE];
+ uint32_t s, t;
+ uint32_t size;
+} UartQueue;
+
+typedef struct S5pc1xxUartState {
+ SysBusDevice busdev;
+
+ UartQueue rx;
+
+ uint32_t ulcon;
+ uint32_t ucon;
+ uint32_t ufcon;
+ uint32_t umcon;
+ uint32_t utrstat;
+ uint32_t uerstat;
+ uint32_t ufstat;
+ uint32_t umstat;
+ uint32_t utxh;
+ uint32_t urxh;
+ uint32_t ubrdiv;
+ uint32_t udivslot;
+ uint32_t uintp;
+ uint32_t uintsp;
+ uint32_t uintm;
+
+ CharDriverState *chr;
+ qemu_irq irq;
+ uint32_t instance;
+} S5pc1xxUartState;
+
+
+static inline int queue_elem_count(const UartQueue *s)
+{
+ if (s->t >= s->s) {
+ return s->t - s->s;
+ } else {
+ return QUEUE_SIZE - s->s + s->t;
+ }
+}
+
+static inline int queue_empty_count(const UartQueue *s)
+{
+ return s->size - queue_elem_count(s) - 1;
+}
+
+static inline int queue_empty(const UartQueue *s)
+{
+ return (queue_elem_count(s) == 0);
+}
+
+static inline void queue_push(UartQueue *s, uint8_t x)
+{
+ s->queue[s->t] = x;
+ s->t = QI(s->t);
+}
+
+static inline uint8_t queue_get(UartQueue *s)
+{
+ uint8_t ret;
+
+ ret = s->queue[s->s];
+ s->s = QI(s->s);
+ return ret;
+}
+
+static inline void queue_reset(UartQueue *s)
+{
+ s->s = 0;
+ s->t = 0;
+}
+
+static void s5pc1xx_uart_update(S5pc1xxUartState *s)
+{
+ if (s->ufcon && UFCON_FIFO_ENABLED) {
+ if (((s->ufstat && UFSTAT_TX_COUNT) >> UFSTAT_TX_COUNT_SHIT) <=
+ ((s->ufcon && UFCON_TX_LEVEL) >> UFCON_TX_LEVEL_SHIFT) * 2 ) {
+ s->uintsp |= INT_TXD;
+ }
+ }
+
+ s->uintp = s->uintsp & ~s->uintm;
+ if (s->uintp) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+/* Read UART by GPIO */
+static uint32_t s5pc1xx_uart_gpio_read(void *opaque,
+ int io_index)
+{
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ /* TODO: check if s->uintp should be used instead of s->uintsp */
+ if (io_index == GPIO_UART_RXD(s->instance)) {
+ return (s->uintsp & INT_RXD);
+ }
+ if (io_index == GPIO_UART_TXD(s->instance)) {
+ return (s->uintsp & INT_TXD);
+ }
+
+ /* TODO: check if this is correct */
+ if (io_index == GPIO_UART_CTS(s->instance)) {
+ return ~(s->umstat & 0x1);
+ }
+ if (io_index == GPIO_UART_RTS(s->instance)) {
+ return ~(s->umcon & 0x1);
+ }
+
+ /* TODO: return correct values */
+ if (io_index == GPIO_UART_AUDIO_RXD) {
+ return 0;
+ }
+ if (io_index == GPIO_UART_AUDIO_TXD) {
+ return 0;
+ }
+
+ return 0;
+}
+
+static GPIOReadMemoryFunc *s5pc1xx_uart_gpio_readfn = s5pc1xx_uart_gpio_read;
+static GPIOWriteMemoryFunc *s5pc1xx_uart_gpio_writefn = s5pc1xx_empty_gpio_write; /* a gag */
+
+static uint32_t s5pc1xx_uart_mm_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t res;
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return s->ulcon;
+ case 0x04:
+ return s->ucon;
+ case 0x08:
+ return s->ufcon;
+ case 0x0C:
+ return s->umcon;
+ case 0x10:
+ return s->utrstat;
+ case 0x14:
+ res = s->uerstat;
+ s->uerstat = 0;
+ return res;
+ case 0x18:
+ s->ufstat = queue_elem_count(&s->rx) & 0xff;
+ if (queue_empty_count(&s->rx) == 0) {
+ s->ufstat |= UFSTAT_RX_FIFO_FULL;
+ }
+ return s->ufstat;
+ case 0x1C:
+ return s->umstat;
+ case 0x24:
+ if (s->ufcon & 1) {
+ if (! queue_empty(&s->rx)) {
+ res = queue_get(&s->rx);
+ if (queue_empty(&s->rx)) {
+ s->utrstat &= ~TRSTATUS_DATA_READY;
+ } else {
+ s->utrstat |= TRSTATUS_DATA_READY;
+ }
+ } else {
+ s->uintsp |= INT_ERROR;
+ s5pc1xx_uart_update(s);
+ res = 0;
+ }
+ } else {
+ s->utrstat &= ~TRSTATUS_DATA_READY;
+ res = s->urxh;
+ }
+ return res;
+ case 0x28:
+ return s->ubrdiv;
+ case 0x2C:
+ return s->udivslot;
+ case 0x30:
+ return s->uintp;
+ case 0x34:
+ return s->uintsp;
+ case 0x38:
+ return s->uintm;
+ default:
+ hw_error("s5pc1xx.uart: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_uart_mm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ uint8_t ch;
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->ulcon = val;
+ break;
+ case 0x04:
+ s->ucon = val;
+ break;
+ case 0x08:
+ s->ufcon = val;
+ if (val & 2) {
+ queue_reset(&s->rx);
+ }
+ s->ufcon &= ~6;
+ break;
+ case 0x0C:
+ s->umcon = val;
+ break;
+ case 0x20:
+ if (s->chr) {
+ s->utrstat &= ~(TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY);
+ ch = (uint8_t)val;
+ qemu_chr_write(s->chr, &ch, 1);
+ s->utrstat |= TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY;
+ s->uintsp |= INT_TXD;
+ }
+ break;
+ case 0x28:
+ s->ubrdiv = val;
+ break;
+ case 0x2C:
+ s->udivslot = val;
+ break;
+ case 0x30:
+ s->uintp &= ~val;
+ s->uintsp &= ~val; /* TODO: does this really work in this way??? */
+ break;
+ case 0x34:
+ s->uintsp = val;
+ break;
+ case 0x38:
+ s->uintm = val;
+ break;
+ default:
+ hw_error("s5pc1xx.uart: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+ s5pc1xx_uart_update(s);
+}
+
+CPUReadMemoryFunc * const s5pc1xx_uart_readfn[] = {
+ s5pc1xx_uart_mm_read,
+ s5pc1xx_uart_mm_read,
+ s5pc1xx_uart_mm_read
+};
+
+CPUWriteMemoryFunc * const s5pc1xx_uart_writefn[] = {
+ s5pc1xx_uart_mm_write,
+ s5pc1xx_uart_mm_write,
+ s5pc1xx_uart_mm_write
+};
+
+static void s5pc1xx_uart_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ qemu_put_buffer(f, s->rx.queue, QUEUE_SIZE);
+ qemu_put_be32s(f, &s->rx.s);
+ qemu_put_be32s(f, &s->rx.t);
+ qemu_put_be32s(f, &s->rx.size);
+
+ qemu_put_be32s(f, &s->ulcon);
+ qemu_put_be32s(f, &s->ucon);
+ qemu_put_be32s(f, &s->ufcon);
+ qemu_put_be32s(f, &s->umcon);
+ qemu_put_be32s(f, &s->utrstat);
+ qemu_put_be32s(f, &s->uerstat);
+ qemu_put_be32s(f, &s->ufstat);
+ qemu_put_be32s(f, &s->umstat);
+ qemu_put_be32s(f, &s->utxh);
+ qemu_put_be32s(f, &s->urxh);
+ qemu_put_be32s(f, &s->ubrdiv);
+ qemu_put_be32s(f, &s->udivslot);
+ qemu_put_be32s(f, &s->uintp);
+ qemu_put_be32s(f, &s->uintsp);
+ qemu_put_be32s(f, &s->uintm);
+}
+
+static int s5pc1xx_uart_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_buffer(f, s->rx.queue, QUEUE_SIZE);
+ qemu_get_be32s(f, &s->rx.s);
+ qemu_get_be32s(f, &s->rx.t);
+ qemu_get_be32s(f, &s->rx.size);
+
+ qemu_get_be32s(f, &s->ulcon);
+ qemu_get_be32s(f, &s->ucon);
+ qemu_get_be32s(f, &s->ufcon);
+ qemu_get_be32s(f, &s->umcon);
+ qemu_get_be32s(f, &s->utrstat);
+ qemu_get_be32s(f, &s->uerstat);
+ qemu_get_be32s(f, &s->ufstat);
+ qemu_get_be32s(f, &s->umstat);
+ qemu_get_be32s(f, &s->utxh);
+ qemu_get_be32s(f, &s->urxh);
+ qemu_get_be32s(f, &s->ubrdiv);
+ qemu_get_be32s(f, &s->udivslot);
+ qemu_get_be32s(f, &s->uintp);
+ qemu_get_be32s(f, &s->uintsp);
+ qemu_get_be32s(f, &s->uintm);
+
+ return 0;
+}
+
+static int s5pc1xx_uart_can_receive(void *opaque)
+{
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+
+ return queue_empty_count(&s->rx);
+}
+
+static void s5pc1xx_uart_trigger_level(S5pc1xxUartState *s)
+{
+ /* TODO: fix this */
+ if (! queue_empty(&s->rx)) {
+ s->uintsp |= INT_RXD;
+ }
+}
+
+static void s5pc1xx_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+ S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
+ if (s->ufcon & 1) {
+ if (queue_empty_count(&s->rx) < size) {
+ for (i = 0; i < queue_empty_count(&s->rx); i++) {
+ queue_push(&s->rx, buf[i]);
+ }
+ s->uintp |= INT_ERROR;
+ s->utrstat |= TRSTATUS_DATA_READY;
+ } else {
+ for (i = 0; i < size; i++) {
+ queue_push(&s->rx, buf[i]);
+ }
+ s->utrstat |= TRSTATUS_DATA_READY;
+ }
+ s5pc1xx_uart_trigger_level(s);
+ } else {
+ s->urxh = buf[0];
+ s->uintsp |= INT_RXD;
+ s->utrstat |= TRSTATUS_DATA_READY;
+ }
+ s5pc1xx_uart_update(s);
+}
+
+static void s5pc1xx_uart_event(void *opaque, int event)
+{
+ /* TODO: implement this */
+}
+
+static void s5pc1xx_uart_reset(DeviceState *d)
+{
+ S5pc1xxUartState *s =
+ FROM_SYSBUS(S5pc1xxUartState, sysbus_from_qdev(d));
+
+ s->ulcon = 0;
+ s->ucon = 0;
+ s->ufcon = 0;
+ s->umcon = 0;
+ s->utrstat = 0x6;
+ s->uerstat = 0;
+ s->ufstat = 0;
+ s->umstat = 0;
+ s->ubrdiv = 0;
+ s->udivslot = 0;
+ s->uintp = 0;
+ s->uintsp = 0;
+ s->uintm = 0;
+ queue_reset(&s->rx);
+}
+
+DeviceState *s5pc1xx_uart_init(target_phys_addr_t base, int instance,
+ int queue_size, qemu_irq irq,
+ CharDriverState *chr)
+{
+ DeviceState *dev = qdev_create(NULL, "s5pc1xx.uart");
+ char str[] = "s5pc1xx.uart.00";
+
+ if (!chr) {
+ snprintf(str, strlen(str) + 1, "s5pc1xx.uart.%02d", instance % 100);
+ chr = qemu_chr_open(str, "null", NULL);
+ }
+ qdev_prop_set_chr(dev, "chr", chr);
+ qdev_prop_set_uint32(dev, "queue-size", queue_size);
+ qdev_prop_set_uint32(dev, "instance", instance);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+static int s5pc1xx_uart_init1(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxUartState *s = FROM_SYSBUS(S5pc1xxUartState, dev);
+
+ s5pc1xx_uart_reset(&s->busdev.qdev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ qemu_chr_add_handlers(s->chr, s5pc1xx_uart_can_receive,
+ s5pc1xx_uart_receive, s5pc1xx_uart_event, s);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_uart_readfn, s5pc1xx_uart_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_UART_REG_MEM_SIZE, iomemtype);
+
+ s5pc1xx_gpio_register_io_memory(GPIO_IDX_UART, s->instance,
+ s5pc1xx_uart_gpio_readfn,
+ s5pc1xx_uart_gpio_writefn, NULL, s);
+
+ register_savevm(&dev->qdev, "s5pc1xx.uart", s->instance, 1,
+ s5pc1xx_uart_save, s5pc1xx_uart_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_uart_info = {
+ .init = s5pc1xx_uart_init1,
+ .qdev.name = "s5pc1xx.uart",
+ .qdev.size = sizeof(S5pc1xxUartState),
+ .qdev.reset = s5pc1xx_uart_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("instance", S5pc1xxUartState, instance, 0),
+ DEFINE_PROP_UINT32("queue-size", S5pc1xxUartState, rx.size, 16),
+ DEFINE_PROP_CHR("chr", S5pc1xxUartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_uart_register(void)
+{
+ sysbus_register_withprop(&s5pc1xx_uart_info);
+}
+
+device_init(s5pc1xx_uart_register)
--- /dev/null
+/*
+ * S5PC1XX UART Emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ */
+
+#include "sysbus.h"
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "net.h"
+#include "irq.h"
+#include "hw.h"
+#include "s5pc1xx.h"
+
+
+/* Interrupts */
+#define USB_INT_MODEMIS (1 << 1) /* Mode Mismatch Interrupt */
+#define USB_INT_OTGINT (1 << 2) /* OTG Interrupt */
+#define USB_INT_SOF (1 << 3) /* Start of (micro) Frame */
+#define USB_INT_RXFLVL (1 << 4) /* RxFIFO Non-Empty */
+#define USB_INT_NPTXFEMP (1 << 5) /* Non-periodic TxFIFO Empty */
+#define USB_INT_GINNAKEFF (1 << 6) /* Global IN Non-periodic NAK Effective */
+#define USB_INT_GOUTNAKEFF (1 << 7) /* Global OUT NAK Effective */
+#define USB_INT_ERLYSUSP (1 << 10) /* Early Suspend */
+#define USB_INT_USBSUSP (1 << 11) /* USB Suspend */
+#define USB_INT_USBRST (1 << 12) /* USB Reset */
+#define USB_INT_ENUMDONE (1 << 13) /* Enumeration Done */
+#define USB_INT_ISOUTDROP (1 << 14) /* Isochronous OUT Packet Dropped */
+#define USB_INT_EOPF (1 << 15) /* End of Periodic Frame */
+#define USB_INT_IEPINT (1 << 18) /* IN Endpoints Interrupt */
+#define USB_INT_OEPINT (1 << 19) /* OUT Endpoints Interrupt */
+#define USB_INT_INCOMPLISOIN \
+ (1 << 20) /* Incomplete Isochronous IN Transfer */
+#define USB_INT_INCOMPLISOOUT \
+ (1 << 21) /* Incomplete Isochronous OUT Transfer */
+#define USB_INT_FETSUSP (1 << 22) /* Data Fetch Suspended */
+//#define USB_INT_PRTINT (1 << 24) /* Host Port Interrupt */
+//#define USB_INT_HCHINT (1 << 25) /* Host Channels Interrupt */
+#define USB_INT_PTXFEMP (1 << 26) /* Periodic TxFIFO Empty */
+#define USB_INT_CONIDSTSCHNG \
+ (1 << 28) /* Connector ID Status Change */
+#define USB_INT_DISCONINT (1 << 29) /* Disconnect Detected Interrupt */
+#define USB_INT_SESSREQINT (1 << 30) /* New Session Detected Interrupt */
+#define USB_INT_WKUPINT (1 << 31) /* Resume Interrupt */
+
+#define EP_INT_XFERCOMPL (1 << 0) /* Transfer complete */
+#define EP_INT_EPDISABLED (1 << 1) /* Endpoint disabled */
+#define EP_INT_AHBERR (1 << 2) /* AHB error */
+#define EP_INT_SETUP (1 << 3) /* [OUT] Setup phase done */
+#define EP_INT_TIMEOUT (1 << 3) /* [IN] Timeout */
+#define EP_INT_OUTTKNEPDIS (1 << 4) /* [OUT] Token Received When EP Disabled */
+#define EP_INT_INTKNFIFOEMP (1 << 4) /* [IN] Token Received When FIFO is Empty */
+#define EP_INT_STSPHSERCVD (1 << 5) /* [OUT] Status Phase Received For Control Write */
+#define EP_INT_INTTKNEPMIS (1 << 5) /* [IN] Token Received With EP Missmatch */
+#define EP_INT_BACK2BACK (1 << 6) /* [OUT] Back-to-Back SETUP Packets Receive */
+#define EP_INT_INEPNAKEFF (1 << 6) /* [IN] Endpoint NAK Effective */
+#define EP_INT_TXFEMP (1 << 7) /* Transmit FIFO Empty */
+#define EP_INT_OUTPKTERR (1 << 8) /* [OUT] Packet Error */
+#define EP_INT_TXFIFOUNDRN (1 << 8) /* [IN] FIFO Underrun */
+#define EP_INT_BNAINTR (1 << 9) /* Buffer not Available */
+
+
+#define OTG_EP_DIR_IN 0x80
+#define OTG_EP_DIR_OUT 0
+
+#define OTG_EP_ENABLE (1U << 31)
+#define OTG_EP_DISABLE (1 << 30)
+
+#define OTG_EP_COUNT 16
+
+
+typedef enum {
+ OTG_STATE_START = 0,
+ OTG_STATE_RESET,
+ OTG_STATE_SPEEDDETECT,
+ OTG_STATE_SETCONFIG_S,
+ OTG_STATE_SETCONFIG_W,
+ OTG_STATE_SETCONFIG_D,
+ OTG_STATE_SETIFACE_S,
+ OTG_STATE_SETIFACE_W,
+ OTG_STATE_SETIFACE_D,
+ OTG_STATE_OPERATIONAL
+} OtgLogicalState;
+
+typedef struct S5pc1xxUsbOtgEndPoint {
+ uint32_t n;
+ uint32_t ctrl;
+ uint32_t interrupt;
+ uint32_t transfer_size;
+ uint32_t dma_addr;
+ uint32_t dma_buf;
+ uint32_t in_fifo_size;
+
+ uint8_t dir;
+
+ struct S5pc1xxUsbOtgState *parent;
+} S5pc1xxUsbOtgEndPoint;
+
+typedef struct S5pc1xxUsbOtgState {
+ SysBusDevice busdev;
+
+ struct S5pc1xxPhyState {
+ uint32_t power;
+ uint32_t clock;
+ uint32_t reset;
+ uint32_t tune0;
+ uint32_t tune1;
+ } phy;
+
+ uint32_t gotg_ctl;
+ uint32_t gotg_int;
+ uint32_t gahb_cfg;
+ uint32_t gusb_cfg;
+ uint32_t grst_ctl;
+ uint32_t gint_sts;
+ uint32_t gint_msk;
+ uint32_t grx_stsr;
+ uint32_t grx_stsp;
+ uint32_t grx_fsiz;
+ uint32_t gnptx_fsiz;
+ uint32_t gnptx_sts;
+ uint32_t hnptx_fsiz;
+ uint32_t daint_sts;
+ uint32_t daint_msk;
+ uint32_t diep_msk;
+ uint32_t doep_msk;
+
+ S5pc1xxUsbOtgEndPoint ep_in[OTG_EP_COUNT];
+ S5pc1xxUsbOtgEndPoint ep_out[OTG_EP_COUNT];
+
+ OtgLogicalState state;
+
+ NICState *nic;
+ NICConf conf;
+ qemu_irq irq;
+ uint8_t buf[1600];
+ uint32_t buf_size;
+ uint8_t buf_full;
+} S5pc1xxUsbOtgState;
+
+
+static const uint8_t otg_setup_packet[] = {
+ 0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00
+};
+static const uint8_t otg_setup_iface[] = {
+ 0x01, 0x0B, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00
+};
+static const uint8_t otg_setup_config[] = {
+ 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void s5pc1xx_usb_otg_update_irq(S5pc1xxUsbOtgState *s)
+{
+ if (s->gint_sts & s->gint_msk) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void s5pc1xx_usb_otg_initial_reset(DeviceState *d)
+{
+ S5pc1xxUsbOtgState *s =
+ FROM_SYSBUS(S5pc1xxUsbOtgState, sysbus_from_qdev(d));
+ int i;
+
+ s->phy.power = 0x000001F9;
+ s->phy.clock = 0x00000000;
+ s->phy.reset = 0x00000009; /* TODO: I believe it should be 0 */
+ s->phy.tune0 = 0x000919B3;
+ s->phy.tune1 = 0x000919B3;
+
+ s->gotg_ctl = 0x00010000;
+ s->gotg_int = 0x00000000;
+ s->gahb_cfg = 0x00000000;
+ s->gusb_cfg = 0x00001408;
+ s->grst_ctl = 0x80000000;
+ s->gint_sts = 0x04000020;
+ s->gint_msk = 0x00000000;
+
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ s->ep_in[i].parent = s;
+ s->ep_in[i].dir = OTG_EP_DIR_IN;
+ s->ep_in[i].n = i;
+ s->ep_out[i].parent = s;
+ s->ep_out[i].dir = OTG_EP_DIR_OUT;
+ s->ep_out[i].n = i;
+ }
+
+ s->state = OTG_STATE_START;
+ s->buf_full = 0;
+}
+
+static void s5pc1xx_usb_otg_reset(S5pc1xxUsbOtgState *s)
+{
+ s5pc1xx_usb_otg_initial_reset(&s->busdev.qdev);
+ s->state = OTG_STATE_RESET;
+ s->gotg_ctl += 0x000C0000;
+ s->gint_sts |= USB_INT_USBRST;
+}
+
+static uint32_t s5pc1xx_usb_otg_phy_mm_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ return s->phy.power;
+ case 0x04:
+ return s->phy.clock;
+ case 0x08:
+ return s->phy.reset;
+ case 0x20:
+ return s->phy.tune0;
+ case 0x24:
+ return s->phy.tune1;
+ default:
+ hw_error("s5pc1xx.usb_otg: bad read offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_usb_otg_phy_mm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+ switch (offset) {
+ case 0x00:
+ s->phy.power = val;
+ break;
+ case 0x04:
+ s->phy.clock = val;
+ break;
+ case 0x08:
+ /* TODO: actually reset USB OTG */
+ if (val & 0x1f) {
+ s5pc1xx_usb_otg_reset(s);
+ s->gint_sts |= USB_INT_USBRST;
+ s5pc1xx_usb_otg_update_irq(s);
+ }
+ break;
+ case 0x20:
+ s->phy.tune0 = val;
+ break;
+ case 0x24:
+ s->phy.tune1 = val;
+ break;
+ default:
+ hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+ offset);
+ }
+}
+
+static void s5pc1xx_usb_otg_ep_update_irq(S5pc1xxUsbOtgEndPoint *s)
+{
+ if (s->interrupt) {
+ if (s->dir == OTG_EP_DIR_IN) {
+ s->parent->daint_sts |= (1 << s->n);
+ if (s->parent->daint_sts & s->parent->daint_msk & 0xffff) {
+ s->parent->gint_sts |= USB_INT_IEPINT;
+ } else {
+ s->parent->gint_sts &= ~USB_INT_IEPINT;
+ }
+ } else {
+ s->parent->daint_sts |= (1 << (s->n + 16));
+ if (s->parent->daint_sts & s->parent->daint_msk & 0xffff0000) {
+ s->parent->gint_sts |= USB_INT_OEPINT;
+ } else {
+ s->parent->gint_sts &= ~USB_INT_OEPINT;
+ }
+ }
+ } else {
+ if (s->dir == OTG_EP_DIR_IN) {
+ s->parent->daint_sts &= ~(1 << s->n);
+ if (s->parent->daint_sts & s->parent->daint_msk & 0xffff) {
+ s->parent->gint_sts |= USB_INT_IEPINT;
+ } else {
+ s->parent->gint_sts &= ~USB_INT_IEPINT;
+ }
+ } else {
+ s->parent->daint_sts &= ~(1 << (s->n + 16));
+ if (s->parent->daint_sts & s->parent->daint_msk & 0xffff0000) {
+ s->parent->gint_sts |= USB_INT_OEPINT;
+ } else {
+ s->parent->gint_sts &= ~USB_INT_OEPINT;
+ }
+ }
+ }
+ s5pc1xx_usb_otg_update_irq(s->parent);
+}
+
+static void s5pc1xx_usb_otg_act(S5pc1xxUsbOtgState *s)
+{
+ switch (s->state) {
+ case OTG_STATE_START:
+ case OTG_STATE_RESET:
+ case OTG_STATE_SPEEDDETECT:
+ break;
+ case OTG_STATE_SETCONFIG_S:
+ if (s->ep_out[0].ctrl & OTG_EP_ENABLE) {
+ s->state = OTG_STATE_SETCONFIG_W;
+ cpu_physical_memory_write(s->ep_out[0].dma_addr,
+ otg_setup_config, 8);
+ s->ep_out[0].ctrl &= ~OTG_EP_ENABLE;
+ s->ep_out[0].interrupt |= EP_INT_SETUP|EP_INT_XFERCOMPL;
+ s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[0]);
+ }
+ break;
+ case OTG_STATE_SETIFACE_S:
+ if (s->ep_out[0].ctrl & OTG_EP_ENABLE) {
+ s->state = OTG_STATE_SETIFACE_W;
+ cpu_physical_memory_write(s->ep_out[0].dma_addr,
+ otg_setup_iface, 8);
+ s->ep_out[0].ctrl &= ~OTG_EP_ENABLE;
+ s->ep_out[0].interrupt |= EP_INT_SETUP|EP_INT_XFERCOMPL;
+ s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[0]);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void s5pc1xx_usb_otg_data_tx(S5pc1xxUsbOtgEndPoint *s)
+{
+ uint8_t buf[1600];
+ uint32_t size = s->transfer_size & 0x7ffff;
+
+ cpu_physical_memory_read(s->dma_addr, buf, size);
+ qemu_send_packet(&s->parent->nic->nc, buf, size);
+ s->interrupt |= EP_INT_XFERCOMPL|EP_INT_TXFEMP;
+ s5pc1xx_usb_otg_ep_update_irq(s);
+}
+
+static void s5pc1xx_usb_otg_data_rx(S5pc1xxUsbOtgEndPoint *s)
+{
+ uint32_t size = s->parent->buf_size;
+
+ if (s->parent->buf_size > (s->transfer_size & 0x7ffff)) {
+ s->parent->buf_full = 0;
+ /* Packet dropped */
+ return ;
+ }
+ cpu_physical_memory_write(s->dma_addr, s->parent->buf, size);
+ s->dma_buf = s->dma_addr + size;
+ s->transfer_size -= size;
+ s->ctrl &= ~OTG_EP_ENABLE;
+ s->interrupt |= EP_INT_XFERCOMPL;
+ s->parent->buf_full = 0;
+ s5pc1xx_usb_otg_ep_update_irq(s);
+}
+
+static uint32_t s5pc1xx_usb_otg_ep_read(S5pc1xxUsbOtgEndPoint *s,
+ target_phys_addr_t addr)
+{
+ switch (addr) {
+ case 0x00:
+ return s->ctrl;
+ case 0x08:
+ return s->interrupt;
+ case 0x10:
+ return s->transfer_size;
+ case 0x14:
+ return s->dma_addr;
+ case 0x1C:
+ return s->dma_buf;
+ default:
+ hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+ addr);
+ }
+}
+
+static uint32_t s5pc1xx_usb_otg_ep_write(S5pc1xxUsbOtgEndPoint *s,
+ target_phys_addr_t addr, uint32_t val)
+{
+ switch (addr) {
+ case 0x00:
+ if ((val & OTG_EP_DISABLE) && (s->ctrl & OTG_EP_ENABLE)) {
+ s->ctrl &= ~OTG_EP_ENABLE;
+ val &= ~OTG_EP_DISABLE;
+ s->interrupt |= EP_INT_EPDISABLED;
+ s5pc1xx_usb_otg_ep_update_irq(s);
+ }
+ val &= ~OTG_EP_DISABLE;
+ if (val & OTG_EP_ENABLE) {
+ s5pc1xx_usb_otg_act(s->parent);
+ if (s->n == 0 && s->dir == OTG_EP_DIR_IN) {
+ s->interrupt |= EP_INT_XFERCOMPL|EP_INT_TXFEMP;
+ if (s->parent->state == OTG_STATE_SETCONFIG_W) {
+ s->parent->state = OTG_STATE_SETIFACE_S;
+ } else if (s->parent->state == OTG_STATE_SETIFACE_W) {
+ s->parent->state = OTG_STATE_OPERATIONAL;
+ }
+ s5pc1xx_usb_otg_ep_update_irq(s);
+ }
+ if (s->n != 0 && s->dir == OTG_EP_DIR_IN) {
+ s5pc1xx_usb_otg_data_tx(s);
+ val &= ~OTG_EP_ENABLE;
+ }
+ if (s->n != 0 && s->dir == OTG_EP_DIR_OUT &&
+ s->parent->buf_full == 1) {
+ s5pc1xx_usb_otg_data_rx(s);
+ val &= ~OTG_EP_ENABLE;
+ }
+ }
+ val &= ~(0xC << 24); /* TODO: handle NAK? */
+ s->ctrl = val;
+ /* TODO: handle control */
+ break;
+ case 0x08:
+ s->interrupt &= ~val;
+ s5pc1xx_usb_otg_ep_update_irq(s);
+ break;
+ case 0x10:
+ s->transfer_size = val;
+ break;
+ case 0x14:
+ s->dma_addr = val;
+ break;
+ default:
+ hw_error("s5pc1xx.usb_otg: bad write offset 0x" TARGET_FMT_plx "\n",
+ addr);
+ }
+ return 0;
+}
+
+static uint32_t s5pc1xx_usb_otg_read(void *opaque, target_phys_addr_t addr)
+{
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+ if (addr >= 0x100000 && addr < 0x100028) {
+ return s5pc1xx_usb_otg_phy_mm_read(opaque, addr - 0x100000);
+ }
+
+ switch (addr) {
+ case 0x00:
+ return s->gotg_ctl;
+ case 0x04:
+ return s->gotg_int;
+ case 0x08:
+ return s->gahb_cfg;
+ case 0x0C:
+ return s->gusb_cfg;
+ case 0x10:
+ return s->grst_ctl;
+ case 0x14:
+ return s->gint_sts;
+ case 0x18:
+ return s->gint_msk;
+ case 0x1C:
+ return s->grx_stsr;
+ case 0x20:
+ return s->grx_stsp;
+ case 0x24:
+ return s->grx_fsiz;
+ case 0x28:
+ return s->gnptx_fsiz;
+ case 0x2C:
+ return s->gnptx_sts;
+ case 0x30:
+ return s->hnptx_fsiz;
+ case 0x100 ... 0x13C:
+ return s->ep_in[(addr - 0x100) >> 2].in_fifo_size;
+ case 0x810:
+ return s->diep_msk;
+ case 0x814:
+ return s->doep_msk;
+ case 0x818:
+ return s->daint_sts;
+ case 0x81C:
+ return s->daint_msk;
+ case 0x900 ... 0xAFC:
+ addr -= 0x900;
+ return s5pc1xx_usb_otg_ep_read(&s->ep_in[addr >> 5], addr & 0x1f);
+ case 0xB00 ... 0xCFC:
+ addr -= 0xB00;
+ return s5pc1xx_usb_otg_ep_read(&s->ep_out[addr >> 5], addr & 0x1f);
+ }
+ return 0;
+}
+
+static void s5pc1xx_usb_otg_write(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ int i;
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+
+ if (addr >= 0x100000 && addr < 0x100028) {
+ s5pc1xx_usb_otg_phy_mm_write(opaque, addr - 0x100000, val);
+ return;
+ }
+
+ switch (addr) {
+ case 0x00:
+ s->gotg_ctl = val;
+ break;
+ case 0x04:
+ s->gotg_int &= ~val;
+ s5pc1xx_usb_otg_update_irq(s);
+ break;
+ case 0x08:
+ s->gahb_cfg = val;
+ break;
+ case 0x0C:
+ s->gusb_cfg = val;
+ break;
+ case 0x10:
+ if (val & 1) {
+ s5pc1xx_usb_otg_reset(s);
+ s->gint_sts |= USB_INT_USBRST;
+ s->state = OTG_STATE_RESET;
+ s5pc1xx_usb_otg_update_irq(s);
+ } else if (val & 0x0f) {
+ s->gint_sts |= USB_INT_USBRST;
+ s->state = OTG_STATE_RESET;
+ s5pc1xx_usb_otg_update_irq(s);
+ }
+ s->grst_ctl = val & (~0x3f);
+ s5pc1xx_usb_otg_update_irq(s);
+ break;
+ case 0x14:
+ val &= ~(7 << 24);
+ val &= ~(3 << 18);
+ val &= ~(0xf << 4);
+ val &= ~0x5;
+ s->gint_sts &= ~val;
+ if (val == 0x1000) {
+ s->gint_sts |= USB_INT_ENUMDONE;
+ s->state = OTG_STATE_SPEEDDETECT;
+ s5pc1xx_usb_otg_act(s);
+ }
+ if (val == USB_INT_ENUMDONE) {
+ s->state = OTG_STATE_SETCONFIG_S;
+ s5pc1xx_usb_otg_act(s);
+ }
+ s5pc1xx_usb_otg_update_irq(s);
+ break;
+ case 0x18:
+ s->gint_msk = val;
+ break;
+ case 0x24:
+ s->grx_fsiz = val;
+ break;
+ case 0x28:
+ s->gnptx_fsiz = val;
+ break;
+ case 0x30:
+ s->hnptx_fsiz = val;
+ break;
+ case 0x100 ... 0x13C:
+ s->ep_in[(addr - 0x100 ) >> 2].in_fifo_size = val;
+ break;
+ case 0x810:
+ s->diep_msk = val;
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ s5pc1xx_usb_otg_ep_update_irq(&s->ep_in[i]);
+ }
+ break;
+ case 0x814:
+ s->doep_msk = val;
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ s5pc1xx_usb_otg_ep_update_irq(&s->ep_out[i]);
+ }
+ break;
+ case 0x81C:
+ s->daint_msk = val;
+ if (s->daint_sts & 0xffff & s->daint_msk) {
+ s->gint_sts |= USB_INT_IEPINT;
+ } else {
+ s->gint_sts &= ~USB_INT_IEPINT;
+ }
+ if ((s->daint_sts & s->daint_msk) >> 16) {
+ s->gint_sts |= USB_INT_OEPINT;
+ } else {
+ s->gint_sts &= ~USB_INT_OEPINT;
+ }
+ s5pc1xx_usb_otg_update_irq(s);
+ break;
+ case 0x900 ... 0xAFC:
+ addr -= 0x900;
+ s5pc1xx_usb_otg_ep_write(&s->ep_in[addr >> 5], addr & 0x1f, val);
+ break;
+ case 0xB00 ... 0xCFC:
+ addr -= 0xB00;
+ s5pc1xx_usb_otg_ep_write(&s->ep_out[addr >> 5], addr & 0x1f, val);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_usb_otg_readfn[] = {
+ s5pc1xx_usb_otg_read,
+ s5pc1xx_usb_otg_read,
+ s5pc1xx_usb_otg_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_usb_otg_writefn[] = {
+ s5pc1xx_usb_otg_write,
+ s5pc1xx_usb_otg_write,
+ s5pc1xx_usb_otg_write
+};
+
+static void s5pc1xx_usb_otg_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->phy.power);
+ qemu_put_be32s(f, &s->phy.clock);
+ qemu_put_be32s(f, &s->phy.reset);
+ qemu_put_be32s(f, &s->phy.tune0);
+ qemu_put_be32s(f, &s->phy.tune1);
+
+ qemu_put_be32s(f, &s->gotg_ctl);
+ qemu_put_be32s(f, &s->gotg_int);
+ qemu_put_be32s(f, &s->gahb_cfg);
+ qemu_put_be32s(f, &s->gusb_cfg);
+ qemu_put_be32s(f, &s->grst_ctl);
+ qemu_put_be32s(f, &s->gint_sts);
+ qemu_put_be32s(f, &s->gint_msk);
+ qemu_put_be32s(f, &s->grx_stsr);
+ qemu_put_be32s(f, &s->grx_stsp);
+ qemu_put_be32s(f, &s->grx_fsiz);
+ qemu_put_be32s(f, &s->gnptx_fsiz);
+ qemu_put_be32s(f, &s->gnptx_sts);
+ qemu_put_be32s(f, &s->hnptx_fsiz);
+ qemu_put_be32s(f, &s->daint_sts);
+ qemu_put_be32s(f, &s->daint_msk);
+ qemu_put_be32s(f, &s->diep_msk);
+ qemu_put_be32s(f, &s->doep_msk);
+ qemu_put_byte (f, (uint8_t) s->state);
+
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ qemu_put_be32s(f, &s->ep_in[i].ctrl);
+ qemu_put_be32s(f, &s->ep_in[i].interrupt);
+ qemu_put_be32s(f, &s->ep_in[i].transfer_size);
+ qemu_put_be32s(f, &s->ep_in[i].dma_addr);
+ qemu_put_be32s(f, &s->ep_in[i].dma_buf);
+ qemu_put_be32s(f, &s->ep_in[i].in_fifo_size);
+
+ qemu_put_be32s(f, &s->ep_out[i].ctrl);
+ qemu_put_be32s(f, &s->ep_out[i].interrupt);
+ qemu_put_be32s(f, &s->ep_out[i].transfer_size);
+ qemu_put_be32s(f, &s->ep_out[i].dma_addr);
+ qemu_put_be32s(f, &s->ep_out[i].dma_buf);
+ qemu_put_be32s(f, &s->ep_out[i].in_fifo_size);
+ }
+ /* FIXME: must the structure below be saved?
+ * NICState *nic; */
+ qemu_put_be32s (f, &s->buf_size);
+ qemu_put_buffer(f, s->buf, s->buf_size);
+ qemu_put_8s (f, &s->buf_full);
+}
+
+static int s5pc1xx_usb_otg_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxUsbOtgState *s = (S5pc1xxUsbOtgState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s(f, &s->phy.power);
+ qemu_get_be32s(f, &s->phy.clock);
+ qemu_get_be32s(f, &s->phy.reset);
+ qemu_get_be32s(f, &s->phy.tune0);
+ qemu_get_be32s(f, &s->phy.tune1);
+
+ qemu_get_be32s(f, &s->gotg_ctl);
+ qemu_get_be32s(f, &s->gotg_int);
+ qemu_get_be32s(f, &s->gahb_cfg);
+ qemu_get_be32s(f, &s->gusb_cfg);
+ qemu_get_be32s(f, &s->grst_ctl);
+ qemu_get_be32s(f, &s->gint_sts);
+ qemu_get_be32s(f, &s->gint_msk);
+ qemu_get_be32s(f, &s->grx_stsr);
+ qemu_get_be32s(f, &s->grx_stsp);
+ qemu_get_be32s(f, &s->grx_fsiz);
+ qemu_get_be32s(f, &s->gnptx_fsiz);
+ qemu_get_be32s(f, &s->gnptx_sts);
+ qemu_get_be32s(f, &s->hnptx_fsiz);
+ qemu_get_be32s(f, &s->daint_sts);
+ qemu_get_be32s(f, &s->daint_msk);
+ qemu_get_be32s(f, &s->diep_msk);
+ qemu_get_be32s(f, &s->doep_msk);
+ s->state = qemu_get_byte(f);
+
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ qemu_get_be32s(f, &s->ep_in[i].ctrl);
+ qemu_get_be32s(f, &s->ep_in[i].interrupt);
+ qemu_get_be32s(f, &s->ep_in[i].transfer_size);
+ qemu_get_be32s(f, &s->ep_in[i].dma_addr);
+ qemu_get_be32s(f, &s->ep_in[i].dma_buf);
+ qemu_get_be32s(f, &s->ep_in[i].in_fifo_size);
+
+ qemu_get_be32s(f, &s->ep_out[i].ctrl);
+ qemu_get_be32s(f, &s->ep_out[i].interrupt);
+ qemu_get_be32s(f, &s->ep_out[i].transfer_size);
+ qemu_get_be32s(f, &s->ep_out[i].dma_addr);
+ qemu_get_be32s(f, &s->ep_out[i].dma_buf);
+ qemu_get_be32s(f, &s->ep_out[i].in_fifo_size);
+ }
+ /* FIXME: NICState *nic; */
+ qemu_get_be32s (f, &s->buf_size);
+ qemu_get_buffer(f, s->buf, s->buf_size);
+ qemu_get_8s (f, &s->buf_full);
+
+ return 0;
+}
+
+static int s5pc1xx_usb_otg_can_receive(VLANClientState *nc)
+{
+ S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+
+ return !s->buf_full;
+}
+
+static ssize_t s5pc1xx_usb_otg_receive(VLANClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+ int i;
+
+ s->buf_full = 1;
+ s->buf_size = size;
+ memcpy(s->buf, buf, size);
+ for (i = 0; i < OTG_EP_COUNT; i++) {
+ if (s->ep_out[i].ctrl & OTG_EP_ENABLE) {
+ s5pc1xx_usb_otg_data_rx(&s->ep_out[i]);
+ break;
+ }
+ }
+ return size;
+}
+
+static void s5pc1xx_usb_otg_cleanup(VLANClientState *nc)
+{
+ S5pc1xxUsbOtgState *s = (DO_UPCAST(NICState, nc, nc))->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_s5pc1xx_usb_otg_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = s5pc1xx_usb_otg_can_receive,
+ .receive = s5pc1xx_usb_otg_receive,
+ .cleanup = s5pc1xx_usb_otg_cleanup,
+};
+
+static int s5pc1xx_usb_otg_init1(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxUsbOtgState *s = FROM_SYSBUS(S5pc1xxUsbOtgState, dev);
+
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_usb_otg_readfn,
+ s5pc1xx_usb_otg_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x100030, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s5pc1xx_usb_otg_initial_reset(&s->busdev.qdev);
+
+ s->nic = qemu_new_nic(&net_s5pc1xx_usb_otg_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+ register_savevm(&dev->qdev, "s5pc1xx.usb.otg", -1, 1,
+ s5pc1xx_usb_otg_save, s5pc1xx_usb_otg_load, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc1xx_usb_otg_info = {
+ .init = s5pc1xx_usb_otg_init1,
+ .qdev.name = "s5pc1xx.usb.otg",
+ .qdev.size = sizeof(S5pc1xxUsbOtgState),
+ .qdev.reset = s5pc1xx_usb_otg_initial_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(S5pc1xxUsbOtgState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void s5pc1xx_usb_otg_register_devices(void)
+{
+ sysbus_register_withprop(&s5pc1xx_usb_otg_info);
+}
+
+/* Legacy helper function. Should go away when machine config files are
+ implemented. */
+void s5pc1xx_usb_otg_init(NICInfo *nd, target_phys_addr_t base, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "s5pc1xx-usb-otg");
+ dev = qdev_create(NULL, "s5pc1xx.usb.otg");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+}
+
+device_init(s5pc1xx_usb_otg_register_devices)
--- /dev/null
+/*
+ * Watchdog Timer for Samsung S5C110-based board emulation
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
+ */
+
+#include "s5pc1xx.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "sysemu.h"
+
+
+#define WTCON 0x00 /* R/W Watchdog Timer Control Register */
+#define WTDAT 0x04 /* R/W Watchdog Timer Data Register */
+#define WTCNT 0x08 /* R/W Watchdog Timer Count Register */
+#define WTCLRINT 0x0C /* W Watchdog Timer Interrupt Clear Register */
+
+#define PRESCALER_SHIFT (8)
+#define WDT_EN (1 << 5)
+#define CLK_SEL_SHIFT (3)
+#define INT_EN (1 << 2)
+#define RESET_EN (1 << 0)
+
+#define S5PC1XX_WDT_REG_MEM_SIZE 0x14
+
+
+typedef struct S5pc1xxWDTState {
+ SysBusDevice busdev;
+
+ qemu_irq irq;
+ uint32_t regs[WTCNT + 1];
+
+ QEMUTimer *wdt_timer;
+ uint32_t freq_out;
+ uint64_t ticnto_last_tick;
+} S5pc1xxWDTState;
+
+
+/* Timer step */
+static void s5pc1xx_wdt_tick(void *opaque)
+{
+ S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+ uint64_t next_wdt_time;
+
+ if (s->regs[WTCON] & WDT_EN) {
+ if (s->regs[WTCON] & INT_EN)
+ qemu_irq_raise(s->irq);
+
+ if (s->regs[WTCON] & RESET_EN) {
+ qemu_system_reset_request();
+ }
+
+ s->ticnto_last_tick = qemu_get_clock(vm_clock);
+ next_wdt_time = s->ticnto_last_tick +
+ muldiv64(s->regs[WTDAT], get_ticks_per_sec(), s->freq_out);
+ qemu_mod_timer(s->wdt_timer, next_wdt_time);
+ } else {
+ s->ticnto_last_tick = 0;
+ qemu_del_timer(s->wdt_timer);
+ }
+}
+
+/* Perform timer step, update frequency and compute next time for update */
+static void s5pc1xx_wdt_update(S5pc1xxWDTState *s)
+{
+ short div_fac;
+ short prescaler;
+ S5pc1xxClk clk;
+
+ clk = s5pc1xx_findclk("pclk_66");
+ div_fac = 16 << (s->regs[WTCON] >> CLK_SEL_SHIFT & 0x3);
+ prescaler = s->regs[WTCON] >> PRESCALER_SHIFT & 0xff;
+
+ s->freq_out = s5pc1xx_clk_getrate(clk) / (prescaler + 1) / div_fac;
+
+ if (!s->freq_out)
+ hw_error("s5pc1xx.wdt: timer update input frequency is zero\n");
+}
+
+/* WDT read */
+static uint32_t s5pc1xx_wdt_read(void *opaque, target_phys_addr_t offset)
+{
+ S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+
+ /* check if offset is correct, WTCLRINT is not for read */
+ if (offset <= WTCNT) {
+ if (offset == WTCNT) {
+ if (s->freq_out && s->ticnto_last_tick && s->regs[WTDAT]) {
+ s->regs[WTCNT] = s->regs[WTDAT] -
+ muldiv64(qemu_get_clock(vm_clock) - s->ticnto_last_tick,
+ s->freq_out, get_ticks_per_sec()) %
+ s->regs[WTDAT];
+ } else
+ s->regs[WTCNT] = 0;
+ }
+ return s->regs[offset];
+ } else {
+ hw_error("s5pc1xx.wdt: bad read offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+/* WDT write */
+static void s5pc1xx_wdt_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+
+ switch (offset) {
+ case WTCON:
+ if ((value & WDT_EN) > (s->regs[WTCON] & WDT_EN)) {
+ s->regs[WTCON] = value;
+ s5pc1xx_wdt_update(s);
+ s5pc1xx_wdt_tick(s);
+ }
+ s->regs[WTCON] = value;
+ s5pc1xx_wdt_update(s);
+ break;
+ case WTDAT:
+ case WTCNT:
+ s->regs[offset] = value;
+ break;
+ case WTCLRINT:
+ qemu_irq_lower(s->irq);
+ break;
+ default:
+ hw_error("s5pc1xx.wdt: bad write offset " TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const s5pc1xx_wdt_readfn[] = {
+ s5pc1xx_wdt_read,
+ s5pc1xx_wdt_read,
+ s5pc1xx_wdt_read
+};
+
+static CPUWriteMemoryFunc * const s5pc1xx_wdt_writefn[] = {
+ s5pc1xx_wdt_write,
+ s5pc1xx_wdt_write,
+ s5pc1xx_wdt_write
+};
+
+static void s5pc1xx_wdt_save(QEMUFile *f, void *opaque)
+{
+ S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+ int i;
+
+ for (i = 0; i <= WTCNT; i++) {
+ qemu_put_be32s(f, &s->regs[i]);
+ }
+
+ qemu_put_be64s(f, &s->ticnto_last_tick);
+ qemu_put_be32s(f, &s->freq_out);
+ qemu_put_timer(f, s->wdt_timer);
+}
+
+static int s5pc1xx_wdt_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S5pc1xxWDTState *s = (S5pc1xxWDTState *)opaque;
+ int i;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i <= WTCNT; i++) {
+ qemu_get_be32s(f, &s->regs[i]);
+ }
+
+ qemu_get_be64s(f, &s->ticnto_last_tick);
+ qemu_get_be32s(f, &s->freq_out);
+ qemu_get_timer(f, s->wdt_timer);
+
+ return 0;
+}
+
+/* WDT init */
+static int s5pc1xx_wdt_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ S5pc1xxWDTState *s = FROM_SYSBUS(S5pc1xxWDTState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype =
+ cpu_register_io_memory(s5pc1xx_wdt_readfn, s5pc1xx_wdt_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, S5PC1XX_WDT_REG_MEM_SIZE, iomemtype);
+
+ s->wdt_timer = qemu_new_timer(vm_clock, s5pc1xx_wdt_tick, s);
+
+ s->regs[WTDAT] = 0x00008000;
+ s->regs[WTCNT] = 0x00008000;
+ /* initially WDT is stopped */
+ s5pc1xx_wdt_write(s, WTCON, 0x00008001);
+
+ register_savevm(&dev->qdev, "s5pc1xx.wdt", -1, 1,
+ s5pc1xx_wdt_save, s5pc1xx_wdt_load, s);
+
+ return 0;
+}
+
+static void s5pc1xx_wdt_register_devices(void)
+{
+ sysbus_register_dev("s5pc1xx.wdt", sizeof(S5pc1xxWDTState),
+ s5pc1xx_wdt_init);
+}
+
+device_init(s5pc1xx_wdt_register_devices)
--- /dev/null
+/*
+ * SMB380 Sensor Emulation
+ *
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#else
+#include <windows.h>
+#include <winsock2.h>
+#include <sys/types.h>
+#endif
+
+#include "i2c-addressable.h"
+
+//#define DEBUG
+
+typedef struct SensorState {
+ I2CAddressableState i2c_addressable;
+ char data[7];
+
+ int16_t x;
+ int16_t y;
+ int16_t z;
+ int idx_out, req_out;
+}SensorState;
+
+SensorState glob_accel_state;
+
+#define BITS_PER_BYTE 8
+#define SMB380_ACCEL_BITS 10
+
+int sensor_update(uint16_t x, uint16_t y, uint16_t z)
+{
+ glob_accel_state.x = x;
+ glob_accel_state.y = y;
+ glob_accel_state.z = z;
+
+ return 0;
+}
+
+static int sensor_xyz_set(struct SensorState *s, uint16_t x, uint16_t y, uint16_t z)
+{
+ /* remains right 10bit */
+
+ x <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ x >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+ /* data[1] : 2 ~ 10 (bit)
+ * data[0] : 0 ~ 1 (bit) */
+
+ s->data[0] = (0x3 & x) << 6;
+ s->data[1] = x >> 2;
+ s->data[2] = (0x3 & y) << 6;
+ s->data[3] = y >> 2;
+ s->data[4] = (0x3 & z) << 6;
+ s->data[5] = z >> 2;
+
+ return 0;
+}
+
+static void smb380_reset(struct SensorState *s)
+{
+ s->idx_out = 0;
+
+ glob_accel_state.x = 0;
+ glob_accel_state.y = -256;
+ glob_accel_state.z = 0;
+
+ s->data[0] = 0;
+ s->data[1] = 0;
+ s->data[2] = 0;
+ s->data[3] = 0;
+ s->data[4] = 0;
+ s->data[5] = 0;
+ s->idx_out = 0;
+
+ return ;
+}
+
+static uint8_t smb380_read(void *opaque, uint32_t address, uint8_t offset)
+{
+ SensorState *s = (SensorState *)opaque;
+ int index = 0;
+
+ index = s->idx_out;
+
+#ifdef DEBUG
+ printf("smb380_read IDX = %d, Data=%d\n", index, s->data[index]);
+#endif
+
+ s->idx_out ++;
+
+ return s->data[index];
+}
+
+#ifdef DEBUG
+static int print_hex(char *data, int len)
+{
+ return 0;
+}
+#endif
+
+static int parse_val(char *buff, unsigned char find_data, char *parsebuff)
+{
+
+ int vvvi = 0;
+ while (1) {
+ if (vvvi > 40) {
+ return -1;
+ }
+ if (buff[vvvi] == find_data) {
+
+ vvvi++;
+ strncpy(parsebuff, buff, vvvi);
+ return vvvi;
+ }
+ vvvi++;
+ }
+
+ return 0;
+}
+
+#define SENSOR_BUF 16
+#define SENSOR_VALUE_BUF 56
+static int get_from_ide(struct SensorState *accel_state)
+{
+ int client_socket;
+ struct sockaddr_in server_addr;
+ char buff[SENSOR_VALUE_BUF + 1];
+ char buff2[SENSOR_VALUE_BUF + 1];
+ char tmpbuf[SENSOR_VALUE_BUF + 1];
+ int len = 0, len1 = 0;
+ int result = 0;
+ const char *command0 = "readSensor()\n";
+ const char *command1 = "accelerometer\n";
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
+ return -1;
+
+ client_socket = socket(AF_INET, SOCK_STREAM, 0);
+#else
+ client_socket = socket(PF_INET, SOCK_STREAM, 0);
+#endif
+ if (client_socket == -1) {
+ return -1;
+ }
+
+ memset(&server_addr, 0, sizeof( server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(8010);
+ server_addr.sin_addr.s_addr= inet_addr("127.0.0.1");
+ if( -1 == connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) {
+ close( client_socket);
+ return -1;
+ }
+
+ if(read (client_socket, buff, SENSOR_BUF) < 0) {
+ close(client_socket);
+ return -1;
+ }
+
+ if(1) {
+ if(write(client_socket, command0, strlen(command0)) < 0) {
+ close(client_socket);
+ return -1;
+ }
+ if(write(client_socket, command1, strlen(command1)) < 0) {
+ close(client_socket);
+ return -1;
+ }
+
+ memset(buff, '\0', sizeof(buff));
+ memset(buff2, '\0', sizeof(buff2));
+
+ result = read(client_socket, buff2, SENSOR_VALUE_BUF);
+ if (result <= (SENSOR_VALUE_BUF)/2) {
+ close( client_socket);
+ return -1;
+ }
+ memcpy(buff, buff2, result);
+ }
+
+#ifdef DEBUG
+ print_hex(buff2, 90);
+#endif
+
+ /* start */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len = parse_val(buff2, 0x33, tmpbuf);
+
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len += parse_val(buff2+len, 0x0a, tmpbuf);
+
+ /* first data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ //accel.read_accelx = atof(tmpbuf);
+ accel_state->x = (int)(atof(tmpbuf) * (-26.2)); // 26 ~= 256 / 9.8
+ if (accel_state->x > 512)
+ accel_state->x = 512;
+ if (accel_state->x < -512)
+ accel_state->x = -512;
+
+ /* second data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ accel_state->y = (int)(atof(tmpbuf) * 26.2);
+ if (accel_state->y > 512)
+ accel_state->y = 512;
+ if (accel_state->y < -512)
+ accel_state->y = -512;
+
+ /* third data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ accel_state->z = (int)(atof(tmpbuf) * 26);
+
+ if (accel_state->z > 512)
+ accel_state->z = 512;
+ if (accel_state->z < -512)
+ accel_state->z = -512;
+
+#ifdef DEBUG
+ printf("accel_state->x=%d %d %d\n", accel_state->x, accel_state->y, accel_state->z);
+#endif
+
+ close( client_socket);
+
+ return 0;
+}
+
+static void smb380_write(void *opaque, uint32_t address, uint8_t offset, uint8_t val)
+{
+ SensorState *s = (SensorState *)opaque;
+
+#ifdef DEBUG
+ printf("smb380_write\n");
+#endif
+
+ get_from_ide (&glob_accel_state);
+
+ sensor_xyz_set (s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+ s->idx_out = 0;
+
+ return;
+}
+
+static int smb380_init(I2CAddressableState *s)
+{
+ SensorState *t = FROM_I2CADDR_SLAVE(SensorState, s);
+
+ smb380_reset(s);
+
+ return 0;
+}
+
+#if 0
+#define DEFAULT_TIME 500000000
+/* get sensor info from IDE when performance problem occurs */
+
+static void sensor_timer (void *opaque)
+{
+ SensorState *s = opaque;
+ int64_t expire_time = DEFAULT_TIME;
+
+#ifdef DEBUG
+ //printf("sensor timer\n");
+#endif
+
+ /* 1 sec : 500000000 */
+
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + expire_time);
+}
+
+
+/* set timer for getting sensor info */
+
+SensorState* sensor_init()
+{
+ SensorState *s = &glob_accel_state;
+
+ s->ts = qemu_new_timer (vm_clock, sensor_timer, s);
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + DEFAULT_TIME);
+
+ return s;
+
+ //s->ts = qemu_new_timer (vm_clock, accel_timer, s);
+}
+#endif
+
+
+static I2CAddressableDeviceInfo smb380_info = {
+ .i2c.qdev.name = "smb380",
+ .i2c.qdev.size = sizeof(SensorState),
+ .init = smb380_init,
+ .read = smb380_read,
+ .write = smb380_write,
+ .size = 1,
+ .rev = 0
+};
+
+static void smb380_register_devices(void)
+{
+ i2c_addressable_register_device(&smb380_info);
+}
+
+
+device_init(smb380_register_devices)
--- /dev/null
+/*
+ * SMB380 Sensor Emulation
+ *
+ * Contributed by Junsik.Park <okdear.park@samsung.com>
+ */
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#else
+#include <windows.h>
+#include <winsock2.h>
+#include <sys/types.h>
+#endif
+
+#include "hw.h"
+#include "i2c.h"
+#include "smbus.h"
+
+//#define DEBUG
+
+typedef struct SMBusSENSORDevice {
+ SMBusDevice smbusedev;
+ uint8_t data[7];
+
+ int16_t x;
+ int16_t y;
+ int16_t z;
+ int idx_out, req_out;
+}SMBusSENSORDevice;
+
+SMBusSENSORDevice glob_accel_state;
+
+static int sensor_xyz_set(struct SMBusSENSORDevice *s, uint16_t x, uint16_t y, uint16_t z);
+
+#define BITS_PER_BYTE 8
+#define SMB380_ACCEL_BITS 10
+
+static void smb380_quick_cmd(SMBusDevice *dev, uint8_t read)
+{
+#ifdef DEBUG
+ printf("sensor quick cmd : addr=0x%02x read%d\n", dev->i2c.address, read);
+#endif
+}
+
+static void smb380_send_byte(SMBusDevice *dev, uint8_t val)
+{
+#ifdef DEBUG
+ SMBusSENSORDevice *s = (SMBusSENSORDevice *) dev;
+ printf("smb380_send_byte: addr=0x%02x val=0x%02x index=%d\n",dev->i2c.address, val, s->idx_out);
+#endif
+
+}
+
+static uint8_t smb380_receive_byte(SMBusDevice *dev, uint8_t address)
+{
+ SMBusSENSORDevice *s = (SMBusSENSORDevice *) dev;
+ int index = (int) address;
+ if (index != 0)
+ return s->data[index-2];
+ else
+ return sensor_xyz_set(s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+}
+
+void sensor_update(uint16_t x, uint16_t y, uint16_t z)
+{
+ glob_accel_state.x = x;
+ glob_accel_state.y = y;
+ glob_accel_state.z = z;
+}
+
+static int sensor_xyz_set(struct SMBusSENSORDevice *s, uint16_t x, uint16_t y, uint16_t z)
+{
+ /* remains right 10bit */
+
+ x <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ x >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+ y <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ y >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+ z <<= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ z >>= (sizeof(uint16_t) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+
+ /* data[1] : 2 ~ 10 (bit)
+ * data[0] : 0 ~ 1 (bit) */
+
+ s->data[0] = (0x3 & x) << 6;
+ s->data[1] = x >> 2;
+ s->data[2] = (0x3 & y) << 6;
+ s->data[3] = y >> 2;
+ s->data[4] = (0x3 & z) << 6;
+ s->data[5] = z >> 2;
+
+ return 0;
+}
+
+static void smb380_reset(struct SMBusSENSORDevice *s)
+{
+ s->idx_out = 0;
+
+ glob_accel_state.x = 0;
+ glob_accel_state.y = -256;
+ glob_accel_state.z = 0;
+
+ s->data[0] = 0;
+ s->data[1] = 0;
+ s->data[2] = 0;
+ s->data[3] = 0;
+ s->data[4] = 0;
+ s->data[5] = 0;
+ s->idx_out = 0;
+
+ return ;
+}
+
+static uint8_t smb380_read_data(SMBusDevice *opaque, uint8_t address, uint8_t offset)
+{
+ return smb380_receive_byte(opaque, address);
+}
+
+#ifdef DEBUG
+static int print_hex(char *data, int len)
+{
+ return 0;
+}
+#endif
+
+static int parse_val(char *buff, unsigned char find_data, char *parsebuff)
+{
+ int vvvi = 0;
+ while (1) {
+ if (vvvi > 40) {
+ return -1;
+ }
+ if (buff[vvvi] == find_data) {
+
+ vvvi++;
+ strncpy(parsebuff, buff, vvvi);
+ return vvvi;
+ }
+ vvvi++;
+ }
+
+ return 0;
+}
+
+#define SENSOR_BUF 16
+#define SENSOR_VALUE_BUF 56
+static int get_from_ide(struct SMBusSENSORDevice *accel_state)
+{
+ int client_socket;
+ struct sockaddr_in server_addr;
+ char buff[SENSOR_VALUE_BUF + 1];
+ char buff2[SENSOR_VALUE_BUF + 1];
+ char tmpbuf[SENSOR_VALUE_BUF + 1];
+ int len = 0, len1 = 0;
+ int result = 0;
+ const char *command0 = "readSensor()\n";
+ const char *command1 = "accelerometer\n";
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
+ return -1;
+
+ client_socket = socket(AF_INET, SOCK_STREAM, 0);
+#else
+ client_socket = socket(PF_INET, SOCK_STREAM, 0);
+#endif
+ if (client_socket == -1) {
+ return -1;
+ }
+
+ memset(&server_addr, 0, sizeof( server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(8010);
+ server_addr.sin_addr.s_addr= inet_addr("127.0.0.1");
+ if( -1 == connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) {
+ close( client_socket);
+ return -1;
+ }
+
+ if(read (client_socket, buff, SENSOR_BUF) < 0) {
+ close(client_socket);
+ return -1;
+ }
+
+ if(1) {
+ if(write(client_socket, command0, strlen(command0)) < 0) {
+ close(client_socket);
+ return -1;
+ }
+ if(write(client_socket, command1, strlen(command1)) < 0) {
+ close(client_socket);
+ return -1;
+ }
+
+ memset(buff, '\0', sizeof(buff));
+ memset(buff2, '\0', sizeof(buff2));
+
+ result = read(client_socket, buff2, SENSOR_VALUE_BUF);
+ if (result <= (SENSOR_VALUE_BUF)/2) {
+ close( client_socket);
+ return -1;
+ }
+ memcpy(buff, buff2, result);
+ }
+
+#ifdef DEBUG
+ print_hex(buff2, 90);
+#endif
+
+ /* start */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len = parse_val(buff2, 0x33, tmpbuf);
+
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len += parse_val(buff2+len, 0x0a, tmpbuf);
+
+ /* first data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ //accel.read_accelx = atof(tmpbuf);
+ accel_state->x = (int)(atof(tmpbuf) * (-26.2)); // 26 ~= 256 / 9.8
+ if (accel_state->x > 512)
+ accel_state->x = 512;
+ if (accel_state->x < -512)
+ accel_state->x = -512;
+
+ /* second data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ accel_state->y = (int)(atof(tmpbuf) * 26.2);
+ if (accel_state->y > 512)
+ accel_state->y = 512;
+ if (accel_state->y < -512)
+ accel_state->y = -512;
+
+ /* third data */
+ memset(tmpbuf, '\0', sizeof(tmpbuf));
+ len1 = parse_val(buff2+len, 0x0a, tmpbuf);
+ len += len1;
+#ifdef DEBUG
+ print_hex(tmpbuf, len1);
+#endif
+ accel_state->z = (int)(atof(tmpbuf) * 26);
+
+ if (accel_state->z > 512)
+ accel_state->z = 512;
+ if (accel_state->z < -512)
+ accel_state->z = -512;
+
+#ifdef DEBUG
+ printf("accel_state->x=%d %d %d\n", accel_state->x, accel_state->y, accel_state->z);
+#endif
+
+ close( client_socket);
+
+ return 0;
+}
+
+static void smb380_write_data(SMBusDevice *dev, uint8_t address, uint8_t offset, uint8_t val)
+{
+ SMBusSENSORDevice *s = (SMBusSENSORDevice *)dev;
+
+#ifdef DEBUG
+ printf("smb380_write\n");
+#endif
+ get_from_ide (&glob_accel_state);
+
+ sensor_xyz_set (s, glob_accel_state.x, glob_accel_state.y, glob_accel_state.z);
+
+
+#ifdef DEBUG
+ printf("smb380_send_byte: addr=0x%02x val=0x%02x\n",address, val);
+#endif
+ smb380_send_byte(dev,val);
+
+}
+
+static int smb380_init(SMBusDevice *s)
+{
+ SMBusSENSORDevice *t = (SMBusSENSORDevice *)s;
+
+ smb380_reset(t);
+ return 0;
+}
+
+#if 0
+#define DEFAULT_TIME 500000000
+/* get sensor info from IDE when performance problem occurs */
+
+static void sensor_timer (void *opaque)
+{
+ SensorState *s = opaque;
+ int64_t expire_time = DEFAULT_TIME;
+
+#ifdef DEBUG
+ //printf("sensor timer\n");
+#endif
+
+ /* 1 sec : 500000000 */
+
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + expire_time);
+}
+
+
+/* set timer for getting sensor info */
+
+SensorState* sensor_init()
+{
+ SensorState *s = &glob_accel_state;
+
+ s->ts = qemu_new_timer (vm_clock, sensor_timer, s);
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + DEFAULT_TIME);
+
+ return s;
+
+ //s->ts = qemu_new_timer (vm_clock, accel_timer, s);
+}
+#endif
+
+
+static SMBusDeviceInfo smb380_info = {
+ .i2c.qdev.name = "smb380",
+ .i2c.qdev.size = sizeof(SMBusSENSORDevice),
+ .init = smb380_init,
+ .quick_cmd = smb380_quick_cmd,
+ .send_byte = smb380_send_byte,
+ .receive_byte = smb380_receive_byte,
+ .read_data = smb380_read_data,
+ .write_data = smb380_write_data
+};
+
+static void smb380_register_devices(void)
+{
+ smbus_register_device(&smb380_info);
+}
+
+device_init(smb380_register_devices)
}
QUEUE_KEY((code & 0x7f) | (keycode & 0x80));
+ return 0;
}
static void spitz_keyboard_tick(void *opaque)
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);
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;
if (keycode == 0xe0 && !s->extension) {
s->extension = 0x80;
- return;
+ return 0;
}
down = (keycode & 0x80) == 0;
}
s->extension = 0;
+ return 0;
}
static void stellaris_gamepad_save(QEMUFile *f, void *opaque)
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);
}
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;
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)
}
syborg_keyboard_update(s);
+ return 0;
}
static void syborg_keyboard_save(QEMUFile *f, void *opaque)
}
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);
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+#ifndef QEMU_USB_EHCI_H
+#define QEMU_USB_EHCI_H
+
+#include "qemu-common.h"
+
+void usb_ehci_init_pci(PCIBus *bus, int devfn);
+
+#endif
uint8_t leds;
uint8_t key[16];
int32_t keys;
+ int keyboard_grabbed;
+ QEMUPutKbdEntry *eh_entry;
} USBKeyboardState;
typedef struct USBHIDState {
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;
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];
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)) {
break;
}
if (i < 0)
- return;
+ return 0;
} else {
for (i = s->keys - 1; i >= 0; i --)
if (s->key[i] == hid_code)
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)
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;
{
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;
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);
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"
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
} 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;
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);
if (ret != USB_RET_NODEV)
break;
}
-
+
if (ret == USB_RET_ASYNC) {
return 1;
}
.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)
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 {
--- /dev/null
+/*
+ * WM8994 Audio Codec
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Alexey Merkulov <steelart@ispras.ru>
+ * Dmitry Zhurikhin <zhur@ispras.ru>
+ * Vladimir Monakhov <vmonakhov@ispras.ru>
+ */
+
+#include "i2c-addressable.h"
+#include "audio/audio.h"
+#include "wm8994_reg.h"
+
+
+#define CODEC "wm8994"
+
+
+typedef struct WM8994State {
+
+ I2CAddressableState i2c_addressable;
+
+ uint16_t registers[WM8994_REGISTER_MEM_SIZE];
+
+ void (*data_req)(void *, int);
+ void *opaque;
+
+ SWVoiceOut *dac_voice;
+ QEMUSoundCard card;
+
+ uint8_t data_out[4 * 4096]; /* magic */
+
+ int idx_out, req_out;
+} WM8994State;
+
+
+static inline uint8_t wm8994_volume(WM8994State *s, uint16_t reg)
+{
+ return s->registers[reg] & 0x3F;
+}
+
+static inline uint8_t wm8994_mute(WM8994State *s, uint16_t reg)
+{
+ return (s->registers[reg] >> 6) & 1;
+}
+
+static inline uint8_t wm8994_outvol_transform(WM8994State *s, uint16_t reg)
+{
+ return 0xFF * wm8994_volume(s, reg) / 0x3F;
+}
+
+static inline int wm8994_rate(WM8994State *s, uint16_t reg)
+{
+ switch ((s->registers[reg] >> 4) & 0xF) {
+ case 0: return 8000;
+ case 1: return 11025;
+ case 2: return 12000;
+ case 3: return 16000;
+ case 4: return 22050;
+ case 5: return 24000;
+ case 6: return 32000;
+ case 7: return 44100;
+ case 8: return 48000;
+ case 9: return 88200;
+ case 10: return 96000;
+
+ default:
+ return 0;
+ }
+}
+
+static inline audfmt_e wm8994_format(WM8994State *s, uint16_t reg)
+{
+ switch ((s->registers[reg] >> 5) & 0x3) {
+ case 0: return AUD_FMT_S16;
+ case 3: return AUD_FMT_S32;
+ case 1:
+ /*TODO: implement conversion */
+ hw_error("wm8994: unsupported format (20 bits for channel)\n");
+ return AUD_FMT_S16;
+ case 2:
+ /*TODO: implement conversion */
+ hw_error("wm8994: unsupported format (24 bits for channel)\n");
+ return AUD_FMT_S16;
+ default:
+ hw_error("wm8994: unknown format\n");
+ return AUD_FMT_S16;
+ }
+}
+
+static inline void wm8994_out_flush(WM8994State *s)
+{
+ int sent = 0;
+
+ if (!s->dac_voice)
+ return;
+ while (sent < s->idx_out) {
+ sent +=
+ AUD_write(s->dac_voice, s->data_out + sent, s->idx_out - sent) ?:
+ s->idx_out;
+ }
+ s->idx_out = 0;
+}
+
+static void wm8994_audio_out_cb(void *opaque, int free_b)
+{
+ WM8994State *s = (WM8994State *) opaque;
+
+ if (s->idx_out >= free_b) {
+ s->idx_out = free_b;
+ s->req_out = 0;
+ wm8994_out_flush(s);
+ } else {
+ s->req_out = free_b - s->idx_out;
+ }
+
+ if (s->data_req) {
+ s->data_req(s->opaque, s->req_out >> 2);
+ }
+}
+
+static void wm8994_vol_update(WM8994State *s)
+{
+ int volume_left = wm8994_volume(s, WM8994_SPEAKER_VOLUME_LEFT);
+ int volume_right = wm8994_volume(s, WM8994_SPEAKER_VOLUME_RIGHT);
+
+ /* Speaker */
+ AUD_set_volume_out(s->dac_voice, 1, volume_left, volume_right);
+}
+
+static void wm8994_set_format(WM8994State *s)
+{
+ struct audsettings out_fmt;
+
+ wm8994_out_flush(s);
+
+ if (s->dac_voice) {
+ AUD_set_active_out(s->dac_voice, 0);
+ }
+
+ if (s->dac_voice) {
+ AUD_close_out(&s->card, s->dac_voice);
+ s->dac_voice = NULL;
+ }
+
+ /* Setup output */
+ out_fmt.endianness = 0;
+ out_fmt.nchannels = 2;
+ out_fmt.freq = wm8994_rate(s, WM8994_AIF1_RATE);
+ out_fmt.fmt = wm8994_format(s, WM8994_AIF1_CONTROL_1);
+
+ s->dac_voice =
+ AUD_open_out(&s->card, s->dac_voice, CODEC ".speaker",
+ s, wm8994_audio_out_cb, &out_fmt);
+ wm8994_vol_update(s);
+
+ if (s->dac_voice) {
+ AUD_set_active_out(s->dac_voice, 1);
+ }
+}
+
+static void wm8994_reset(DeviceState *d)
+{
+ WM8994State *s =
+ FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(d));
+
+ memset(s->registers, 0, sizeof(s->registers));
+ s->registers[WM8994_SOFTWARE_RESET] = 0x8994;
+
+ s->registers[WM8994_POWER_MANAGEMENT_2] = 0x6000;
+
+ s->registers[WM8994_SPEAKER_VOLUME_LEFT] = 0x79;
+ s->registers[WM8994_SPEAKER_VOLUME_RIGHT] = 0x79;
+
+ s->registers[WM8994_AIF1_RATE] = 0x73;
+
+ s->idx_out = 0;
+ s->req_out = 0;
+ s->dac_voice = NULL;
+
+ wm8994_set_format(s);
+}
+
+static uint8_t wm8994_read(void *opaque, uint32_t address, uint8_t offset)
+{
+ WM8994State *s = (WM8994State *)opaque;
+
+ if (offset >= 2) {
+ /* FIXME: there should be an error but kernel wants to read more
+ * than allowed; so just pretend there's nothing here */
+ /* hw_error("wm8994: too much data requested"); */
+ return 0;
+ }
+ if (address < WM8994_REGISTER_MEM_SIZE) {
+ return (s->registers[address] >> ((1 - offset) * 8)) & 0xFF;
+ } else {
+ hw_error("wm8994: illegal read offset 0x%x\n", address + offset);
+ }
+}
+
+static void wm8994_write(void *opaque, uint32_t address, uint8_t offset,
+ uint8_t val)
+{
+ WM8994State *s = (WM8994State *)opaque;
+
+ address += offset / 2;
+ offset %= 2;
+
+ if (address < WM8994_REGISTER_MEM_SIZE) {
+ s->registers[address] &= ~(0xFF << ((1 - offset) * 8));
+ s->registers[address] |= val << ((1 - offset) * 8);
+
+ if (offset == 1) {
+ switch (address) {
+ case WM8994_SOFTWARE_RESET:
+ wm8994_reset(&s->i2c_addressable.i2c.qdev);
+ break;
+
+ case WM8994_SPEAKER_VOLUME_LEFT:
+ case WM8994_SPEAKER_VOLUME_RIGHT:
+ wm8994_vol_update(s);
+ break;
+
+ case WM8994_AIF1_RATE:
+ case WM8994_AIF1_CONTROL_1:
+ wm8994_set_format(s);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ hw_error("wm8994: illegal write offset 0x%x\n", address + offset);
+ }
+}
+
+void wm8994_data_req_set(DeviceState *dev, void (*data_req)(void *, int),
+ void *opaque)
+{
+ WM8994State *s =
+ FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+ s->data_req = data_req;
+ s->opaque = opaque;
+}
+
+void *wm8994_dac_buffer(DeviceState *dev, int samples)
+{
+ WM8994State *s =
+ FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+ /* XXX: Should check if there are <i>samples</i> free samples available */
+ void *ret = s->data_out + s->idx_out;
+
+ s->idx_out += samples << 2;
+ s->req_out -= samples << 2;
+ return ret;
+}
+
+void wm8994_dac_dat(DeviceState *dev, uint32_t sample)
+{
+ WM8994State *s =
+ FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+ *(uint32_t *) &s->data_out[s->idx_out] = sample;
+ s->req_out -= 4;
+ s->idx_out += 4;
+ if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) {
+ wm8994_out_flush(s);
+ }
+}
+
+void wm8994_dac_commit(DeviceState *dev)
+{
+ WM8994State *s =
+ FROM_I2CADDR_SLAVE(WM8994State, I2CADDR_SLAVE_FROM_QDEV(dev));
+
+ return wm8994_out_flush(s);
+}
+
+static int wm8994_init(I2CAddressableState *i2c)
+{
+ WM8994State *s = FROM_I2CADDR_SLAVE(WM8994State, i2c);
+
+ AUD_register_card(CODEC, &s->card);
+
+ wm8994_reset(&i2c->i2c.qdev);
+
+ return 0;
+}
+
+static I2CAddressableDeviceInfo wm8994_info = {
+ .i2c.qdev.name = "wm8994",
+ .i2c.qdev.size = sizeof(WM8994State),
+ .i2c.qdev.reset = wm8994_reset,
+ .init = wm8994_init,
+ .read = wm8994_read,
+ .write = wm8994_write,
+ .size = 2,
+ .rev = 1
+};
+
+static void wm8994_register_devices(void)
+{
+ i2c_addressable_register_device(&wm8994_info);
+}
+
+device_init(wm8994_register_devices)
--- /dev/null
+#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 */
int button_state; /* Last seen pointer button state */
int extended;
QEMUPutMouseEntry *qmouse;
+ QEMUPutKbdEntry *qkbd;
};
#define UP_QUEUE 8
if (scancode == 0xe0) {
xenfb->extended = 1;
- return;
+ return 0;
} else if (scancode & 0x80) {
scancode &= 0x7f;
down = 0;
xenfb->extended = 0;
}
xenfb_send_key(xenfb, down, scancode2linux[scancode]);
+ return 0;
}
/*
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");
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);
}
#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;
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)
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;
+ }
}
}
{
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;
+}
--- /dev/null
+#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;
+}
#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"
.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 = "",
@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
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 \
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",
const char *message);
void icmp_reflect(struct mbuf *);
+#ifndef _WIN32
+#include <sys/wait.h>
+#endif
+#include <unistd.h>
+
#endif
#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
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];
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 {
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. */
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. */
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;
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
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)
#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
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 };
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;
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);
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;
{ 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" },
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;
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;
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;
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);
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;
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)
}
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:
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. */
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;
}
/* ??? 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) {
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. */
default:
goto bad_reg;
}
+ break;
case 1:
/* These registers aren't documented on arm11 cores. However
Linux looks at them anyway. */
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) {
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. */
} 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;
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) {
--- /dev/null
+#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;
+}
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)
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)
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)
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"
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);
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);
float32 s;
} v;
+ /* flush-to-zero */
+ if (!(i & (0xff << 23))) {
+ i &= 1 << 31; /* make it +-0 */
+ }
+
v.i = i;
return v.s;
}
}} 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;
val = 0;
} else if (shift < -64) {
val >>= 63;
- } else if (shift == -63) {
+ } else if (shift == -64) {
val >>= 63;
val++;
val >>= 1;
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 { \
}} 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;
}
}} 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;
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)
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();
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)
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;
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;
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) {
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;
}
return result;
}
-#include <stdio.h>
uint64_t HELPER(neon_negl_u32)(uint64_t x)
{
uint32_t low = -x;
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);
+ }
+}
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
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "debug_ch.h"
+
+MULTI_DEBUG_CHANNEL(qemu, arm_dummy);
+
+void *init_opengl_server(void *arg);
+
+void *init_opengl_server(void *arg){
+ ERR("init_open_gl_server(arm_dummy) called!!!\n");
+ return 0;
+}
+
+#ifndef _WIN32
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+void opengl_exec_set_parent_window(Display* _dpy, Window _parent_window);
+void opengl_exec_set_parent_window(Display* _dpy, Window _parent_window)
+{
+ ERR("opengl_exec_set_parent_window(arm_dummy) called!!!\n");
+}
+#endif
#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)
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. */
#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();
}
}
+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();
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();
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;
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();
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)
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;
offset = 8;
else
offset = 4;
+ tmp = tcg_const_i32(offset);
for (i = 0; i < n; i++) {
if (insn & ARM_CP_RW_BIT) {
/* load */
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))
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) {
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;
dead_tmp(rd);
}
-
static struct {
int nregs;
int interleave;
int pass;
int load;
int shift;
- int n;
TCGv addr;
TCGv tmp;
TCGv tmp2;
- TCGv_i64 tmp64;
if (!s->vfp_enabled)
return 1;
/* 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) {
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);
}
}
+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)
{
} 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 {
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);
} 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 */
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++) {
/* 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 {
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 */
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 */
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 */
GEN_NEON_INTEGER_OP_ENV(qshl);
break;
}
- dead_tmp(tmp2);
+ tcg_temp_free_i32(tmp2);
if (op == 1 || op == 3) {
/* Accumulate. */
/* 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);
} /* 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) {
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 {
}
} 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));
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);
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;
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;
} 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
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);
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);
}
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;
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) {
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) {
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++) {
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);
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
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:
/* 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);
/* 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);
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);
}
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;
}
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));
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);
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 */
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 */
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;
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;
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) {
} 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();
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();
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 {
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();
}
if ((insn & 0x0d70f000) == 0x0550f000)
return; /* PLD */
+ if ((insn & 0x0d70f000) == 0x0450f000) {
+ ARCH(7);
+ return; /* PLI */
+ }
else if ((insn & 0x0ffffdff) == 0x01010000) {
ARCH(6);
/* setend */
}
} 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;
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) {
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);
}
rn = (insn >> 16) & 0xf;
addr = load_reg(s, rn);
-
+ tmp3 = tcg_const_i32(4);
+
/* compute total size */
loaded_base = 0;
TCGV_UNUSED(loaded_var);
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 */
}
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)) {
/* pre increment */
} else {
/* post increment */
- tcg_gen_addi_i32(addr, addr, 4);
+ tcg_gen_add_i32(addr, addr, tmp3);
}
} else {
if (insn & (1 << 24)) {
} else {
dead_tmp(addr);
}
+ tcg_temp_free_i32(tmp3);
if (loaded_base) {
store_reg(s, rn, loaded_var);
}
tcg_gen_addi_i32(addr, addr, -offset);
}
+ tmp2 = tcg_const_i32(4);
for (i = 0; i < 16; i++) {
if ((insn & (1 << i)) == 0)
continue;
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)) {
/* 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 {
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) {
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)) {
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);
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);
}
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;
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
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;
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. */
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.
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);
}
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. */
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;
}
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");
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
@echo " CC $@"
$(Q)$(CC) $(CFLAGS) -o $@ -c $<
-#all: $(TARGET_X86) $(TARGET_ARM)
-all: $(TARGET_X86) $(VTM)
+all: $(TARGET_X86) $(TARGET_ARM) $(VTM)
-include $(DEPFILES)
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
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;
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 {
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);
}
}
}
#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). */
USBPacket *packet;
USBHostDevice *hdev;
+
+ int more; /* packet required multiple URBs */
} AsyncURB;
static AsyncURB *async_alloc(void)
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);
}
break;
}
- usb_packet_complete(p);
+ if (!aurb->more) {
+ DPRINTF("invoking packet_complete. plen = %d\n", p->len);
+ usb_packet_complete(p);
+ }
}
async_free(aurb);
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 */
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];
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;
}
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;
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;