]> code.ossystems Code Review - openembedded-core.git/commitdiff
qemu: Restore qemu r4027 until i686 issues are resolved
authorRichard Purdie <richard@openedhand.com>
Fri, 25 Apr 2008 12:21:49 +0000 (12:21 +0000)
committerRichard Purdie <richard@openedhand.com>
Fri, 25 Apr 2008 12:21:49 +0000 (12:21 +0000)
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@4338 311d38ba-8fff-0310-9ca6-ca027cbcb966

33 files changed:
meta/conf/distro/poky.conf
meta/packages/qemu/qemu-0.9.1+svnr4027/02_snapshot_use_tmpdir.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/04_do_not_print_rtc_freq_if_ok.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/05_non-fatal_if_linux_hd_missing.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/06_exit_segfault.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/10_signal_jobs.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/11_signal_sigaction.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/22_net_tuntap_stall.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/31_syscalls.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/32_syscall_sysctl.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/33_syscall_ppc_clone.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/39_syscall_fadvise64.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/41_arm_fpa_sigfpe.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/52_ne2000_return.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/61_safe_64bit_int.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/63_sparc_build.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/64_ppc_asm_constraints.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/65_kfreebsd.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/66_tls_ld.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/91-oh-sdl-cursor.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/configure_symlinkpath_fix.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/fix_brk.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/fix_protection_bits.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/fix_segfault.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/no-strip.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl-update.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-amd64-32b-mapping-0.9.0.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-n800-support.patch [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/series [new file with mode: 0644]
meta/packages/qemu/qemu-0.9.1+svnr4027/workaround_bad_futex_headers.patch [new file with mode: 0644]
meta/packages/qemu/qemu_0.9.1.bb
meta/packages/qemu/qemu_svn.bb

index fb176d0f905af08e51da5c7c662d17d7bf82d8d7..2118a3466a52b9a93ab84dc6eed6cda90a699833 100644 (file)
@@ -149,9 +149,11 @@ SRCREV_pn-opkg ?= "4209"
 SRCREV_pn-opkg-native ?= "4209"
 SRCREV_pn-opkg-sdk ?= "4209"
 SRCREV_pn-libxosd ?= "627"
-SRCREV_pn-qemu-native ?= "4242"
-SRCREV_pn-qemu-sdk ?= "4242"
-SRCREV_pn-qemu ?= "4242"
+#QEMUSRCREV = "4242"
+QEMUSRCREV = "4027"
+SRCREV_pn-qemu-native ?= "${QEMUSRCREV}"
+SRCREV_pn-qemu-sdk ?= "${QEMUSRCREV}"
+SRCREV_pn-qemu ?= "${QEMUSRCREV}"
 SRCREV_pn-vincent ?= "246"
 
 # Previously floating revisions
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/02_snapshot_use_tmpdir.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/02_snapshot_use_tmpdir.patch
new file mode 100644 (file)
index 0000000..40264ed
--- /dev/null
@@ -0,0 +1,23 @@
+#DPATCHLEVEL=0
+---
+# block.c |    6 +++++-
+# 1 file changed, 5 insertions(+), 1 deletion(-)
+#
+Index: block.c
+===================================================================
+--- block.c.orig       2007-12-03 23:47:25.000000000 +0000
++++ block.c    2007-12-03 23:47:31.000000000 +0000
+@@ -191,8 +191,12 @@ void get_tmp_filename(char *filename, in
+ void get_tmp_filename(char *filename, int size)
+ {
+     int fd;
++    char *tmpdir;
+     /* XXX: race condition possible */
+-    pstrcpy(filename, size, "/tmp/vl.XXXXXX");
++    tmpdir = getenv("TMPDIR");
++    if (!tmpdir)
++        tmpdir = "/tmp";
++    snprintf(filename, size, "%s/vl.XXXXXX", tmpdir);
+     fd = mkstemp(filename);
+     close(fd);
+ }
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/04_do_not_print_rtc_freq_if_ok.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/04_do_not_print_rtc_freq_if_ok.patch
new file mode 100644 (file)
index 0000000..31c9da4
--- /dev/null
@@ -0,0 +1,26 @@
+#DPATCHLEVEL=1
+---
+# vl.c |    5 ++++-
+# 1 file changed, 4 insertions(+), 1 deletion(-)
+#
+Index: qemu/vl.c
+===================================================================
+--- qemu.orig/vl.c     2007-12-03 15:44:35.000000000 +0000
++++ qemu/vl.c  2007-12-03 15:51:03.000000000 +0000
+@@ -1289,12 +1289,15 @@ static void hpet_stop_timer(struct qemu_
+ static int rtc_start_timer(struct qemu_alarm_timer *t)
+ {
++    unsigned long current_rtc_freq = 0;
+     int rtc_fd;
+     TFR(rtc_fd = open("/dev/rtc", O_RDONLY));
+     if (rtc_fd < 0)
+         return -1;
+-    if (ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) {
++    ioctl(rtc_fd, RTC_IRQP_READ, &current_rtc_freq);
++    if (current_rtc_freq != RTC_FREQ &&
++        ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) {
+         fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n"
+                 "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n"
+                 "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n");
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/05_non-fatal_if_linux_hd_missing.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/05_non-fatal_if_linux_hd_missing.patch
new file mode 100644 (file)
index 0000000..fdd9226
--- /dev/null
@@ -0,0 +1,17 @@
+#DPATCHLEVEL=1
+---
+# hw/pc.c |    1 -
+# 1 file changed, 1 deletion(-)
+#
+Index: qemu/hw/pc.c
+===================================================================
+--- qemu.orig/hw/pc.c  2007-12-03 23:47:25.000000000 +0000
++++ qemu/hw/pc.c       2007-12-03 23:47:38.000000000 +0000
+@@ -385,7 +385,6 @@ static void generate_bootsect(uint32_t g
+     if (bs_table[0] == NULL) {
+       fprintf(stderr, "A disk image must be given for 'hda' when booting "
+               "a Linux kernel\n");
+-      exit(1);
+     }
+     memset(bootsect, 0, sizeof(bootsect));
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/06_exit_segfault.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/06_exit_segfault.patch
new file mode 100644 (file)
index 0000000..06123d0
--- /dev/null
@@ -0,0 +1,45 @@
+#DPATCHLEVEL=0
+---
+# linux-user/main.c |    8 ++++----
+# 1 file changed, 4 insertions(+), 4 deletions(-)
+#
+Index: linux-user/main.c
+===================================================================
+--- linux-user/main.c.orig     2007-12-03 23:47:25.000000000 +0000
++++ linux-user/main.c  2007-12-03 23:47:41.000000000 +0000
+@@ -714,7 +714,7 @@ void cpu_loop (CPUSPARCState *env)
+         default:
+             printf ("Unhandled trap: 0x%x\n", trapnr);
+             cpu_dump_state(env, stderr, fprintf, 0);
+-            exit (1);
++            _exit (1);
+         }
+         process_pending_signals (env);
+     }
+@@ -1634,7 +1634,7 @@ void cpu_loop (CPUState *env)
+         default:
+             printf ("Unhandled trap: 0x%x\n", trapnr);
+             cpu_dump_state(env, stderr, fprintf, 0);
+-            exit (1);
++            _exit (1);
+         }
+         process_pending_signals (env);
+     }
+@@ -1954,7 +1954,7 @@ int main(int argc, char **argv)
+                 for(item = cpu_log_items; item->mask != 0; item++) {
+                     printf("%-10s %s\n", item->name, item->help);
+                 }
+-                exit(1);
++                _exit(1);
+             }
+             cpu_set_log(mask);
+         } else if (!strcmp(r, "s")) {
+@@ -1973,7 +1973,7 @@ int main(int argc, char **argv)
+             if (qemu_host_page_size == 0 ||
+                 (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
+                 fprintf(stderr, "page size must be a power of two\n");
+-                exit(1);
++                _exit(1);
+             }
+         } else if (!strcmp(r, "g")) {
+             gdbstub_port = atoi(argv[optind++]);
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/10_signal_jobs.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/10_signal_jobs.patch
new file mode 100644 (file)
index 0000000..34282ad
--- /dev/null
@@ -0,0 +1,26 @@
+#DPATCHLEVEL=0
+---
+# linux-user/signal.c |    7 ++++++-
+# 1 file changed, 6 insertions(+), 1 deletion(-)
+#
+Index: linux-user/signal.c
+===================================================================
+--- linux-user/signal.c.orig   2007-12-03 15:40:26.000000000 +0000
++++ linux-user/signal.c        2007-12-03 15:55:49.000000000 +0000
+@@ -364,10 +364,15 @@ int queue_signal(int sig, target_siginfo
+     k = &sigact_table[sig - 1];
+     handler = k->sa._sa_handler;
+     if (handler == TARGET_SIG_DFL) {
++        if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
++            kill(getpid(),SIGSTOP);
++            return 0;
++        } else
+         /* default handler : ignore some signal. The other are fatal */
+         if (sig != TARGET_SIGCHLD &&
+             sig != TARGET_SIGURG &&
+-            sig != TARGET_SIGWINCH) {
++            sig != TARGET_SIGWINCH &&
++            sig != TARGET_SIGCONT) {
+             force_sig(sig);
+         } else {
+             return 0; /* indicate ignored */
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/11_signal_sigaction.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/11_signal_sigaction.patch
new file mode 100644 (file)
index 0000000..33c5e8b
--- /dev/null
@@ -0,0 +1,21 @@
+#DPATCHLEVEL=0
+---
+# linux-user/signal.c |    5 +++++
+# 1 file changed, 5 insertions(+)
+#
+Index: linux-user/signal.c
+===================================================================
+--- linux-user/signal.c.orig   2007-12-03 23:47:44.000000000 +0000
++++ linux-user/signal.c        2007-12-03 23:47:46.000000000 +0000
+@@ -512,6 +512,11 @@ int do_sigaction(int sig, const struct t
+     if (sig < 1 || sig > TARGET_NSIG || sig == SIGKILL || sig == SIGSTOP)
+         return -EINVAL;
++
++    /* no point doing the stuff as those are not allowed for sigaction */
++    if ((sig == TARGET_SIGKILL) || (sig == TARGET_SIGSTOP))
++        return -EINVAL;
++
+     k = &sigact_table[sig - 1];
+ #if defined(DEBUG_SIGNAL)
+     fprintf(stderr, "sigaction sig=%d act=0x%08x, oact=0x%08x\n",
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/22_net_tuntap_stall.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/22_net_tuntap_stall.patch
new file mode 100644 (file)
index 0000000..6017df0
--- /dev/null
@@ -0,0 +1,18 @@
+#DPATCHLEVEL=0
+---
+# vl.c |    2 +-
+# 1 file changed, 1 insertion(+), 1 deletion(-)
+#
+Index: vl.c
+===================================================================
+--- vl.c.orig  2007-12-03 23:47:36.000000000 +0000
++++ vl.c       2007-12-03 23:47:48.000000000 +0000
+@@ -4023,7 +4023,7 @@ static int tap_open(char *ifname, int if
+         return -1;
+     }
+     memset(&ifr, 0, sizeof(ifr));
+-    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
++    ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
+     if (ifname[0] != '\0')
+         pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+     else
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/31_syscalls.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/31_syscalls.patch
new file mode 100644 (file)
index 0000000..95a7332
--- /dev/null
@@ -0,0 +1,48 @@
+#DPATCHLEVEL=0
+---
+# linux-user/syscall.c |   11 ++++++++---
+# 1 file changed, 8 insertions(+), 3 deletions(-)
+#
+Index: linux-user/syscall.c
+===================================================================
+--- linux-user/syscall.c.orig  2007-12-03 19:32:56.000000000 +0000
++++ linux-user/syscall.c       2007-12-03 19:33:41.000000000 +0000
+@@ -250,6 +250,7 @@ extern int getresuid(uid_t *, uid_t *, u
+ extern int setresgid(gid_t, gid_t, gid_t);
+ extern int getresgid(gid_t *, gid_t *, gid_t *);
+ extern int setgroups(int, gid_t *);
++extern int uselib(const char*);
+ #define ERRNO_TABLE_SIZE 1200
+@@ -4024,7 +4025,8 @@ abi_long do_syscall(void *cpu_env, int n
+ #endif
+ #ifdef TARGET_NR_uselib
+     case TARGET_NR_uselib:
+-        goto unimplemented;
++        ret = get_errno(uselib(path((const char*)arg1)));
++        break;
+ #endif
+ #ifdef TARGET_NR_swapon
+     case TARGET_NR_swapon:
+@@ -5289,7 +5291,9 @@ abi_long do_syscall(void *cpu_env, int n
+         goto unimplemented;
+ #ifdef TARGET_NR_mincore
+     case TARGET_NR_mincore:
+-        goto unimplemented;
++        /*page_unprotect_range((void*)arg3, ((size_t)arg2 + TARGET_PAGE_SIZE - 1) / TARGET_PAGE_SIZE);*/
++        ret = get_errno(mincore((void*)arg1, (size_t)arg2, (unsigned char*)arg3));
++        break;
+ #endif
+ #ifdef TARGET_NR_madvise
+     case TARGET_NR_madvise:
+@@ -5429,7 +5433,8 @@ abi_long do_syscall(void *cpu_env, int n
+         break;
+ #ifdef TARGET_NR_readahead
+     case TARGET_NR_readahead:
+-        goto unimplemented;
++        ret = get_errno(readahead((int)arg1, (off64_t)arg2, (size_t)arg3));
++        break;
+ #endif
+ #ifdef TARGET_NR_setxattr
+     case TARGET_NR_setxattr:
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/32_syscall_sysctl.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/32_syscall_sysctl.patch
new file mode 100644 (file)
index 0000000..5e8dd75
--- /dev/null
@@ -0,0 +1,55 @@
+#DPATCHLEVEL=0
+---
+# linux-user/syscall.c |   32 +++++++++++++++++++++++++++++---
+# 1 file changed, 29 insertions(+), 3 deletions(-)
+#
+Index: linux-user/syscall.c
+===================================================================
+--- linux-user/syscall.c.orig  2007-12-03 15:56:24.000000000 +0000
++++ linux-user/syscall.c       2007-12-03 15:57:36.000000000 +0000
+@@ -52,6 +52,7 @@
+ //#include <sys/user.h>
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
++#include <sys/sysctl.h>
+ #define termios host_termios
+ #define winsize host_winsize
+@@ -4739,9 +4740,34 @@ abi_long do_syscall(void *cpu_env, int n
+         break;
+ #endif
+     case TARGET_NR__sysctl:
+-        /* We don't implement this, but ENOTDIR is always a safe
+-           return value. */
+-        ret = -TARGET_ENOTDIR;
++        {
++            struct __sysctl_args *args = (struct __sysctl_args *) arg1;
++            int *name_target, *name, nlen, *oldlenp, oldlen, newlen, i;
++            void *oldval, *newval;
++
++            name_target = (int *) tswapl((long) args->name);
++            nlen = tswapl(args->nlen);
++            oldval = (void *) tswapl((long) args->oldval);
++            oldlenp = (int *) tswapl((long) args->oldlenp);
++            oldlen = tswapl(*oldlenp);
++            newval = (void *) tswapl((long) args->newval);
++            newlen = tswapl(args->newlen);
++
++            name = alloca(nlen * sizeof (int));
++            for (i = 0; i < nlen; i++)
++                name[i] = tswapl(name_target[i]);
++
++            if (nlen == 2 && name[0] == CTL_KERN && name[1] == KERN_VERSION) {
++                ret = get_errno(
++                        sysctl(name, nlen, oldval, &oldlen, newval, newlen));
++                if (!is_error(ret)) {
++                    *oldlenp = tswapl(oldlen);
++                }
++            } else {
++                gemu_log("qemu: Unsupported sysctl name\n");
++                ret = -ENOSYS;
++            }
++        }
+         break;
+     case TARGET_NR_sched_setparam:
+         {
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/33_syscall_ppc_clone.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/33_syscall_ppc_clone.patch
new file mode 100644 (file)
index 0000000..3f733b6
--- /dev/null
@@ -0,0 +1,22 @@
+#DPATCHLEVEL=0
+---
+# linux-user/syscall.c |    6 +-----
+# 1 file changed, 1 insertion(+), 5 deletions(-)
+#
+Index: linux-user/syscall.c
+===================================================================
+--- linux-user/syscall.c.orig  2007-12-03 15:58:11.000000000 +0000
++++ linux-user/syscall.c       2007-12-03 15:58:46.000000000 +0000
+@@ -2750,11 +2750,7 @@ int do_fork(CPUState *env, unsigned int 
+         if (!newsp)
+             newsp = env->gpr[1];
+         new_env->gpr[1] = newsp;
+-        {
+-            int i;
+-            for (i = 7; i < 32; i++)
+-                new_env->gpr[i] = 0;
+-        }
++        new_env->gpr[3] = 0;
+ #elif defined(TARGET_SH4)
+       if (!newsp)
+         newsp = env->gregs[15];
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/39_syscall_fadvise64.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/39_syscall_fadvise64.patch
new file mode 100644 (file)
index 0000000..54ee3e0
--- /dev/null
@@ -0,0 +1,21 @@
+---
+ linux-user/syscall.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+Index: linux-user/syscall.c
+===================================================================
+--- linux-user/syscall.c.orig  2007-12-03 19:33:47.000000000 +0000
++++ linux-user/syscall.c       2007-12-03 19:33:48.000000000 +0000
+@@ -5317,6 +5317,12 @@ abi_long do_syscall(void *cpu_env, int n
+         ret = get_errno(mincore((void*)arg1, (size_t)arg2, (unsigned char*)arg3));
+         break;
+ #endif
++#ifdef TARGET_NR_fadvise64_64
++     case TARGET_NR_fadvise64_64:
++        /* Just return success */
++        ret = get_errno(0);
++        break;
++#endif
+ #ifdef TARGET_NR_madvise
+     case TARGET_NR_madvise:
+         /* A straight passthrough may not be safe because qemu sometimes
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/41_arm_fpa_sigfpe.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/41_arm_fpa_sigfpe.patch
new file mode 100644 (file)
index 0000000..cea3afc
--- /dev/null
@@ -0,0 +1,104 @@
+#DPATCHLEVEL=0
+---
+# linux-user/main.c        |   51 ++++++++++++++++++++++++++++++++++++++++++++++-
+# target-arm/nwfpe/fpa11.c |    7 ++++++
+# 2 files changed, 57 insertions(+), 1 deletion(-)
+#
+Index: linux-user/main.c
+===================================================================
+--- linux-user/main.c.orig     2007-12-03 15:59:10.000000000 +0000
++++ linux-user/main.c  2007-12-03 16:01:27.000000000 +0000
+@@ -377,18 +377,67 @@ void cpu_loop(CPUARMState *env)
+             {
+                 TaskState *ts = env->opaque;
+                 uint32_t opcode;
++                int rc;
+                 /* we handle the FPU emulation here, as Linux */
+                 /* we get the opcode */
+                 /* FIXME - what to do if get_user() fails? */
+                 get_user_u32(opcode, env->regs[15]);
+-                if (EmulateAll(opcode, &ts->fpa, env) == 0) {
++                rc = EmulateAll(opcode, &ts->fpa, env);
++                if (rc == 0) { /* illegal instruction */
+                     info.si_signo = SIGILL;
+                     info.si_errno = 0;
+                     info.si_code = TARGET_ILL_ILLOPN;
+                     info._sifields._sigfault._addr = env->regs[15];
+                     queue_signal(info.si_signo, &info);
++                } else if (rc < 0) { /* FP exception */
++                    int arm_fpe=0;
++
++                     /* translate softfloat flags to FPSR flags */
++                    if (-rc & float_flag_invalid)
++                      arm_fpe |= BIT_IOC;
++                    if (-rc & float_flag_divbyzero)
++                      arm_fpe |= BIT_DZC;
++                    if (-rc & float_flag_overflow)
++                      arm_fpe |= BIT_OFC;
++                    if (-rc & float_flag_underflow)
++                      arm_fpe |= BIT_UFC;
++                    if (-rc & float_flag_inexact)
++                      arm_fpe |= BIT_IXC;
++
++                    FPSR fpsr = ts->fpa.fpsr;
++                    //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe);
++
++                    if (fpsr & (arm_fpe << 16)) { /* exception enabled? */
++                      info.si_signo = SIGFPE;
++                      info.si_errno = 0;
++
++                      /* ordered by priority, least first */
++                      if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES;
++                      if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND;
++                      if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF;
++                      if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV;
++                      if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV;
++
++                      info._sifields._sigfault._addr = env->regs[15];
++                      queue_signal(info.si_signo, &info);
++                    } else {
++                      env->regs[15] += 4;
++                    }
++
++                    /* accumulate unenabled exceptions */
++                    if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC))
++                      fpsr |= BIT_IXC;
++                    if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC))
++                      fpsr |= BIT_UFC;
++                    if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC))
++                      fpsr |= BIT_OFC;
++                    if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC))
++                      fpsr |= BIT_DZC;
++                    if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC))
++                      fpsr |= BIT_IOC;
++                    ts->fpa.fpsr=fpsr;
+                 } else {
+                     /* increment PC */
+                     env->regs[15] += 4;
+Index: target-arm/nwfpe/fpa11.c
+===================================================================
+--- target-arm/nwfpe/fpa11.c.orig      2007-12-03 15:40:26.000000000 +0000
++++ target-arm/nwfpe/fpa11.c   2007-12-03 15:59:11.000000000 +0000
+@@ -162,6 +162,8 @@ unsigned int EmulateAll(unsigned int opc
+     fpa11->initflag = 1;
+   }
++  set_float_exception_flags(0, &fpa11->fp_status);  
++
+   if (TEST_OPCODE(opcode,MASK_CPRT))
+   {
+     //fprintf(stderr,"emulating CPRT\n");
+@@ -191,6 +193,11 @@ unsigned int EmulateAll(unsigned int opc
+   }
+ //  restore_flags(flags);
++  if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status))
++  {
++    //printf("fef 0x%x\n",float_exception_flags);
++    nRc=-get_float_exception_flags(&fpa11->fp_status);
++  }
+   //printf("returning %d\n",nRc);
+   return(nRc);
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/52_ne2000_return.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/52_ne2000_return.patch
new file mode 100644 (file)
index 0000000..e4ea33f
--- /dev/null
@@ -0,0 +1,17 @@
+---
+ hw/ne2000.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: qemu/hw/ne2000.c
+===================================================================
+--- qemu.orig/hw/ne2000.c      2007-12-03 19:32:52.000000000 +0000
++++ qemu/hw/ne2000.c   2007-12-03 19:33:55.000000000 +0000
+@@ -217,7 +217,7 @@ static int ne2000_can_receive(void *opaq
+     NE2000State *s = opaque;
+     if (s->cmd & E8390_STOP)
+-        return 1;
++        return 0;
+     return !ne2000_buffer_full(s);
+ }
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/61_safe_64bit_int.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/61_safe_64bit_int.patch
new file mode 100644 (file)
index 0000000..9b1ace8
--- /dev/null
@@ -0,0 +1,27 @@
+#DPATCHLEVEL=0
+---
+# dyngen-exec.h |    4 ++--
+# 1 file changed, 2 insertions(+), 2 deletions(-)
+#
+Index: dyngen-exec.h
+===================================================================
+--- dyngen-exec.h.orig 2007-12-31 13:06:21.000000000 +0000
++++ dyngen-exec.h      2007-12-31 13:08:54.000000000 +0000
+@@ -38,7 +38,7 @@
+ // Linux/Sparc64 defines uint64_t
+ #if !(defined (__sparc_v9__) && defined(__linux__))
+ /* XXX may be done for all 64 bits targets ? */
+-#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__) 
++#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__) || defined(__sparc__)
+ typedef unsigned long uint64_t;
+ #else
+ typedef unsigned long long uint64_t;
+@@ -55,7 +55,7 @@
+ typedef signed int int32_t;
+ // Linux/Sparc64 defines int64_t
+ #if !(defined (__sparc_v9__) && defined(__linux__))
+-#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__)
++#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__) || defined(__sparc__)
+ typedef signed long int64_t;
+ #else
+ typedef signed long long int64_t;
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/63_sparc_build.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/63_sparc_build.patch
new file mode 100644 (file)
index 0000000..37b38f6
--- /dev/null
@@ -0,0 +1,18 @@
+#DPATCHLEVEL=0
+---
+# sparc.ld |    2 +-
+# 1 file changed, 1 insertion(+), 1 deletion(-)
+#
+Index: sparc.ld
+===================================================================
+--- sparc.ld.orig      2007-12-03 15:40:26.000000000 +0000
++++ sparc.ld   2007-12-03 16:05:06.000000000 +0000
+@@ -6,7 +6,7 @@ ENTRY(_start)
+ SECTIONS
+ {
+   /* Read-only sections, merged into text segment: */
+-  . = 0x60000000 + SIZEOF_HEADERS;
++  . = 0x60000000 + 0x400;
+   .interp     : { *(.interp)    }
+   .hash          : { *(.hash)           }
+   .dynsym        : { *(.dynsym)         }
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/64_ppc_asm_constraints.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/64_ppc_asm_constraints.patch
new file mode 100644 (file)
index 0000000..e4858b7
--- /dev/null
@@ -0,0 +1,18 @@
+#DPATCHLEVEL=1
+---
+# cpu-all.h |    2 +-
+# 1 file changed, 1 insertion(+), 1 deletion(-)
+#
+Index: qemu/cpu-all.h
+===================================================================
+--- qemu.orig/cpu-all.h        2007-06-13 11:48:22.000000000 +0100
++++ qemu/cpu-all.h     2007-06-13 11:51:56.000000000 +0100
+@@ -250,7 +250,7 @@ static inline void stw_le_p(void *ptr, i
+ static inline void stl_le_p(void *ptr, int v)
+ {
+ #ifdef __powerpc__
+-    __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
++    __asm__ __volatile__ ("stwbrx %0,0,%1" : : "r" (v), "r" (ptr) : "memory");
+ #else
+     uint8_t *p = ptr;
+     p[0] = v;
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/65_kfreebsd.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/65_kfreebsd.patch
new file mode 100644 (file)
index 0000000..dfece80
--- /dev/null
@@ -0,0 +1,35 @@
+---
+ configure |    6 ++++++
+ vl.c      |    2 ++
+ 2 files changed, 8 insertions(+)
+
+Index: configure
+===================================================================
+--- configure.orig     2007-12-03 15:40:26.000000000 +0000
++++ configure  2007-12-03 16:05:34.000000000 +0000
+@@ -129,6 +129,12 @@ if [ "$cpu" = "i386" -o "$cpu" = "x86_64
+     kqemu="yes"
+ fi
+ ;;
++GNU/kFreeBSD)
++oss="yes"
++if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
++    kqemu="yes"
++fi
++;;
+ FreeBSD)
+ bsd="yes"
+ oss="yes"
+Index: vl.c
+===================================================================
+--- vl.c.orig  2007-12-03 16:05:32.000000000 +0000
++++ vl.c       2007-12-03 16:05:34.000000000 +0000
+@@ -97,6 +97,8 @@
+ #include <stropts.h>
+ #endif
+ #endif
++#elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
++#include <freebsd/stdlib.h>
+ #else
+ #include <winsock2.h>
+ int inet_aton(const char *cp, struct in_addr *ia);
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/66_tls_ld.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/66_tls_ld.patch
new file mode 100644 (file)
index 0000000..54e02ef
--- /dev/null
@@ -0,0 +1,55 @@
+---
+ arm.ld  |    7 +++++++
+ i386.ld |    7 +++++++
+ 2 files changed, 14 insertions(+)
+
+Index: arm.ld
+===================================================================
+--- arm.ld.orig        2007-06-13 11:48:22.000000000 +0100
++++ arm.ld     2007-06-13 11:51:56.000000000 +0100
+@@ -26,6 +26,10 @@ SECTIONS
+     { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+   .rela.rodata   :
+     { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
++  .rel.tdata     : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
++  .rela.tdata    : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
++  .rel.tbss      : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
++  .rela.tbss     : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+   .rel.got       : { *(.rel.got)              }
+   .rela.got      : { *(.rela.got)             }
+   .rel.ctors     : { *(.rel.ctors)    }
+@@ -58,6 +62,9 @@ SECTIONS
+   .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
+    __exidx_end = .;
+   .reginfo : { *(.reginfo) }
++  /* Thread Local Storage sections  */
++  .tdata        : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
++  .tbss                 : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+   /* Adjust the address for the data segment.  We want to adjust up to
+      the same address within the page on the next page up.  */
+   . = ALIGN(0x100000) + (. & (0x100000 - 1));
+Index: i386.ld
+===================================================================
+--- i386.ld.orig       2007-06-13 11:48:22.000000000 +0100
++++ i386.ld    2007-06-13 11:51:56.000000000 +0100
+@@ -28,6 +28,10 @@ SECTIONS
+     { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+   .rela.rodata   :
+     { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
++  .rel.tdata     : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
++  .rela.tdata    : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
++  .rel.tbss      : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
++  .rela.tbss     : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+   .rel.got       : { *(.rel.got)              }
+   .rela.got      : { *(.rela.got)             }
+   .rel.ctors     : { *(.rel.ctors)    }
+@@ -53,6 +57,9 @@ SECTIONS
+   _etext = .;
+   PROVIDE (etext = .);
+   .fini      : { *(.fini)    } =0x47ff041f
++  /* Thread Local Storage sections  */
++  .tdata        : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
++  .tbss                 : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+   . = ALIGN(32 / 8);
+   PROVIDE (__preinit_array_start = .);
+   .preinit_array     : { *(.preinit_array) }
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/91-oh-sdl-cursor.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/91-oh-sdl-cursor.patch
new file mode 100644 (file)
index 0000000..0d60c1c
--- /dev/null
@@ -0,0 +1,18 @@
+=== modified file 'sdl.c'
+---
+ sdl.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: sdl.c
+===================================================================
+--- sdl.c.orig 2007-12-03 19:32:15.000000000 +0000
++++ sdl.c      2007-12-03 19:34:04.000000000 +0000
+@@ -247,7 +247,7 @@ static void sdl_hide_cursor(void)
+     if (kbd_mouse_is_absolute()) {
+         SDL_ShowCursor(1);
+-        SDL_SetCursor(sdl_cursor_hidden);
++        /* SDL_SetCursor(sdl_cursor_hidden); */
+     } else {
+         SDL_ShowCursor(0);
+     }
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/configure_symlinkpath_fix.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/configure_symlinkpath_fix.patch
new file mode 100644 (file)
index 0000000..3ec304a
--- /dev/null
@@ -0,0 +1,28 @@
+Index: qemu-0.9.1/configure
+===================================================================
+--- qemu-0.9.1.orig/configure  2008-01-24 15:33:13.000000000 +0000
++++ qemu-0.9.1/configure       2008-01-24 15:45:50.000000000 +0000
+@@ -209,15 +209,17 @@
+ # find source path
+ source_path=`dirname "$0"`
++source_path_used="no"
++workdir=`pwd`
++workdir=`readlink -f $workdir`
+ if [ -z "$source_path" ]; then
+-    source_path=`pwd`
++    source_path=$workdir
+ else
+     source_path=`cd "$source_path"; pwd`
+-fi
+-if test "$source_path" = `pwd` ; then
+-    source_path_used="no"
+-else
+-    source_path_used="yes"
++    source_path=`readlink -f $source_path`
++    if test "$source_path" != "$workdir" ; then
++        source_path_used="yes"
++    fi
+ fi
+ werror="no"
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_brk.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_brk.patch
new file mode 100644 (file)
index 0000000..783198d
--- /dev/null
@@ -0,0 +1,55 @@
+--- qemu/linux-user/syscall.c1 (revision 16)
++++ qemu/linux-user/syscall.c  (working copy)
+@@ -441,7 +441,7 @@
+     if (!new_brk)
+         return target_brk;
+     if (new_brk < target_original_brk)
+-        return -TARGET_ENOMEM;
++        return target_brk;
+     brk_page = HOST_PAGE_ALIGN(target_brk);
+@@ -456,12 +456,11 @@
+     mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
+                                         PROT_READ|PROT_WRITE,
+                                         MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0));
+-    if (is_error(mapped_addr)) {
+-      return mapped_addr;
+-    } else {
++
++    if (!is_error(mapped_addr))
+       target_brk = new_brk;
+-      return target_brk;
+-    }
++    
++    return target_brk;
+ }
+ static inline abi_long copy_from_user_fdset(fd_set *fds,
+--- qemu/linux-user/mmap.c1    (revision 16)
++++ qemu/linux-user/mmap.c     (working copy)
+@@ -260,6 +259,9 @@
+             host_start += offset - host_offset;
+         start = h2g(host_start);
+     } else {
++        int flg;
++        target_ulong addr;
++
+         if (start & ~TARGET_PAGE_MASK) {
+             errno = EINVAL;
+             return -1;
+@@ -267,6 +269,14 @@
+         end = start + len;
+         real_end = HOST_PAGE_ALIGN(end);
+         
++        for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
++            flg = page_get_flags(addr);
++            if( flg & PAGE_RESERVED ) {
++                errno = ENXIO;
++                return -1;
++            }
++        }
++
+         /* worst case: we cannot map the file because the offset is not
+            aligned, so we read it */
+         if (!(flags & MAP_ANONYMOUS) &&
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_protection_bits.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_protection_bits.patch
new file mode 100644 (file)
index 0000000..ee2b077
--- /dev/null
@@ -0,0 +1,14 @@
+Index: qemu-0.9.1/linux-user/mmap.c
+===================================================================
+--- qemu-0.9.1.orig/linux-user/mmap.c  2008-04-16 14:10:26.000000000 +0100
++++ qemu-0.9.1/linux-user/mmap.c       2008-04-16 14:10:51.000000000 +0100
+@@ -49,8 +49,7 @@
+     end = start + len;
+     if (end < start)
+         return -EINVAL;
+-    if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
+-        return -EINVAL;
++    prot = prot & (PROT_READ | PROT_WRITE | PROT_EXEC);
+     if (len == 0)
+         return 0;
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_segfault.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/fix_segfault.patch
new file mode 100644 (file)
index 0000000..443c330
--- /dev/null
@@ -0,0 +1,37 @@
+---
+ linux-user/syscall.c |   22 ----------------------
+ 1 file changed, 22 deletions(-)
+
+Index: qemu/linux-user/syscall.c
+===================================================================
+--- qemu.orig/linux-user/syscall.c     2007-12-03 23:40:11.000000000 +0000
++++ qemu/linux-user/syscall.c  2007-12-03 23:40:21.000000000 +0000
+@@ -5695,28 +5695,6 @@ abi_long do_syscall(void *cpu_env, int n
+            goto unimplemented_nowarn;
+ #endif
+-#ifdef TARGET_NR_clock_gettime
+-    case TARGET_NR_clock_gettime:
+-    {
+-        struct timespec ts;
+-        ret = get_errno(clock_gettime(arg1, &ts));
+-        if (!is_error(ret)) {
+-            host_to_target_timespec(arg2, &ts);
+-        }
+-        break;
+-    }
+-#endif
+-#ifdef TARGET_NR_clock_getres
+-    case TARGET_NR_clock_getres:
+-    {
+-        struct timespec ts;
+-        ret = get_errno(clock_getres(arg1, &ts));
+-        if (!is_error(ret)) {
+-            host_to_target_timespec(arg2, &ts);
+-        }
+-        break;
+-    }
+-#endif
+ #if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
+     case TARGET_NR_set_tid_address:
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/no-strip.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/no-strip.patch
new file mode 100644 (file)
index 0000000..fc69b37
--- /dev/null
@@ -0,0 +1,22 @@
+--- qemu.orig/Makefile 2008-01-29 23:16:27.000000000 -0800
++++ qemu-0.9.1/Makefile        2008-01-29 23:16:38.000000000 -0800
+@@ -174,7 +174,7 @@
+ install: all $(if $(BUILD_DOCS),install-doc)
+       mkdir -p "$(DESTDIR)$(bindir)"
+ ifneq ($(TOOLS),)
+-      $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
++      $(INSTALL) -m 755 $(TOOLS) "$(DESTDIR)$(bindir)"
+ endif
+       mkdir -p "$(DESTDIR)$(datadir)"
+       for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
+--- qemu.orig/Makefile.target  2008-01-29 23:16:27.000000000 -0800
++++ qemu-0.9.1/Makefile.target 2008-01-29 23:17:33.000000000 -0800
+@@ -632,7 +632,7 @@
+ install: all
+ ifneq ($(PROGS),)
+-      $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
++      $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
+ endif
+ ifneq ($(wildcard .depend),)
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl-update.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl-update.patch
new file mode 100644 (file)
index 0000000..ebc996e
--- /dev/null
@@ -0,0 +1,219 @@
+---
+ linux-user/main.c    |    7 ++-
+ linux-user/syscall.c |  114 ++++++++++++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 111 insertions(+), 10 deletions(-)
+
+Index: qemu/linux-user/main.c
+===================================================================
+--- qemu.orig/linux-user/main.c        2007-12-03 19:34:09.000000000 +0000
++++ qemu/linux-user/main.c     2007-12-03 23:44:45.000000000 +0000
+@@ -391,7 +391,7 @@ do_kernel_trap(CPUARMState *env)
+         cpu_unlock();
+         break;
+     case 0xffff0fe0: /* __kernel_get_tls */
+-        env->regs[0] = env->cp15.c13_tls;
++        env->regs[0] = env->cp15.c13_tls2;
+         break;
+     default:
+         return 1;
+@@ -2037,6 +2037,11 @@ int main(int argc, char **argv)
+     int drop_ld_preload = 0, environ_count = 0;
+     char **target_environ, **wrk, **dst;
++    char *assume_kernel = getenv("QEMU_ASSUME_KERNEL");
++
++    if (assume_kernel)
++       setenv("LD_ASSUME_KERNEL", assume_kernel, 1);
++
+     if (argc <= 1)
+         usage();
+Index: qemu/linux-user/syscall.c
+===================================================================
+--- qemu.orig/linux-user/syscall.c     2007-12-03 19:34:09.000000000 +0000
++++ qemu/linux-user/syscall.c  2007-12-03 23:46:54.000000000 +0000
+@@ -61,6 +61,7 @@
+ #define tchars host_tchars /* same as target */
+ #define ltchars host_ltchars /* same as target */
++#include <linux/futex.h>
+ #include <linux/termios.h>
+ #include <linux/unistd.h>
+ #include <linux/utsname.h>
+@@ -2694,7 +2695,6 @@ abi_long do_arch_prctl(CPUX86State *env,
+     return 0;
+ }
+ #endif
+-
+ #endif /* defined(TARGET_I386) */
+ /* this stack is the equivalent of the kernel stack associated with a
+@@ -2729,16 +2729,19 @@ int do_fork(CPUState *env, unsigned int 
+     TaskState *ts;
+     uint8_t *new_stack;
+     CPUState *new_env;
+-
++#if defined(TARGET_I386)
++     uint64_t *new_gdt_table;
++#endif
+ #ifdef USE_NPTL
+     unsigned int nptl_flags;
+     if (flags & CLONE_PARENT_SETTID)
+         *parent_tidptr = gettid();
+ #endif
+-
+     if (flags & CLONE_VM) {
+         ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
++        if (!ts)
++          return -ENOMEM;
+         memset(ts, 0, sizeof(TaskState));
+         new_stack = ts->stack;
+         ts->used = 1;
+@@ -2750,6 +2753,29 @@ int do_fork(CPUState *env, unsigned int 
+ #if defined(TARGET_I386)
+         if (!newsp)
+             newsp = env->regs[R_ESP];
++       new_gdt_table = malloc(9 * 8);
++       if (!new_gdt_table) {
++               free(new_env);
++               return -ENOMEM;
++       }
++       /* Copy main GDT table from parent, but clear TLS entries */
++       memcpy(new_gdt_table, g2h(env->gdt.base), 6 * 8);
++       memset(&new_gdt_table[6], 0, 3 * 8); 
++       new_env->gdt.base = h2g(new_gdt_table);
++       if (flags & 0x00080000 /* CLONE_SETTLS */) {
++               ret = do_set_thread_area(new_env, new_env->regs[R_ESI]);
++               if (ret) {
++                       free(new_gdt_table);
++                       free(new_env);
++                       return ret;
++               }
++       }
++       cpu_x86_load_seg(env, R_CS, new_env->regs[R_CS]);
++       cpu_x86_load_seg(env, R_DS, new_env->regs[R_DS]);
++       cpu_x86_load_seg(env, R_ES, new_env->regs[R_ES]);
++       cpu_x86_load_seg(env, R_SS, new_env->regs[R_SS]);
++       cpu_x86_load_seg(env, R_FS, new_env->regs[R_FS]);
++       cpu_x86_load_seg(env, R_GS, new_env->regs[R_GS]);
+         new_env->regs[R_ESP] = newsp;
+         new_env->regs[R_EAX] = 0;
+ #elif defined(TARGET_ARM)
+@@ -3121,6 +3147,68 @@ static inline abi_long host_to_target_ti
+     unlock_user_struct(target_ts, target_addr, 1);
+ }
++static long do_futex(target_ulong uaddr, int op, uint32_t val,
++                    target_ulong utime, target_ulong uaddr2,
++                    uint32_t val3)
++{
++       struct timespec host_utime;
++       unsigned long val2 = utime;
++
++       if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) {
++               target_to_host_timespec(&host_utime, utime);
++               val2 = (unsigned long)&host_utime;
++       }
++ 
++#ifdef BSWAP_NEEDED
++       switch(op) {
++       case FUTEX_CMP_REQUEUE:
++               val3 = tswap32(val3);
++       case FUTEX_REQUEUE:
++               val2 = tswap32(val2);
++       case FUTEX_WAIT:
++       case FUTEX_WAKE:
++               val = tswap32(val);
++       case FUTEX_LOCK_PI: /* This one's icky, but comes out OK */
++       case FUTEX_UNLOCK_PI:
++               break;
++       default: 
++               gemu_log("qemu: Unsupported futex op %d\n", op);
++               return -ENOSYS;
++       } 
++#if 0 /* No, it's worse than this */
++       if (op == FUTEX_WAKE_OP) {
++               /* Need to munge the secondary operation (val3) */
++               val3 = tswap32(val3);
++               int op2 = (val3 >> 28) & 7;
++               int cmp = (val3 >> 24) & 15;
++               int oparg = (val3 << 8) >> 20;
++               int cmparg = (val3 << 20) >> 20;
++               int shift = val3 & (FUTEX_OP_OPARG_SHIFT << 28);
++
++               if (shift)
++                   oparg = (oparg & 7) + 24 - (oparg & 24);
++               else oparg = 
++               if (op2 == FUTEX_OP_ADD) {
++                       gemu_log("qemu: Unsupported wrong-endian FUTEX_OP_ADD\n");
++                       return -ENOSYS;
++               }
++               if (cmparg == FUTEX_OP_CMP_LT || cmparg == FUTEX_OP_CMP_GE ||
++                   cmparg == FUTEX_OP_CMP_LE || cmparg == FUTEX_OP_CMP_GT) {
++                       gemu_log("qemu: Unsupported wrong-endian futex cmparg %d\n", cmparg);
++                       return -ENOSYS;
++               }
++               val3 = shift | (op2<<28) | (cmp<<24) | (oparg<<12) | cmparg;
++       }
++#endif
++#endif
++       return syscall(__NR_futex, g2h(uaddr), op, val, val2, g2h(uaddr2), val3);
++}
++
++int do_set_tid_address(target_ulong tidptr)
++{
++       return syscall(__NR_set_tid_address, g2h(tidptr));
++}
++
+ /* do_syscall() should always have a single exit point at the end so
+    that actions, such as logging of syscall results, can be performed.
+    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
+@@ -3145,7 +3233,7 @@ abi_long do_syscall(void *cpu_env, int n
+         _mcleanup();
+ #endif
+         gdb_exit(cpu_env, arg1);
+-        /* XXX: should free thread stack and CPU env */
++        /* XXX: should free thread stack, GDT and CPU env */
+         _exit(arg1);
+         ret = 0; /* avoid warning */
+         break;
+@@ -5569,6 +5657,9 @@ abi_long do_syscall(void *cpu_env, int n
+ #elif defined(TARGET_I386) && defined(TARGET_ABI32)
+       ret = do_set_thread_area(cpu_env, arg1);
+       break;
++#elif TARGET_i386
++        ret = get_errno(do_set_thread_area(cpu_env, arg1));
++        break;
+ #else
+       goto unimplemented_nowarn;
+ #endif
+@@ -5586,6 +5677,16 @@ abi_long do_syscall(void *cpu_env, int n
+         goto unimplemented_nowarn;
+ #endif
++#ifdef TARGET_NR_futex
++    case TARGET_NR_futex:
++       ret = get_errno(do_futex(arg1, arg2, arg3, arg4, arg5, arg6));
++       break;
++#endif
++#ifdef TARGET_NR_set_robust_list
++    case TARGET_NR_set_robust_list:
++           goto unimplemented_nowarn;
++#endif
++
+ #ifdef TARGET_NR_clock_gettime
+     case TARGET_NR_clock_gettime:
+     {
+@@ -5627,11 +5728,6 @@ abi_long do_syscall(void *cpu_env, int n
+       break;
+ #endif
+-#ifdef TARGET_NR_set_robust_list
+-    case TARGET_NR_set_robust_list:
+-      goto unimplemented_nowarn;
+-#endif
+-
+ #if defined(TARGET_NR_utimensat) && defined(__NR_utimensat)
+     case TARGET_NR_utimensat:
+         {
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-0.9.0-nptl.patch
new file mode 100644 (file)
index 0000000..4a87d8d
--- /dev/null
@@ -0,0 +1,854 @@
+These are Paul Brook's patches to QEMU-0.8.2 to enable the running of single
+ARM binaries under QEMU's user-emulation mode. Without them, QEMU-0.8.1
+immediately dies saying:
+       Error: f0005
+       qemu: uncaught target signal 6 (Aborted) - exiting
+while qemu-0.8.2 dies saying:
+       qemu: Unsupported syscall: 983045
+       cannot set up thread-local storage: unknown error
+
+This file is a rediffing of the patches visible at
+https://nowt.dyndns.org/patch.qemu_nptl on 27 Sept 2006
+which "patch" fails to apply automatically.
+See also http://lists.gnu.org/archive/html/qemu-devel/2006-09/msg00194.html
+
+       Martin Guy, 27 Sept 2006
+
+---
+ configure                |   25 ++++++
+ exec-all.h               |  165 ------------------------------------------
+ linux-user/arm/syscall.h |    4 -
+ linux-user/main.c        |   94 +++++++++++++++++++++---
+ linux-user/qemu.h        |    3 
+ linux-user/syscall.c     |   91 ++++++++++++++++++++++-
+ qemu_spinlock.h          |  181 +++++++++++++++++++++++++++++++++++++++++++++++
+ target-arm/cpu.h         |   10 ++
+ target-arm/op.c          |    6 +
+ target-arm/translate.c   |    9 ++
+ 10 files changed, 405 insertions(+), 183 deletions(-)
+
+Index: qemu/configure
+===================================================================
+--- qemu.orig/configure        2008-04-09 23:02:37.000000000 +0100
++++ qemu/configure     2008-04-09 23:06:36.000000000 +0100
+@@ -109,6 +109,7 @@
+ build_docs="no"
+ uname_release=""
+ curses="yes"
++nptl="yes"
+ # OS specific
+ targetos=`uname -s`
+@@ -334,6 +335,8 @@
+   ;;
+   *) echo "ERROR: unknown option $opt"; show_help="yes"
+   ;;
++  --disable-nptl) nptl="no"
++  ;;
+   esac
+ done
+@@ -429,6 +432,7 @@
+ echo "  --disable-linux-user     disable all linux usermode emulation targets"
+ echo "  --enable-darwin-user     enable all darwin usermode emulation targets"
+ echo "  --disable-darwin-user    disable all darwin usermode emulation targets"
++echo "  --disable-nptl           disable usermode NPTL guest support"
+ echo "  --fmod-lib               path to FMOD library"
+ echo "  --fmod-inc               path to FMOD includes"
+ echo "  --enable-uname-release=R Return R for uname -r in usermode emulation"
+@@ -595,6 +599,23 @@
+ }
+ EOF
++# check NPTL support
++cat > $TMPC <<EOF
++#include <sched.h>
++void foo()
++{
++#ifndef CLONE_SETTLS
++#error bork
++#endif
++}
++EOF
++
++if $cc -c -o $TMPO $TMPC 2> /dev/null ; then
++  :
++else
++   nptl="no"
++fi
++
+ ##########################################
+ # SDL probe
+@@ -778,6 +799,7 @@
+ echo "Documentation     $build_docs"
+ [ ! -z "$uname_release" ] && \
+ echo "uname -r          $uname_release"
++echo "NPTL support      $nptl"
+ if test $sdl_too_old = "yes"; then
+ echo "-> Your SDL version is too old - please upgrade to have SDL support"
+@@ -1115,6 +1137,9 @@
+   echo "TARGET_ARCH=arm" >> $config_mak
+   echo "#define TARGET_ARCH \"arm\"" >> $config_h
+   echo "#define TARGET_ARM 1" >> $config_h
++  if test "$nptl" = "yes" ; then
++        echo "#define USE_NPTL 1" >> $config_h
++  fi
+   bflt="yes"
+ elif test "$target_cpu" = "sparc" ; then
+   echo "TARGET_ARCH=sparc" >> $config_mak
+Index: qemu/exec-all.h
+===================================================================
+--- qemu.orig/exec-all.h       2008-04-09 22:39:38.000000000 +0100
++++ qemu/exec-all.h    2008-04-09 23:05:55.000000000 +0100
+@@ -297,170 +297,7 @@
+ extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+ extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+-#if defined(__powerpc__)
+-static inline int testandset (int *p)
+-{
+-    int ret;
+-    __asm__ __volatile__ (
+-                          "0:    lwarx %0,0,%1\n"
+-                          "      xor. %0,%3,%0\n"
+-                          "      bne 1f\n"
+-                          "      stwcx. %2,0,%1\n"
+-                          "      bne- 0b\n"
+-                          "1:    "
+-                          : "=&r" (ret)
+-                          : "r" (p), "r" (1), "r" (0)
+-                          : "cr0", "memory");
+-    return ret;
+-}
+-#elif defined(__i386__)
+-static inline int testandset (int *p)
+-{
+-    long int readval = 0;
+-
+-    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+-                          : "+m" (*p), "+a" (readval)
+-                          : "r" (1)
+-                          : "cc");
+-    return readval;
+-}
+-#elif defined(__x86_64__)
+-static inline int testandset (int *p)
+-{
+-    long int readval = 0;
+-
+-    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+-                          : "+m" (*p), "+a" (readval)
+-                          : "r" (1)
+-                          : "cc");
+-    return readval;
+-}
+-#elif defined(__s390__)
+-static inline int testandset (int *p)
+-{
+-    int ret;
+-
+-    __asm__ __volatile__ ("0: cs    %0,%1,0(%2)\n"
+-                        "   jl    0b"
+-                        : "=&d" (ret)
+-                        : "r" (1), "a" (p), "0" (*p)
+-                        : "cc", "memory" );
+-    return ret;
+-}
+-#elif defined(__alpha__)
+-static inline int testandset (int *p)
+-{
+-    int ret;
+-    unsigned long one;
+-
+-    __asm__ __volatile__ ("0: mov 1,%2\n"
+-                        "     ldl_l %0,%1\n"
+-                        "     stl_c %2,%1\n"
+-                        "     beq %2,1f\n"
+-                        ".subsection 2\n"
+-                        "1:   br 0b\n"
+-                        ".previous"
+-                        : "=r" (ret), "=m" (*p), "=r" (one)
+-                        : "m" (*p));
+-    return ret;
+-}
+-#elif defined(__sparc__)
+-static inline int testandset (int *p)
+-{
+-      int ret;
+-
+-      __asm__ __volatile__("ldstub    [%1], %0"
+-                           : "=r" (ret)
+-                           : "r" (p)
+-                           : "memory");
+-
+-      return (ret ? 1 : 0);
+-}
+-#elif defined(__arm__)
+-static inline int testandset (int *spinlock)
+-{
+-    register unsigned int ret;
+-    __asm__ __volatile__("swp %0, %1, [%2]"
+-                         : "=r"(ret)
+-                         : "0"(1), "r"(spinlock));
+-
+-    return ret;
+-}
+-#elif defined(__mc68000)
+-static inline int testandset (int *p)
+-{
+-    char ret;
+-    __asm__ __volatile__("tas %1; sne %0"
+-                         : "=r" (ret)
+-                         : "m" (p)
+-                         : "cc","memory");
+-    return ret;
+-}
+-#elif defined(__ia64)
+-
+-#include <ia64intrin.h>
+-
+-static inline int testandset (int *p)
+-{
+-    return __sync_lock_test_and_set (p, 1);
+-}
+-#elif defined(__mips__)
+-static inline int testandset (int *p)
+-{
+-    int ret;
+-
+-    __asm__ __volatile__ (
+-      "       .set push               \n"
+-      "       .set noat               \n"
+-      "       .set mips2              \n"
+-      "1:     li      $1, 1           \n"
+-      "       ll      %0, %1          \n"
+-      "       sc      $1, %1          \n"
+-      "       beqz    $1, 1b          \n"
+-      "       .set pop                "
+-      : "=r" (ret), "+R" (*p)
+-      :
+-      : "memory");
+-
+-    return ret;
+-}
+-#else
+-#error unimplemented CPU support
+-#endif
+-
+-typedef int spinlock_t;
+-
+-#define SPIN_LOCK_UNLOCKED 0
+-
+-#if defined(CONFIG_USER_ONLY)
+-static inline void spin_lock(spinlock_t *lock)
+-{
+-    while (testandset(lock));
+-}
+-
+-static inline void spin_unlock(spinlock_t *lock)
+-{
+-    *lock = 0;
+-}
+-
+-static inline int spin_trylock(spinlock_t *lock)
+-{
+-    return !testandset(lock);
+-}
+-#else
+-static inline void spin_lock(spinlock_t *lock)
+-{
+-}
+-
+-static inline void spin_unlock(spinlock_t *lock)
+-{
+-}
+-
+-static inline int spin_trylock(spinlock_t *lock)
+-{
+-    return 1;
+-}
+-#endif
++#include "qemu_spinlock.h"
+ extern spinlock_t tb_lock;
+Index: qemu/linux-user/arm/syscall.h
+===================================================================
+--- qemu.orig/linux-user/arm/syscall.h 2007-11-27 12:09:33.000000000 +0000
++++ qemu/linux-user/arm/syscall.h      2008-04-09 23:05:55.000000000 +0100
+@@ -28,7 +28,9 @@
+ #define ARM_SYSCALL_BASE      0x900000
+ #define ARM_THUMB_SYSCALL     0
+-#define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2)
++#define ARM_NR_BASE     0xf0000
++#define ARM_NR_cacheflush (ARM_NR_BASE + 2)
++#define ARM_NR_set_tls          (ARM_NR_BASE + 5)
+ #define ARM_NR_semihosting      0x123456
+ #define ARM_NR_thumb_semihosting  0xAB
+Index: qemu/linux-user/main.c
+===================================================================
+--- qemu.orig/linux-user/main.c        2008-04-09 23:02:37.000000000 +0100
++++ qemu/linux-user/main.c     2008-04-09 23:05:55.000000000 +0100
+@@ -364,6 +364,50 @@
+     }
+ }
++/* Handle a jump to the kernel code page.  */
++static int
++do_kernel_trap(CPUARMState *env)
++{
++    uint32_t addr;
++    uint32_t *ptr;
++    uint32_t cpsr;
++
++    switch (env->regs[15]) {
++    case 0xffff0fc0: /* __kernel_cmpxchg */
++        /* XXX: This only works between threads, not between processes.
++           Use native atomic operations.  */
++        /* ??? This probably breaks horribly if the access segfaults.  */
++        cpu_lock();
++        ptr = (uint32_t *)env->regs[2];
++        cpsr = cpsr_read(env);
++        if (*ptr == env->regs[0]) {
++            *ptr = env->regs[1];
++            env->regs[0] = 0;
++            cpsr |= CPSR_C;
++        } else {
++            env->regs[0] = -1;
++            cpsr &= ~CPSR_C;
++        }
++        cpsr_write(env, cpsr, CPSR_C);
++        cpu_unlock();
++        break;
++    case 0xffff0fe0: /* __kernel_get_tls */
++        env->regs[0] = env->cp15.c13_tls;
++        break;
++    default:
++        return 1;
++    }
++    /* Jump back to the caller.  */
++    addr = env->regs[14];
++    if (addr & 1) {
++        env->thumb = 1;
++        addr &= ~1;
++    }
++    env->regs[15] = addr;
++
++    return 0;
++}
++
+ void cpu_loop(CPUARMState *env)
+ {
+     int trapnr;
+@@ -474,10 +518,8 @@
+                     }
+                 }
+-                if (n == ARM_NR_cacheflush) {
+-                    arm_cache_flush(env->regs[0], env->regs[1]);
+-                } else if (n == ARM_NR_semihosting
+-                           || n == ARM_NR_thumb_semihosting) {
++                if (n == ARM_NR_semihosting
++                    || n == ARM_NR_thumb_semihosting) {
+                     env->regs[0] = do_arm_semihosting (env);
+                 } else if (n == 0 || n >= ARM_SYSCALL_BASE
+                            || (env->thumb && n == ARM_THUMB_SYSCALL)) {
+@@ -488,14 +530,34 @@
+                         n -= ARM_SYSCALL_BASE;
+                         env->eabi = 0;
+                     }
+-                    env->regs[0] = do_syscall(env,
+-                                              n,
+-                                              env->regs[0],
+-                                              env->regs[1],
+-                                              env->regs[2],
+-                                              env->regs[3],
+-                                              env->regs[4],
+-                                              env->regs[5]);
++                    if ( n > ARM_NR_BASE) {
++                        switch (n)
++                          {
++                          case ARM_NR_cacheflush:
++                              arm_cache_flush(env->regs[0], env->regs[1]);
++                              break;
++#ifdef USE_NPTL
++                          case ARM_NR_set_tls:
++                              cpu_set_tls(env, env->regs[0]);
++                              env->regs[0] = 0;
++                              break;
++#endif
++                          default:
++                              printf ("Error: Bad syscall: %x\n", n);
++                              goto error;
++                          }
++                      }
++                    else
++                      {
++                        env->regs[0] = do_syscall(env,
++                                                  n,
++                                                  env->regs[0],
++                                                  env->regs[1],
++                                                  env->regs[2],
++                                                  env->regs[3],
++                                                  env->regs[4],
++                                                  env->regs[5]);
++                      }
+                 } else {
+                     goto error;
+                 }
+@@ -534,6 +596,10 @@
+                   }
+             }
+             break;
++        case EXCP_KERNEL_TRAP:
++            if (do_kernel_trap(env))
++              goto error;
++            break;
+         default:
+         error:
+             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+@@ -2402,6 +2468,10 @@
+     ts->heap_base = info->brk;
+     /* This will be filled in on the first SYS_HEAPINFO call.  */
+     ts->heap_limit = 0;
++    /* Register the magic kernel code page.  The cpu will generate a
++       special exception when it tries to execute code here.  We can't
++       put real code here because it may be in use by the host kernel.  */
++    page_set_flags(0xffff0000, 0xffff0fff, 0);
+ #endif
+     if (gdbstub_port) {
+Index: qemu/linux-user/qemu.h
+===================================================================
+--- qemu.orig/linux-user/qemu.h        2008-01-02 15:48:21.000000000 +0000
++++ qemu/linux-user/qemu.h     2008-04-09 23:05:55.000000000 +0100
+@@ -107,6 +107,9 @@
+     uint32_t heap_base;
+     uint32_t heap_limit;
+ #endif
++#ifdef USE_NPTL
++    uint32_t *child_tidptr;
++#endif
+     int used; /* non zero if used */
+     struct image_info *info;
+     uint8_t stack[0];
+Index: qemu/linux-user/syscall.c
+===================================================================
+--- qemu.orig/linux-user/syscall.c     2008-04-09 23:02:38.000000000 +0100
++++ qemu/linux-user/syscall.c  2008-04-09 23:05:55.000000000 +0100
+@@ -71,9 +71,18 @@
+ #include <linux/kd.h>
+ #include "qemu.h"
++#include "qemu_spinlock.h"
+ //#define DEBUG
++#ifdef USE_NPTL
++#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
++    CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
++#else
++/* XXX: Hardcode the above values.  */
++#define CLONE_NPTL_FLAGS2 0
++#endif
++
+ #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \
+     || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS)
+ /* 16 bit uid wrappers emulation */
+@@ -2702,9 +2711,19 @@
+    thread/process */
+ #define NEW_STACK_SIZE 8192
++#ifdef USE_NPTL
++static spinlock_t nptl_lock = SPIN_LOCK_UNLOCKED;
++#endif
++
+ static int clone_func(void *arg)
+ {
+     CPUState *env = arg;
++#ifdef HAVE_NPTL
++    /* Wait until the parent has finshed initializing the tls state.  */
++    while (!spin_trylock(&nptl_lock))
++        usleep(1);
++    spin_unlock(&nptl_lock);
++#endif
+     cpu_loop(env);
+     /* never exits */
+     return 0;
+@@ -2712,13 +2731,22 @@
+ /* do_fork() Must return host values and target errnos (unlike most
+    do_*() functions). */
+-int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp)
++int do_fork(CPUState *env, unsigned int flags, unsigned long newsp,
++            uint32_t *parent_tidptr, void *newtls,
++            uint32_t *child_tidptr)
+ {
+     int ret;
+     TaskState *ts;
+     uint8_t *new_stack;
+     CPUState *new_env;
++#ifdef USE_NPTL
++    unsigned int nptl_flags;
++
++    if (flags & CLONE_PARENT_SETTID)
++        *parent_tidptr = gettid();
++#endif
++
+     if (flags & CLONE_VM) {
+         ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
+         memset(ts, 0, sizeof(TaskState));
+@@ -2784,16 +2812,67 @@
+ #error unsupported target CPU
+ #endif
+         new_env->opaque = ts;
++#ifdef USE_NPTL
++        nptl_flags = flags;
++        flags &= ~CLONE_NPTL_FLAGS2;
++
++        if (nptl_flags & CLONE_CHILD_CLEARTID) {
++            ts->child_tidptr = child_tidptr;
++        }
++
++        if (nptl_flags & CLONE_SETTLS)
++            cpu_set_tls (new_env, newtls);
++
++        /* Grab the global cpu lock so that the thread setup appears
++           atomic.  */
++        if (nptl_flags & CLONE_CHILD_SETTID)
++            spin_lock(&nptl_lock);
++
++#else
++        if (flags & CLONE_NPTL_FLAGS2)
++            return -EINVAL;
++#endif
++
++       if (CLONE_VFORK & flags)
++              flags ^= CLONE_VM;
+ #ifdef __ia64__
+         ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+ #else
+       ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+ #endif
++#ifdef USE_NPTL
++        if (ret != -1) {
++            if (nptl_flags & CLONE_CHILD_SETTID)
++                *child_tidptr = ret;
++        }
++
++        /* Allow the child to continue.  */
++        if (nptl_flags & CLONE_CHILD_SETTID)
++            spin_unlock(&nptl_lock);
++#endif
+     } else {
+         /* if no CLONE_VM, we consider it is a fork */
+-        if ((flags & ~CSIGNAL) != 0)
++        if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0)
+             return -EINVAL;
+         ret = fork();
++#ifdef USE_NPTL
++        /* There is a race condition here.  The parent process could
++           theoretically read the TID in the child process before the child
++           tid is set.  This would require using either ptrace
++           (not implemented) or having *_tidptr to point at a shared memory
++           mapping.  We can't repeat the spinlock hack used above because
++           the child process gets its own copy of the lock.  */
++        if (ret == 0) {
++            /* Child Process.  */
++            if (flags & CLONE_CHILD_SETTID)
++                *child_tidptr = gettid();
++            ts = (TaskState *)env->opaque;
++            if (flags & CLONE_CHILD_CLEARTID)
++                ts->child_tidptr = child_tidptr;
++            if (flags & CLONE_SETTLS)
++                cpu_set_tls (env, newtls);
++        }
++#endif
+     }
+     return ret;
+ }
+@@ -3118,7 +3197,7 @@
+         ret = do_brk(arg1);
+         break;
+     case TARGET_NR_fork:
+-        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
++        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, NULL, NULL, NULL));
+         break;
+ #ifdef TARGET_NR_waitpid
+     case TARGET_NR_waitpid:
+@@ -4481,7 +4560,8 @@
+         ret = get_errno(fsync(arg1));
+         break;
+     case TARGET_NR_clone:
+-        ret = get_errno(do_fork(cpu_env, arg1, arg2));
++        ret = get_errno(do_fork(cpu_env, arg1, arg2, (uint32_t *)arg3,
++                        (void *)arg4, (uint32_t *)arg5));
+         break;
+ #ifdef __NR_exit_group
+         /* new thread calls */
+@@ -4928,7 +5008,8 @@
+ #endif
+ #ifdef TARGET_NR_vfork
+     case TARGET_NR_vfork:
+-        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
++        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
++                                NULL, NULL, NULL));
+         break;
+ #endif
+ #ifdef TARGET_NR_ugetrlimit
+Index: qemu/qemu_spinlock.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ qemu/qemu_spinlock.h       2008-04-09 23:05:55.000000000 +0100
+@@ -0,0 +1,181 @@
++/*
++ * Atomic operation helper include
++ *
++ *  Copyright (c) 2005 Fabrice Bellard
++ *
++ * 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
++ */
++#ifndef QEMU_SPINLOCK_H
++#define QEMU_SPINLOCK_H
++
++#ifdef __powerpc__
++static inline int testandset (int *p)
++{
++    int ret;
++    __asm__ __volatile__ (
++                          "0:    lwarx %0,0,%1\n"
++                          "      xor. %0,%3,%0\n"
++                          "      bne 1f\n"
++                          "      stwcx. %2,0,%1\n"
++                          "      bne- 0b\n"
++                          "1:    "
++                          : "=&r" (ret)
++                          : "r" (p), "r" (1), "r" (0)
++                          : "cr0", "memory");
++    return ret;
++}
++#endif
++
++#ifdef __i386__
++static inline int testandset (int *p)
++{
++    long int readval = 0;
++
++    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
++                          : "+m" (*p), "+a" (readval)
++                          : "r" (1)
++                          : "cc");
++    return readval;
++}
++#endif
++
++#ifdef __x86_64__
++static inline int testandset (int *p)
++{
++    long int readval = 0;
++
++    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
++                          : "+m" (*p), "+a" (readval)
++                          : "r" (1)
++                          : "cc");
++    return readval;
++}
++#endif
++
++#ifdef __s390__
++static inline int testandset (int *p)
++{
++    int ret;
++
++    __asm__ __volatile__ ("0: cs    %0,%1,0(%2)\n"
++                        "   jl    0b"
++                        : "=&d" (ret)
++                        : "r" (1), "a" (p), "0" (*p)
++                        : "cc", "memory" );
++    return ret;
++}
++#endif
++
++#ifdef __alpha__
++static inline int testandset (int *p)
++{
++    int ret;
++    unsigned long one;
++
++    __asm__ __volatile__ ("0: mov 1,%2\n"
++                        "     ldl_l %0,%1\n"
++                        "     stl_c %2,%1\n"
++                        "     beq %2,1f\n"
++                        ".subsection 2\n"
++                        "1:   br 0b\n"
++                        ".previous"
++                        : "=r" (ret), "=m" (*p), "=r" (one)
++                        : "m" (*p));
++    return ret;
++}
++#endif
++
++#ifdef __sparc__
++static inline int testandset (int *p)
++{
++      int ret;
++
++      __asm__ __volatile__("ldstub    [%1], %0"
++                           : "=r" (ret)
++                           : "r" (p)
++                           : "memory");
++
++      return (ret ? 1 : 0);
++}
++#endif
++
++#ifdef __arm__
++static inline int testandset (int *spinlock)
++{
++    register unsigned int ret;
++    __asm__ __volatile__("swp %0, %1, [%2]"
++                         : "=r"(ret)
++                         : "0"(1), "r"(spinlock));
++
++    return ret;
++}
++#endif
++
++#ifdef __mc68000
++static inline int testandset (int *p)
++{
++    char ret;
++    __asm__ __volatile__("tas %1; sne %0"
++                         : "=r" (ret)
++                         : "m" (p)
++                         : "cc","memory");
++    return ret;
++}
++#endif
++
++#ifdef __ia64
++#include <ia64intrin.h>
++
++static inline int testandset (int *p)
++{
++    return __sync_lock_test_and_set (p, 1);
++}
++#endif
++
++typedef int spinlock_t;
++
++#define SPIN_LOCK_UNLOCKED 0
++
++#if defined(CONFIG_USER_ONLY)
++static inline void spin_lock(spinlock_t *lock)
++{
++    while (testandset(lock));
++}
++
++static inline void spin_unlock(spinlock_t *lock)
++{
++    *lock = 0;
++}
++
++static inline int spin_trylock(spinlock_t *lock)
++{
++    return !testandset(lock);
++}
++#else
++static inline void spin_lock(spinlock_t *lock)
++{
++}
++
++static inline void spin_unlock(spinlock_t *lock)
++{
++}
++
++static inline int spin_trylock(spinlock_t *lock)
++{
++    return 1;
++}
++#endif
++
++#endif
+Index: qemu/target-arm/cpu.h
+===================================================================
+--- qemu.orig/target-arm/cpu.h 2007-11-27 12:09:57.000000000 +0000
++++ qemu/target-arm/cpu.h      2008-04-09 23:05:55.000000000 +0100
+@@ -38,6 +38,7 @@
+ #define EXCP_FIQ             6
+ #define EXCP_BKPT            7
+ #define EXCP_EXCEPTION_EXIT  8   /* Return from v7M exception.  */
++#define EXCP_KERNEL_TRAP     9   /* Jumped to kernel code page.  */
+ #define ARMV7M_EXCP_RESET   1
+ #define ARMV7M_EXCP_NMI     2
+@@ -222,6 +223,15 @@
+ void cpu_lock(void);
+ void cpu_unlock(void);
++void cpu_lock(void);
++void cpu_unlock(void);
++#if defined(USE_NPTL)
++static inline void cpu_set_tls(CPUARMState *env, void *newtls)
++{
++  env->cp15.c13_tls2 = (uint32_t)(long)newtls;
++}
++#endif
++
+ #define CPSR_M (0x1f)
+ #define CPSR_T (1 << 5)
+ #define CPSR_F (1 << 6)
+Index: qemu/target-arm/op.c
+===================================================================
+--- qemu.orig/target-arm/op.c  2008-04-09 22:40:01.000000000 +0100
++++ qemu/target-arm/op.c       2008-04-09 23:05:55.000000000 +0100
+@@ -994,6 +994,12 @@
+     cpu_loop_exit();
+ }
++void OPPROTO op_kernel_trap(void)
++{
++    env->exception_index = EXCP_KERNEL_TRAP;
++    cpu_loop_exit();
++}
++
+ /* VFP support.  We follow the convention used for VFP instrunctions:
+    Single precition routines have a "s" suffix, double precision a
+    "d" suffix.  */
+Index: qemu/target-arm/translate.c
+===================================================================
+--- qemu.orig/target-arm/translate.c   2008-04-09 22:40:01.000000000 +0100
++++ qemu/target-arm/translate.c        2008-04-09 23:05:55.000000000 +0100
+@@ -7496,7 +7496,14 @@
+             gen_op_exception_exit();
+         }
+ #endif
+-
++#ifdef CONFIG_USER_ONLY
++        /* Intercept jump to the magic kernel page.  */
++        if (dc->pc > 0xffff0000) {
++            gen_op_kernel_trap();
++            dc->is_jmp = DISAS_UPDATE;
++            break;
++        }
++#endif
+         if (env->nb_breakpoints > 0) {
+             for(j = 0; j < env->nb_breakpoints; j++) {
+                 if (env->breakpoints[j] == dc->pc) {
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-amd64-32b-mapping-0.9.0.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-amd64-32b-mapping-0.9.0.patch
new file mode 100644 (file)
index 0000000..c7f36d8
--- /dev/null
@@ -0,0 +1,37 @@
+---
+ linux-user/mmap.c |    8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+Index: qemu/linux-user/mmap.c
+===================================================================
+--- qemu.orig/linux-user/mmap.c        2007-12-03 15:40:25.000000000 +0000
++++ qemu/linux-user/mmap.c     2007-12-03 16:37:21.000000000 +0000
+@@ -29,6 +29,10 @@
+ //#define DEBUG_MMAP
++#ifndef MAP_32BIT
++#define MAP_32BIT 0
++#endif
++
+ /* NOTE: all the constants are the HOST ones, but addresses are target. */
+ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
+ {
+@@ -251,7 +255,7 @@ abi_long target_mmap(abi_ulong start, ab
+            especially important if qemu_host_page_size >
+            qemu_real_host_page_size */
+         p = mmap(g2h(mmap_start),
+-                 host_len, prot, flags | MAP_FIXED, fd, host_offset);
++                 host_len, prot, flags | MAP_FIXED | MAP_32BIT, fd, host_offset);
+         if (p == MAP_FAILED)
+             return -1;
+         /* update start so that it points to the file position at 'offset' */
+@@ -406,7 +410,7 @@ abi_long target_mremap(abi_ulong old_add
+     unsigned long host_addr;
+     /* XXX: use 5 args syscall */
+-    host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
++    host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags | MAP_32BIT);
+     if (host_addr == -1)
+         return -1;
+     new_addr = h2g(host_addr);
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-n800-support.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/qemu-n800-support.patch
new file mode 100644 (file)
index 0000000..b1b6649
--- /dev/null
@@ -0,0 +1,13970 @@
+diff --git a/Makefile b/Makefile
+index c36a978..cb0cf7b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -51,7 +51,8 @@ OBJS+=block.o
+ OBJS+=irq.o
+ OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
+-OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
++OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
++OBJS+=tmp105.o
+ OBJS+=scsi-disk.o cdrom.o
+ OBJS+=scsi-generic.o
+ OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
+diff --git a/Makefile.target b/Makefile.target
+index d1deda1..48f31bc 100644
+--- a/Makefile.target
++++ b/Makefile.target
+@@ -593,7 +593,9 @@ OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
+ OBJS+= pflash_cfi01.o gumstix.o
+ OBJS+= spitz.o ide.o serial.o nand.o ecc.o
+ OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o
++OBJS+= omap2.o omap_dss.o
+ OBJS+= palm.o tsc210x.o
++OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o
+ OBJS+= mst_fpga.o mainstone.o
+ CPPFLAGS += -DHAS_AUDIO
+ endif
+diff --git a/console.h b/console.h
+index b8a5c6d..b45974e 100644
+--- a/console.h
++++ b/console.h
+@@ -32,6 +32,12 @@ void kbd_put_keycode(int keycode);
+ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state);
+ int kbd_mouse_is_absolute(void);
++struct mouse_transform_info_s {
++    int x;
++    int y;
++    int a[7];
++};
++
+ void do_info_mice(void);
+ void do_mouse_set(int index);
+diff --git a/cpu-all.h b/cpu-all.h
+index 7a7e655..c7c9611 100644
+--- a/cpu-all.h
++++ b/cpu-all.h
+@@ -810,7 +810,7 @@ extern uint8_t *phys_ram_dirty;
+ /* physical memory access */
+ #define TLB_INVALID_MASK   (1 << 3)
+ #define IO_MEM_SHIFT       4
+-#define IO_MEM_NB_ENTRIES  (1 << (TARGET_PAGE_BITS  - IO_MEM_SHIFT))
++#define IO_MEM_NB_ENTRIES  (16 << (TARGET_PAGE_BITS  - IO_MEM_SHIFT))
+ #define IO_MEM_RAM         (0 << IO_MEM_SHIFT) /* hardcoded offset */
+ #define IO_MEM_ROM         (1 << IO_MEM_SHIFT) /* hardcoded offset */
+diff --git a/exec.c b/exec.c
+index e9a5918..c69f742 100644
+--- a/exec.c
++++ b/exec.c
+@@ -1658,7 +1658,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+     {
+         if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+             /* IO memory case */
+-            address = vaddr | pd;
++            address = vaddr | (pd & ~TARGET_PAGE_MASK);
+             addend = paddr;
+         } else {
+             /* standard memory */
+@@ -1692,7 +1692,9 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+         } else {
+             te->addr_read = -1;
+         }
+-        if (prot & PAGE_EXEC) {
++        if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
++            te->addr_code = pd;
++        } else if (prot & PAGE_EXEC) {
+             te->addr_code = address;
+         } else {
+             te->addr_code = -1;
+@@ -2487,7 +2489,9 @@ int cpu_register_io_memory(int io_index,
+     if (io_index <= 0) {
+         if (io_mem_nb >= IO_MEM_NB_ENTRIES)
+             return -1;
+-        io_index = io_mem_nb++;
++        do io_index = io_mem_nb++;
++        while (((io_index << IO_MEM_SHIFT) & ~TARGET_PAGE_MASK)
++               <= IO_MEM_NOTDIRTY);
+     } else {
+         if (io_index >= IO_MEM_NB_ENTRIES)
+             return -1;
+diff --git a/hw/arm-misc.h b/hw/arm-misc.h
+index 7914ff1..a1e0061 100644
+--- a/hw/arm-misc.h
++++ b/hw/arm-misc.h
+@@ -21,10 +21,7 @@ qemu_irq *armv7m_init(int flash_size, int sram_size,
+                       const char *kernel_filename, const char *cpu_model);
+ /* arm_boot.c */
+-
+-void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
+-                     const char *kernel_cmdline, const char *initrd_filename,
+-                     int board_id, target_phys_addr_t loader_start);
++void arm_load_kernel(CPUState *env, struct arm_boot_info *info);
+ /* armv7m_nvic.c */
+ int system_clock_scale;
+diff --git a/hw/arm_boot.c b/hw/arm_boot.c
+index 8335e69..20b1512 100644
+--- a/hw/arm_boot.c
++++ b/hw/arm_boot.c
+@@ -47,21 +47,18 @@ static void main_cpu_reset(void *opaque)
+     CPUState *env = opaque;
+     cpu_reset(env);
+-    if (env->kernel_filename)
+-        arm_load_kernel(env, env->ram_size, env->kernel_filename,
+-                        env->kernel_cmdline, env->initrd_filename,
+-                        env->board_id, env->loader_start);
++    if (env->boot_info)
++        arm_load_kernel(env, env->boot_info);
+     /* TODO:  Reset secondary CPUs.  */
+ }
+-static void set_kernel_args(uint32_t ram_size, int initrd_size,
+-                            const char *kernel_cmdline,
+-                            target_phys_addr_t loader_start)
++static void set_kernel_args(struct arm_boot_info *info,
++                int initrd_size, void *base)
+ {
+     uint32_t *p;
+-    p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
++    p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+     /* ATAG_CORE */
+     stl_raw(p++, 5);
+     stl_raw(p++, 0x54410001);
+@@ -69,46 +66,55 @@ static void set_kernel_args(uint32_t ram_size, int initrd_size,
+     stl_raw(p++, 0x1000);
+     stl_raw(p++, 0);
+     /* ATAG_MEM */
++    /* TODO: multiple chips */
+     stl_raw(p++, 4);
+     stl_raw(p++, 0x54410002);
+-    stl_raw(p++, ram_size);
+-    stl_raw(p++, loader_start);
++    stl_raw(p++, info->ram_size);
++    stl_raw(p++, info->loader_start);
+     if (initrd_size) {
+         /* ATAG_INITRD2 */
+         stl_raw(p++, 4);
+         stl_raw(p++, 0x54420005);
+-        stl_raw(p++, loader_start + INITRD_LOAD_ADDR);
++        stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+         stl_raw(p++, initrd_size);
+     }
+-    if (kernel_cmdline && *kernel_cmdline) {
++    if (info->kernel_cmdline && *info->kernel_cmdline) {
+         /* ATAG_CMDLINE */
+         int cmdline_size;
+-        cmdline_size = strlen(kernel_cmdline);
+-        memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
++        cmdline_size = strlen(info->kernel_cmdline);
++        memcpy(p + 2, info->kernel_cmdline, cmdline_size + 1);
+         cmdline_size = (cmdline_size >> 2) + 1;
+         stl_raw(p++, cmdline_size + 2);
+         stl_raw(p++, 0x54410009);
+         p += cmdline_size;
+     }
++    if (info->atag_board) {
++        /* ATAG_BOARD */
++        int atag_board_len;
++
++        atag_board_len = (info->atag_board(info, p + 2) + 3) >> 2;
++        stl_raw(p++, 2 + atag_board_len);
++        stl_raw(p++, 0x414f4d50);
++        p += atag_board_len;
++    }
+     /* ATAG_END */
+     stl_raw(p++, 0);
+     stl_raw(p++, 0);
+ }
+-static void set_kernel_args_old(uint32_t ram_size, int initrd_size,
+-                                const char *kernel_cmdline,
+-                                target_phys_addr_t loader_start)
++static void set_kernel_args_old(struct arm_boot_info *info,
++                int initrd_size, void *base)
+ {
+     uint32_t *p;
+     unsigned char *s;
+     /* see linux/include/asm-arm/setup.h */
+-    p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
++    p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+     /* page_size */
+     stl_raw(p++, 4096);
+     /* nr_pages */
+-    stl_raw(p++, ram_size / 4096);
++    stl_raw(p++, info->ram_size / 4096);
+     /* ramdisk_size */
+     stl_raw(p++, 0);
+ #define FLAG_READONLY 1
+@@ -142,7 +148,7 @@ static void set_kernel_args_old(uint32_t ram_size, int initrd_size,
+     stl_raw(p++, 0);
+     /* initrd_start */
+     if (initrd_size)
+-        stl_raw(p++, loader_start + INITRD_LOAD_ADDR);
++        stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+     else
+         stl_raw(p++, 0);
+     /* initrd_size */
+@@ -159,17 +165,15 @@ static void set_kernel_args_old(uint32_t ram_size, int initrd_size,
+     stl_raw(p++, 0);
+     /* zero unused fields */
+     memset(p, 0, 256 + 1024 -
+-           (p - ((uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR))));
+-    s = phys_ram_base + KERNEL_ARGS_ADDR + 256 + 1024;
+-    if (kernel_cmdline)
+-        strcpy (s, kernel_cmdline);
++           (p - ((uint32_t *)(base + KERNEL_ARGS_ADDR))));
++    s = base + KERNEL_ARGS_ADDR + 256 + 1024;
++    if (info->kernel_cmdline)
++        strcpy (s, info->kernel_cmdline);
+     else
+         stb_raw(s, 0);
+ }
+-void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
+-                     const char *kernel_cmdline, const char *initrd_filename,
+-                     int board_id, target_phys_addr_t loader_start)
++void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
+ {
+     int kernel_size;
+     int initrd_size;
+@@ -177,36 +181,41 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
+     int is_linux = 0;
+     uint64_t elf_entry;
+     target_ulong entry;
++    uint32_t pd;
++    void *loader_phys;
+     /* Load the kernel.  */
+-    if (!kernel_filename) {
++    if (!info->kernel_filename) {
+         fprintf(stderr, "Kernel image must be specified\n");
+         exit(1);
+     }
+-    if (!env->kernel_filename) {
+-        env->ram_size = ram_size;
+-        env->kernel_filename = kernel_filename;
+-        env->kernel_cmdline = kernel_cmdline;
+-        env->initrd_filename = initrd_filename;
+-        env->board_id = board_id;
+-        env->loader_start = loader_start;
++    if (!env->boot_info) {
++        if (info->nb_cpus == 0)
++            info->nb_cpus = 1;
++        env->boot_info = info;
+         qemu_register_reset(main_cpu_reset, env);
+     }
++
++    pd = cpu_get_physical_page_desc(info->loader_start);
++    loader_phys = phys_ram_base + (pd & TARGET_PAGE_MASK) +
++            (info->loader_start & ~TARGET_PAGE_MASK);
++
+     /* Assume that raw images are linux kernels, and ELF images are not.  */
+-    kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL);
++    kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL);
+     entry = elf_entry;
+     if (kernel_size < 0) {
+-        kernel_size = load_uboot(kernel_filename, &entry, &is_linux);
++        kernel_size = load_uboot(info->kernel_filename, &entry, &is_linux);
+     }
+     if (kernel_size < 0) {
+-        kernel_size = load_image(kernel_filename,
+-                                 phys_ram_base + KERNEL_LOAD_ADDR);
+-        entry = loader_start + KERNEL_LOAD_ADDR;
++        kernel_size = load_image(info->kernel_filename,
++                                 loader_phys + KERNEL_LOAD_ADDR);
++        entry = info->loader_start + KERNEL_LOAD_ADDR;
+         is_linux = 1;
+     }
+     if (kernel_size < 0) {
+-        fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
++        fprintf(stderr, "qemu: could not load kernel '%s'\n",
++                info->kernel_filename);
+         exit(1);
+     }
+     if (!is_linux) {
+@@ -214,30 +223,29 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
+         env->regs[15] = entry & 0xfffffffe;
+         env->thumb = entry & 1;
+     } else {
+-        if (initrd_filename) {
+-            initrd_size = load_image(initrd_filename,
+-                                     phys_ram_base + INITRD_LOAD_ADDR);
++        if (info->initrd_filename) {
++            initrd_size = load_image(info->initrd_filename,
++                                     loader_phys + INITRD_LOAD_ADDR);
+             if (initrd_size < 0) {
+                 fprintf(stderr, "qemu: could not load initrd '%s'\n",
+-                        initrd_filename);
++                        info->initrd_filename);
+                 exit(1);
+             }
+         } else {
+             initrd_size = 0;
+         }
+-        bootloader[1] |= board_id & 0xff;
+-        bootloader[2] |= (board_id >> 8) & 0xff;
+-        bootloader[5] = loader_start + KERNEL_ARGS_ADDR;
++        bootloader[1] |= info->board_id & 0xff;
++        bootloader[2] |= (info->board_id >> 8) & 0xff;
++        bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+         bootloader[6] = entry;
+         for (n = 0; n < sizeof(bootloader) / 4; n++)
+-            stl_raw(phys_ram_base + (n * 4), bootloader[n]);
+-        for (n = 0; n < sizeof(smpboot) / 4; n++)
+-            stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]);
++            stl_raw(loader_phys + (n * 4), bootloader[n]);
++        if (info->nb_cpus > 1)
++            for (n = 0; n < sizeof(smpboot) / 4; n++)
++                stl_raw(loader_phys + info->ram_size + (n * 4), smpboot[n]);
+         if (old_param)
+-            set_kernel_args_old(ram_size, initrd_size,
+-                                kernel_cmdline, loader_start);
++            set_kernel_args_old(info, initrd_size, loader_phys);
+         else
+-            set_kernel_args(ram_size, initrd_size,
+-                            kernel_cmdline, loader_start);
++            set_kernel_args(info, initrd_size, loader_phys);
+     }
+ }
+diff --git a/hw/blizzard.c b/hw/blizzard.c
+new file mode 100644
+index 0000000..9046b5d
+--- /dev/null
++++ b/hw/blizzard.c
+@@ -0,0 +1,1001 @@
++/*
++ * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller.
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "qemu-common.h"
++#include "sysemu.h"
++#include "console.h"
++#include "devices.h"
++#include "vga_int.h"
++#include "pixel_ops.h"
++
++typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
++
++struct blizzard_s {
++    uint8_t reg;
++    uint32_t addr;
++    int swallow;
++
++    int pll;
++    int pll_range;
++    int pll_ctrl;
++    uint8_t pll_mode;
++    uint8_t clksel;
++    int memenable;
++    int memrefresh;
++    uint8_t timing[3];
++    int priority;
++
++    uint8_t lcd_config;
++    int x;
++    int y;
++    int skipx;
++    int skipy;
++    uint8_t hndp;
++    uint8_t vndp;
++    uint8_t hsync;
++    uint8_t vsync;
++    uint8_t pclk;
++    uint8_t u;
++    uint8_t v;
++    uint8_t yrc[2];
++    int ix[2];
++    int iy[2];
++    int ox[2];
++    int oy[2];
++
++    int enable;
++    int blank;
++    int bpp;
++    int invalidate;
++    int mx[2];
++    int my[2];
++    uint8_t mode;
++    uint8_t effect;
++    uint8_t iformat;
++    uint8_t source;
++    DisplayState *state;
++    blizzard_fn_t *line_fn_tab[2];
++    void *fb;
++
++    uint8_t hssi_config[3];
++    uint8_t tv_config;
++    uint8_t tv_timing[4];
++    uint8_t vbi;
++    uint8_t tv_x;
++    uint8_t tv_y;
++    uint8_t tv_test;
++    uint8_t tv_filter_config;
++    uint8_t tv_filter_idx;
++    uint8_t tv_filter_coeff[0x20];
++    uint8_t border_r;
++    uint8_t border_g;
++    uint8_t border_b;
++    uint8_t gamma_config;
++    uint8_t gamma_idx;
++    uint8_t gamma_lut[0x100];
++    uint8_t matrix_ena;
++    uint8_t matrix_coeff[0x12];
++    uint8_t matrix_r;
++    uint8_t matrix_g;
++    uint8_t matrix_b;
++    uint8_t pm;
++    uint8_t status;
++    uint8_t rgbgpio_dir;
++    uint8_t rgbgpio;
++    uint8_t gpio_dir;
++    uint8_t gpio;
++    uint8_t gpio_edge[2];
++    uint8_t gpio_irq;
++    uint8_t gpio_pdown;
++
++    struct {
++        int x;
++        int y;
++        int dx;
++        int dy;
++        int len;
++        int buflen;
++        void *buf;
++        void *data;
++        uint16_t *ptr;
++        int angle;
++        int pitch;
++        blizzard_fn_t line_fn;
++    } data;
++};
++
++/* Bytes(!) per pixel */
++static const int blizzard_iformat_bpp[0x10] = {
++    0,
++    2,        /* RGB 5:6:5*/
++    3,        /* RGB 6:6:6 mode 1 */
++    3,        /* RGB 8:8:8 mode 1 */
++    0, 0,
++    4,        /* RGB 6:6:6 mode 2 */
++    4,        /* RGB 8:8:8 mode 2 */
++    0,        /* YUV 4:2:2 */
++    0,        /* YUV 4:2:0 */
++    0, 0, 0, 0, 0, 0,
++};
++
++static inline void blizzard_rgb2yuv(int r, int g, int b,
++                int *y, int *u, int *v)
++{
++    *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13);
++    *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13);
++    *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13);
++}
++
++static void blizzard_window(struct blizzard_s *s)
++{
++    uint8_t *src, *dst;
++    int bypp[2];
++    int bypl[3];
++    int y;
++    blizzard_fn_t fn = s->data.line_fn;
++
++    if (!fn)
++        return;
++    if (s->mx[0] > s->data.x)
++        s->mx[0] = s->data.x;
++    if (s->my[0] > s->data.y)
++        s->my[0] = s->data.y;
++    if (s->mx[1] < s->data.x + s->data.dx)
++        s->mx[1] = s->data.x + s->data.dx;
++    if (s->my[1] < s->data.y + s->data.dy)
++        s->my[1] = s->data.y + s->data.dy;
++
++    bypp[0] = s->bpp;
++    bypp[1] = (s->state->depth + 7) >> 3;
++    bypl[0] = bypp[0] * s->data.pitch;
++    bypl[1] = bypp[1] * s->x;
++    bypl[2] = bypp[0] * s->data.dx;
++
++    src = s->data.data;
++    dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x;
++    for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1])
++        fn(dst, src, bypl[2]);
++}
++
++static int blizzard_transfer_setup(struct blizzard_s *s)
++{
++    if (s->source > 3 || !s->bpp ||
++                    s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0])
++        return 0;
++
++    s->data.angle = s->effect & 3;
++    s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat];
++    s->data.x = s->ix[0];
++    s->data.y = s->iy[0];
++    s->data.dx = s->ix[1] - s->ix[0] + 1;
++    s->data.dy = s->iy[1] - s->iy[0] + 1;
++    s->data.len = s->bpp * s->data.dx * s->data.dy;
++    s->data.pitch = s->data.dx;
++    if (s->data.len > s->data.buflen) {
++        s->data.buf = realloc(s->data.buf, s->data.len);
++        s->data.buflen = s->data.len;
++    }
++    s->data.ptr = s->data.buf;
++    s->data.data = s->data.buf;
++    s->data.len /= 2;
++    return 1;
++}
++
++static void blizzard_reset(struct blizzard_s *s)
++{
++    s->reg = 0;
++    s->swallow = 0;
++
++    s->pll = 9;
++    s->pll_range = 1;
++    s->pll_ctrl = 0x14;
++    s->pll_mode = 0x32;
++    s->clksel = 0x00;
++    s->memenable = 0;
++    s->memrefresh = 0x25c;
++    s->timing[0] = 0x3f;
++    s->timing[1] = 0x13;
++    s->timing[2] = 0x21;
++    s->priority = 0;
++
++    s->lcd_config = 0x74;
++    s->x = 8;
++    s->y = 1;
++    s->skipx = 0;
++    s->skipy = 0;
++    s->hndp = 3;
++    s->vndp = 2;
++    s->hsync = 1;
++    s->vsync = 1;
++    s->pclk = 0x80;
++
++    s->ix[0] = 0;
++    s->ix[1] = 0;
++    s->iy[0] = 0;
++    s->iy[1] = 0;
++    s->ox[0] = 0;
++    s->ox[1] = 0;
++    s->oy[0] = 0;
++    s->oy[1] = 0;
++
++    s->yrc[0] = 0x00;
++    s->yrc[1] = 0x30;
++    s->u = 0;
++    s->v = 0;
++
++    s->iformat = 3;
++    s->source = 0;
++    s->bpp = blizzard_iformat_bpp[s->iformat];
++
++    s->hssi_config[0] = 0x00;
++    s->hssi_config[1] = 0x00;
++    s->hssi_config[2] = 0x01;
++    s->tv_config = 0x00;
++    s->tv_timing[0] = 0x00;
++    s->tv_timing[1] = 0x00;
++    s->tv_timing[2] = 0x00;
++    s->tv_timing[3] = 0x00;
++    s->vbi = 0x10;
++    s->tv_x = 0x14;
++    s->tv_y = 0x03;
++    s->tv_test = 0x00;
++    s->tv_filter_config = 0x80;
++    s->tv_filter_idx = 0x00;
++    s->border_r = 0x10;
++    s->border_g = 0x80;
++    s->border_b = 0x80;
++    s->gamma_config = 0x00;
++    s->gamma_idx = 0x00;
++    s->matrix_ena = 0x00;
++    memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff));
++    s->matrix_r = 0x00;
++    s->matrix_g = 0x00;
++    s->matrix_b = 0x00;
++    s->pm = 0x02;
++    s->status = 0x00;
++    s->rgbgpio_dir = 0x00;
++    s->gpio_dir = 0x00;
++    s->gpio_edge[0] = 0x00;
++    s->gpio_edge[1] = 0x00;
++    s->gpio_irq = 0x00;
++    s->gpio_pdown = 0xff;
++}
++
++static inline void blizzard_invalidate_display(void *opaque) {
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    s->invalidate = 1;
++}
++
++static uint16_t blizzard_reg_read(void *opaque, uint8_t reg)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    switch (reg) {
++    case 0x00:        /* Revision Code */
++        return 0xa5;
++
++    case 0x02:        /* Configuration Readback */
++        return 0x83;  /* Macrovision OK, CNF[2:0] = 3 */
++
++    case 0x04:        /* PLL M-Divider */
++        return (s->pll - 1) | (1 << 7);
++    case 0x06:        /* PLL Lock Range Control */
++        return s->pll_range;
++    case 0x08:        /* PLL Lock Synthesis Control 0 */
++        return s->pll_ctrl & 0xff;
++    case 0x0a:        /* PLL Lock Synthesis Control 1 */
++        return s->pll_ctrl >> 8;
++    case 0x0c:        /* PLL Mode Control 0 */
++        return s->pll_mode;
++
++    case 0x0e:        /* Clock-Source Select */
++        return s->clksel;
++
++    case 0x10:        /* Memory Controller Activate */
++    case 0x14:        /* Memory Controller Bank 0 Status Flag */
++        return s->memenable;
++
++    case 0x18:        /* Auto-Refresh Interval Setting 0 */
++        return s->memrefresh & 0xff;
++    case 0x1a:        /* Auto-Refresh Interval Setting 1 */
++        return s->memrefresh >> 8;
++
++    case 0x1c:        /* Power-On Sequence Timing Control */
++        return s->timing[0];
++    case 0x1e:        /* Timing Control 0 */
++        return s->timing[1];
++    case 0x20:        /* Timing Control 1 */
++        return s->timing[2];
++
++    case 0x24:        /* Arbitration Priority Control */
++        return s->priority;
++
++    case 0x28:        /* LCD Panel Configuration */
++        return s->lcd_config;
++
++    case 0x2a:        /* LCD Horizontal Display Width */
++        return s->x >> 3;
++    case 0x2c:        /* LCD Horizontal Non-display Period */
++        return s->hndp;
++    case 0x2e:        /* LCD Vertical Display Height 0 */
++        return s->y & 0xff;
++    case 0x30:        /* LCD Vertical Display Height 1 */
++        return s->y >> 8;
++    case 0x32:        /* LCD Vertical Non-display Period */
++        return s->vndp;
++    case 0x34:        /* LCD HS Pulse-width */
++        return s->hsync;
++    case 0x36:        /* LCd HS Pulse Start Position */
++        return s->skipx >> 3;
++    case 0x38:        /* LCD VS Pulse-width */
++        return s->vsync;
++    case 0x3a:        /* LCD VS Pulse Start Position */
++        return s->skipy;
++
++    case 0x3c:        /* PCLK Polarity */
++        return s->pclk;
++
++    case 0x3e:        /* High-speed Serial Interface Tx Configuration Port 0 */
++        return s->hssi_config[0];
++    case 0x40:        /* High-speed Serial Interface Tx Configuration Port 1 */
++        return s->hssi_config[1];
++    case 0x42:        /* High-speed Serial Interface Tx Mode */
++        return s->hssi_config[2];
++    case 0x44:        /* TV Display Configuration */
++        return s->tv_config;
++    case 0x46 ... 0x4c:       /* TV Vertical Blanking Interval Data bits */
++        return s->tv_timing[(reg - 0x46) >> 1];
++    case 0x4e:        /* VBI: Closed Caption / XDS Control / Status */
++        return s->vbi;
++    case 0x50:        /* TV Horizontal Start Position */
++        return s->tv_x;
++    case 0x52:        /* TV Vertical Start Position */
++        return s->tv_y;
++    case 0x54:        /* TV Test Pattern Setting */
++        return s->tv_test;
++    case 0x56:        /* TV Filter Setting */
++        return s->tv_filter_config;
++    case 0x58:        /* TV Filter Coefficient Index */
++        return s->tv_filter_idx;
++    case 0x5a:        /* TV Filter Coefficient Data */
++        if (s->tv_filter_idx < 0x20)
++            return s->tv_filter_coeff[s->tv_filter_idx ++];
++        return 0;
++
++    case 0x60:        /* Input YUV/RGB Translate Mode 0 */
++        return s->yrc[0];
++    case 0x62:        /* Input YUV/RGB Translate Mode 1 */
++        return s->yrc[1];
++    case 0x64:        /* U Data Fix */
++        return s->u;
++    case 0x66:        /* V Data Fix */
++        return s->v;
++
++    case 0x68:        /* Display Mode */
++        return s->mode;
++
++    case 0x6a:        /* Special Effects */
++        return s->effect;
++
++    case 0x6c:        /* Input Window X Start Position 0 */
++        return s->ix[0] & 0xff;
++    case 0x6e:        /* Input Window X Start Position 1 */
++        return s->ix[0] >> 3;
++    case 0x70:        /* Input Window Y Start Position 0 */
++        return s->ix[0] & 0xff;
++    case 0x72:        /* Input Window Y Start Position 1 */
++        return s->ix[0] >> 3;
++    case 0x74:        /* Input Window X End Position 0 */
++        return s->ix[1] & 0xff;
++    case 0x76:        /* Input Window X End Position 1 */
++        return s->ix[1] >> 3;
++    case 0x78:        /* Input Window Y End Position 0 */
++        return s->ix[1] & 0xff;
++    case 0x7a:        /* Input Window Y End Position 1 */
++        return s->ix[1] >> 3;
++    case 0x7c:        /* Output Window X Start Position 0 */
++        return s->ox[0] & 0xff;
++    case 0x7e:        /* Output Window X Start Position 1 */
++        return s->ox[0] >> 3;
++    case 0x80:        /* Output Window Y Start Position 0 */
++        return s->oy[0] & 0xff;
++    case 0x82:        /* Output Window Y Start Position 1 */
++        return s->oy[0] >> 3;
++    case 0x84:        /* Output Window X End Position 0 */
++        return s->ox[1] & 0xff;
++    case 0x86:        /* Output Window X End Position 1 */
++        return s->ox[1] >> 3;
++    case 0x88:        /* Output Window Y End Position 0 */
++        return s->oy[1] & 0xff;
++    case 0x8a:        /* Output Window Y End Position 1 */
++        return s->oy[1] >> 3;
++
++    case 0x8c:        /* Input Data Format */
++        return s->iformat;
++    case 0x8e:        /* Data Source Select */
++        return s->source;
++    case 0x90:        /* Display Memory Data Port */
++        return 0;
++
++    case 0xa8:        /* Border Color 0 */
++        return s->border_r;
++    case 0xaa:        /* Border Color 1 */
++        return s->border_g;
++    case 0xac:        /* Border Color 2 */
++        return s->border_b;
++
++    case 0xb4:        /* Gamma Correction Enable */
++        return s->gamma_config;
++    case 0xb6:        /* Gamma Correction Table Index */
++        return s->gamma_idx;
++    case 0xb8:        /* Gamma Correction Table Data */
++        return s->gamma_lut[s->gamma_idx ++];
++
++    case 0xba:        /* 3x3 Matrix Enable */
++        return s->matrix_ena;
++    case 0xbc ... 0xde:       /* Coefficient Registers */
++        return s->matrix_coeff[(reg - 0xbc) >> 1];
++    case 0xe0:        /* 3x3 Matrix Red Offset */
++        return s->matrix_r;
++    case 0xe2:        /* 3x3 Matrix Green Offset */
++        return s->matrix_g;
++    case 0xe4:        /* 3x3 Matrix Blue Offset */
++        return s->matrix_b;
++
++    case 0xe6:        /* Power-save */
++        return s->pm;
++    case 0xe8:        /* Non-display Period Control / Status */
++        return s->status | (1 << 5);
++    case 0xea:        /* RGB Interface Control */
++        return s->rgbgpio_dir;
++    case 0xec:        /* RGB Interface Status */
++        return s->rgbgpio;
++    case 0xee:        /* General-purpose IO Pins Configuration */
++        return s->gpio_dir;
++    case 0xf0:        /* General-purpose IO Pins Status / Control */
++        return s->gpio;
++    case 0xf2:        /* GPIO Positive Edge Interrupt Trigger */
++        return s->gpio_edge[0];
++    case 0xf4:        /* GPIO Negative Edge Interrupt Trigger */
++        return s->gpio_edge[1];
++    case 0xf6:        /* GPIO Interrupt Status */
++        return s->gpio_irq;
++    case 0xf8:        /* GPIO Pull-down Control */
++        return s->gpio_pdown;
++
++    default:
++        fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
++        return 0;
++    }
++}
++
++static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    switch (reg) {
++    case 0x04:        /* PLL M-Divider */
++        s->pll = (value & 0x3f) + 1;
++        break;
++    case 0x06:        /* PLL Lock Range Control */
++        s->pll_range = value & 3;
++        break;
++    case 0x08:        /* PLL Lock Synthesis Control 0 */
++        s->pll_ctrl &= 0xf00;
++        s->pll_ctrl |= (value << 0) & 0x0ff;
++        break;
++    case 0x0a:        /* PLL Lock Synthesis Control 1 */
++        s->pll_ctrl &= 0x0ff;
++        s->pll_ctrl |= (value << 8) & 0xf00;
++        break;
++    case 0x0c:        /* PLL Mode Control 0 */
++        s->pll_mode = value & 0x77;
++        if ((value & 3) == 0 || (value & 3) == 3)
++            fprintf(stderr, "%s: wrong PLL Control bits (%i)\n",
++                    __FUNCTION__, value & 3);
++        break;
++
++    case 0x0e:        /* Clock-Source Select */
++        s->clksel = value & 0xff;
++        break;
++
++    case 0x10:        /* Memory Controller Activate */
++        s->memenable = value & 1;
++        break;
++    case 0x14:        /* Memory Controller Bank 0 Status Flag */
++        break;
++
++    case 0x18:        /* Auto-Refresh Interval Setting 0 */
++        s->memrefresh &= 0xf00;
++        s->memrefresh |= (value << 0) & 0x0ff;
++        break;
++    case 0x1a:        /* Auto-Refresh Interval Setting 1 */
++        s->memrefresh &= 0x0ff;
++        s->memrefresh |= (value << 8) & 0xf00;
++        break;
++
++    case 0x1c:        /* Power-On Sequence Timing Control */
++        s->timing[0] = value & 0x7f;
++        break;
++    case 0x1e:        /* Timing Control 0 */
++        s->timing[1] = value & 0x17;
++        break;
++    case 0x20:        /* Timing Control 1 */
++        s->timing[2] = value & 0x35;
++        break;
++
++    case 0x24:        /* Arbitration Priority Control */
++        s->priority = value & 1;
++        break;
++
++    case 0x28:        /* LCD Panel Configuration */
++        s->lcd_config = value & 0xff;
++        if (value & (1 << 7))
++            fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__);
++        break;
++
++    case 0x2a:        /* LCD Horizontal Display Width */
++        s->x = value << 3;
++        break;
++    case 0x2c:        /* LCD Horizontal Non-display Period */
++        s->hndp = value & 0xff;
++        break;
++    case 0x2e:        /* LCD Vertical Display Height 0 */
++        s->y &= 0x300;
++        s->y |= (value << 0) & 0x0ff;
++        break;
++    case 0x30:        /* LCD Vertical Display Height 1 */
++        s->y &= 0x0ff;
++        s->y |= (value << 8) & 0x300;
++        break;
++    case 0x32:        /* LCD Vertical Non-display Period */
++        s->vndp = value & 0xff;
++        break;
++    case 0x34:        /* LCD HS Pulse-width */
++        s->hsync = value & 0xff;
++        break;
++    case 0x36:        /* LCD HS Pulse Start Position */
++        s->skipx = value & 0xff;
++        break;
++    case 0x38:        /* LCD VS Pulse-width */
++        s->vsync = value & 0xbf;
++        break;
++    case 0x3a:        /* LCD VS Pulse Start Position */
++        s->skipy = value & 0xff;
++        break;
++
++    case 0x3c:        /* PCLK Polarity */
++        s->pclk = value & 0x82;
++        /* Affects calculation of s->hndp, s->hsync and s->skipx.  */
++        break;
++
++    case 0x3e:        /* High-speed Serial Interface Tx Configuration Port 0 */
++        s->hssi_config[0] = value;
++        break;
++    case 0x40:        /* High-speed Serial Interface Tx Configuration Port 1 */
++        s->hssi_config[1] = value;
++        if (((value >> 4) & 3) == 3)
++            fprintf(stderr, "%s: Illegal active-data-links value\n",
++                            __FUNCTION__);
++        break;
++    case 0x42:        /* High-speed Serial Interface Tx Mode */
++        s->hssi_config[2] = value & 0xbd;
++        break;
++
++    case 0x44:        /* TV Display Configuration */
++        s->tv_config = value & 0xfe;
++        break;
++    case 0x46 ... 0x4c:       /* TV Vertical Blanking Interval Data bits 0 */
++        s->tv_timing[(reg - 0x46) >> 1] = value;
++        break;
++    case 0x4e:        /* VBI: Closed Caption / XDS Control / Status */
++        s->vbi = value;
++        break;
++    case 0x50:        /* TV Horizontal Start Position */
++        s->tv_x = value;
++        break;
++    case 0x52:        /* TV Vertical Start Position */
++        s->tv_y = value & 0x7f;
++        break;
++    case 0x54:        /* TV Test Pattern Setting */
++        s->tv_test = value;
++        break;
++    case 0x56:        /* TV Filter Setting */
++        s->tv_filter_config = value & 0xbf;
++        break;
++    case 0x58:        /* TV Filter Coefficient Index */
++        s->tv_filter_idx = value & 0x1f;
++        break;
++    case 0x5a:        /* TV Filter Coefficient Data */
++        if (s->tv_filter_idx < 0x20)
++            s->tv_filter_coeff[s->tv_filter_idx ++] = value;
++        break;
++
++    case 0x60:        /* Input YUV/RGB Translate Mode 0 */
++        s->yrc[0] = value & 0xb0;
++        break;
++    case 0x62:        /* Input YUV/RGB Translate Mode 1 */
++        s->yrc[1] = value & 0x30;
++        break;
++    case 0x64:        /* U Data Fix */
++        s->u = value & 0xff;
++        break;
++    case 0x66:        /* V Data Fix */
++        s->v = value & 0xff;
++        break;
++
++    case 0x68:        /* Display Mode */
++        if ((s->mode ^ value) & 3)
++            s->invalidate = 1;
++        s->mode = value & 0xb7;
++        s->enable = value & 1;
++        s->blank = (value >> 1) & 1;
++        if (value & (1 << 4))
++            fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__);
++        break;
++
++    case 0x6a:        /* Special Effects */
++        s->effect = value & 0xfb;
++        break;
++
++    case 0x6c:        /* Input Window X Start Position 0 */
++        s->ix[0] &= 0x300;
++        s->ix[0] |= (value << 0) & 0x0ff;
++        break;
++    case 0x6e:        /* Input Window X Start Position 1 */
++        s->ix[0] &= 0x0ff;
++        s->ix[0] |= (value << 8) & 0x300;
++        break;
++    case 0x70:        /* Input Window Y Start Position 0 */
++        s->iy[0] &= 0x300;
++        s->iy[0] |= (value << 0) & 0x0ff;
++        break;
++    case 0x72:        /* Input Window Y Start Position 1 */
++        s->iy[0] &= 0x0ff;
++        s->iy[0] |= (value << 8) & 0x300;
++        break;
++    case 0x74:        /* Input Window X End Position 0 */
++        s->ix[1] &= 0x300;
++        s->ix[1] |= (value << 0) & 0x0ff;
++        break;
++    case 0x76:        /* Input Window X End Position 1 */
++        s->ix[1] &= 0x0ff;
++        s->ix[1] |= (value << 8) & 0x300;
++        break;
++    case 0x78:        /* Input Window Y End Position 0 */
++        s->iy[1] &= 0x300;
++        s->iy[1] |= (value << 0) & 0x0ff;
++        break;
++    case 0x7a:        /* Input Window Y End Position 1 */
++        s->iy[1] &= 0x0ff;
++        s->iy[1] |= (value << 8) & 0x300;
++        break;
++    case 0x7c:        /* Output Window X Start Position 0 */
++        s->ox[0] &= 0x300;
++        s->ox[0] |= (value << 0) & 0x0ff;
++        break;
++    case 0x7e:        /* Output Window X Start Position 1 */
++        s->ox[0] &= 0x0ff;
++        s->ox[0] |= (value << 8) & 0x300;
++        break;
++    case 0x80:        /* Output Window Y Start Position 0 */
++        s->oy[0] &= 0x300;
++        s->oy[0] |= (value << 0) & 0x0ff;
++        break;
++    case 0x82:        /* Output Window Y Start Position 1 */
++        s->oy[0] &= 0x0ff;
++        s->oy[0] |= (value << 8) & 0x300;
++        break;
++    case 0x84:        /* Output Window X End Position 0 */
++        s->ox[1] &= 0x300;
++        s->ox[1] |= (value << 0) & 0x0ff;
++        break;
++    case 0x86:        /* Output Window X End Position 1 */
++        s->ox[1] &= 0x0ff;
++        s->ox[1] |= (value << 8) & 0x300;
++        break;
++    case 0x88:        /* Output Window Y End Position 0 */
++        s->oy[1] &= 0x300;
++        s->oy[1] |= (value << 0) & 0x0ff;
++        break;
++    case 0x8a:        /* Output Window Y End Position 1 */
++        s->oy[1] &= 0x0ff;
++        s->oy[1] |= (value << 8) & 0x300;
++        break;
++
++    case 0x8c:        /* Input Data Format */
++        s->iformat = value & 0xf;
++        s->bpp = blizzard_iformat_bpp[s->iformat];
++        if (!s->bpp)
++            fprintf(stderr, "%s: Illegal or unsupported input format %x\n",
++                            __FUNCTION__, s->iformat);
++        break;
++    case 0x8e:        /* Data Source Select */
++        s->source = value & 7;
++        /* Currently all windows will be "destructive overlays".  */
++        if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] ||
++                                        s->iy[0] != s->oy[0] ||
++                                        s->ix[1] != s->ox[1] ||
++                                        s->iy[1] != s->oy[1])) ||
++                        !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) &
++                          (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1))
++            fprintf(stderr, "%s: Illegal input/output window positions\n",
++                            __FUNCTION__);
++
++        blizzard_transfer_setup(s);
++        break;
++
++    case 0x90:        /* Display Memory Data Port */
++        if (!s->data.len && !blizzard_transfer_setup(s))
++            break;
++
++        *s->data.ptr ++ = value;
++        if (-- s->data.len == 0)
++            blizzard_window(s);
++        break;
++
++    case 0xa8:        /* Border Color 0 */
++        s->border_r = value;
++        break;
++    case 0xaa:        /* Border Color 1 */
++        s->border_g = value;
++        break;
++    case 0xac:        /* Border Color 2 */
++        s->border_b = value;
++        break;
++
++    case 0xb4:        /* Gamma Correction Enable */
++        s->gamma_config = value & 0x87;
++        break;
++    case 0xb6:        /* Gamma Correction Table Index */
++        s->gamma_idx = value;
++        break;
++    case 0xb8:        /* Gamma Correction Table Data */
++        s->gamma_lut[s->gamma_idx ++] = value;
++        break;
++
++    case 0xba:        /* 3x3 Matrix Enable */
++        s->matrix_ena = value & 1;
++        break;
++    case 0xbc ... 0xde:       /* Coefficient Registers */
++        s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff);
++        break;
++    case 0xe0:        /* 3x3 Matrix Red Offset */
++        s->matrix_r = value;
++        break;
++    case 0xe2:        /* 3x3 Matrix Green Offset */
++        s->matrix_g = value;
++        break;
++    case 0xe4:        /* 3x3 Matrix Blue Offset */
++        s->matrix_b = value;
++        break;
++
++    case 0xe6:        /* Power-save */
++        s->pm = value & 0x83;
++        if (value & s->mode & 1)
++            fprintf(stderr, "%s: The display must be disabled before entering "
++                            "Standby Mode\n", __FUNCTION__);
++        break;
++    case 0xe8:        /* Non-display Period Control / Status */
++        s->status = value & 0x1b;
++        break;
++    case 0xea:        /* RGB Interface Control */
++        s->rgbgpio_dir = value & 0x8f;
++        break;
++    case 0xec:        /* RGB Interface Status */
++        s->rgbgpio = value & 0xcf;
++        break;
++    case 0xee:        /* General-purpose IO Pins Configuration */
++        s->gpio_dir = value;
++        break;
++    case 0xf0:        /* General-purpose IO Pins Status / Control */
++        s->gpio = value;
++        break;
++    case 0xf2:        /* GPIO Positive Edge Interrupt Trigger */
++        s->gpio_edge[0] = value;
++        break;
++    case 0xf4:        /* GPIO Negative Edge Interrupt Trigger */
++        s->gpio_edge[1] = value;
++        break;
++    case 0xf6:        /* GPIO Interrupt Status */
++        s->gpio_irq &= value;
++        break;
++    case 0xf8:        /* GPIO Pull-down Control */
++        s->gpio_pdown = value;
++        break;
++
++    default:
++        fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
++        break;
++    }
++}
++
++uint16_t s1d13745_read(void *opaque, int dc)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++    uint16_t value = blizzard_reg_read(s, s->reg);
++
++    if (s->swallow -- > 0)
++        return 0;
++    if (dc)
++        s->reg ++;
++
++    return value;
++}
++
++void s1d13745_write(void *opaque, int dc, uint16_t value)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    if (s->swallow -- > 0)
++        return;
++    if (dc) {
++        blizzard_reg_write(s, s->reg, value);
++
++        if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8)
++            s->reg += 2;
++    } else
++        s->reg = value & 0xff;
++}
++
++void s1d13745_write_block(void *opaque, int dc,
++                void *buf, size_t len, int pitch)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    while (len > 0) {
++        if (s->reg == 0x90 && dc &&
++                        (s->data.len || blizzard_transfer_setup(s)) &&
++                        len >= (s->data.len << 1)) {
++            len -= s->data.len << 1;
++            s->data.len = 0;
++            s->data.data = buf;
++            if (pitch)
++                s->data.pitch = pitch;
++            blizzard_window(s);
++            s->data.data = s->data.buf;
++            continue;
++        }
++
++        s1d13745_write(opaque, dc, *(uint16_t *) buf);
++        len -= 2;
++        buf += 2;
++    }
++
++    return;
++}
++
++static void blizzard_update_display(void *opaque)
++{
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++    int y, bypp, bypl, bwidth;
++    uint8_t *src, *dst;
++
++    if (!s->enable)
++        return;
++
++    if (s->x != s->state->width || s->y != s->state->height) {
++        s->invalidate = 1;
++        dpy_resize(s->state, s->x, s->y);
++    }
++
++    if (s->invalidate) {
++        s->invalidate = 0;
++
++        if (s->blank) {
++            bypp = (s->state->depth + 7) >> 3;
++            memset(s->state->data, 0, bypp * s->x * s->y);
++            return;
++        }
++
++        s->mx[0] = 0;
++        s->mx[1] = s->x;
++        s->my[0] = 0;
++        s->my[1] = s->y;
++    }
++
++    if (s->mx[1] <= s->mx[0])
++        return;
++
++    bypp = (s->state->depth + 7) >> 3;
++    bypl = bypp * s->x;
++    bwidth = bypp * (s->mx[1] - s->mx[0]);
++    y = s->my[0];
++    src = s->fb + bypl * y + bypp * s->mx[0];
++    dst = s->state->data + bypl * y + bypp * s->mx[0];
++    for (; y < s->my[1]; y ++, src += bypl, dst += bypl)
++        memcpy(dst, src, bwidth);
++
++    dpy_update(s->state, s->mx[0], s->my[0],
++                    s->mx[1] - s->mx[0], y - s->my[0]);
++
++    s->mx[0] = s->x;
++    s->mx[1] = 0;
++    s->my[0] = s->y;
++    s->my[1] = 0;
++}
++
++static void blizzard_screen_dump(void *opaque, const char *filename) {
++    struct blizzard_s *s = (struct blizzard_s *) opaque;
++
++    blizzard_update_display(opaque);
++    if (s && s->state->data)
++        ppm_save(filename, s->state->data, s->x, s->y, s->state->linesize);
++}
++
++#define DEPTH 8
++#include "blizzard_template.h"
++#define DEPTH 15
++#include "blizzard_template.h"
++#define DEPTH 16
++#include "blizzard_template.h"
++#define DEPTH 24
++#include "blizzard_template.h"
++#define DEPTH 32
++#include "blizzard_template.h"
++
++void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds)
++{
++    struct blizzard_s *s = (struct blizzard_s *) qemu_mallocz(sizeof(*s));
++
++    s->state = ds;
++    s->fb = qemu_malloc(0x180000);
++
++    switch (s->state->depth) {
++    case 0:
++        s->line_fn_tab[0] = s->line_fn_tab[1] =
++                qemu_mallocz(sizeof(blizzard_fn_t) * 0x10);
++        break;
++    case 8:
++        s->line_fn_tab[0] = blizzard_draw_fn_8;
++        s->line_fn_tab[1] = blizzard_draw_fn_r_8;
++        break;
++    case 15:
++        s->line_fn_tab[0] = blizzard_draw_fn_15;
++        s->line_fn_tab[1] = blizzard_draw_fn_r_15;
++        break;
++    case 16:
++        s->line_fn_tab[0] = blizzard_draw_fn_16;
++        s->line_fn_tab[1] = blizzard_draw_fn_r_16;
++        break;
++    case 24:
++        s->line_fn_tab[0] = blizzard_draw_fn_24;
++        s->line_fn_tab[1] = blizzard_draw_fn_r_24;
++        break;
++    case 32:
++        s->line_fn_tab[0] = blizzard_draw_fn_32;
++        s->line_fn_tab[1] = blizzard_draw_fn_r_32;
++        break;
++    default:
++        fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
++        exit(1);
++    }
++
++    blizzard_reset(s);
++
++    graphic_console_init(s->state, blizzard_update_display,
++                    blizzard_invalidate_display, blizzard_screen_dump,
++                    NULL, s);
++
++    return s;
++}
+diff --git a/hw/blizzard_template.h b/hw/blizzard_template.h
+new file mode 100644
+index 0000000..8c6451d
+--- /dev/null
++++ b/hw/blizzard_template.h
+@@ -0,0 +1,138 @@
++/*
++ * QEMU Epson S1D13744/S1D13745 templates
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#define SKIP_PIXEL(to)                to += deststep
++#if DEPTH == 8
++# define PIXEL_TYPE           uint8_t
++# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
++# define COPY_PIXEL1(to, from)        *to ++ = from
++#elif DEPTH == 15 || DEPTH == 16
++# define PIXEL_TYPE           uint16_t
++# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
++# define COPY_PIXEL1(to, from)        *to ++ = from
++#elif DEPTH == 24
++# define PIXEL_TYPE           uint8_t
++# define COPY_PIXEL(to, from) \
++    to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to)
++# define COPY_PIXEL1(to, from)        \
++    *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
++#elif DEPTH == 32
++# define PIXEL_TYPE           uint32_t
++# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
++# define COPY_PIXEL1(to, from)        *to ++ = from
++#else
++# error unknown bit depth
++#endif
++
++#ifdef WORDS_BIGENDIAN
++# define SWAP_WORDS   1
++#endif
++
++static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest,
++                const uint16_t *src, unsigned int width)
++{
++#if !defined(SWAP_WORDS) && DEPTH == 16
++    memcpy(dest, src, width << 1);
++#else
++    uint16_t data;
++    unsigned int r, g, b;
++    const uint16_t *end = (void *) src + width;
++    while (src < end) {
++        data = lduw_raw(src ++);
++        b = (data & 0x1f) << 3;
++        data >>= 5;
++        g = (data & 0x3f) << 2;
++        data >>= 6;
++        r = (data & 0x1f) << 3;
++        data >>= 5;
++        COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
++    }
++#endif
++}
++
++static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest,
++                const uint8_t *src, unsigned int width)
++{
++    /* TODO: check if SDL 24-bit planes are not in the same format and
++     * if so, use memcpy */
++    unsigned int r[2], g[2], b[2];
++    const uint8_t *end = src + width;
++    while (src < end) {
++        g[0] = *src ++;
++        r[0] = *src ++;
++        r[1] = *src ++;
++        b[0] = *src ++;
++        COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0]));
++        b[1] = *src ++;
++        g[1] = *src ++;
++        COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1]));
++    }
++}
++
++static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest,
++                const uint8_t *src, unsigned int width)
++{
++    unsigned int r, g, b;
++    const uint8_t *end = src + width;
++    while (src < end) {
++        r = *src ++;
++        src ++;
++        b = *src ++;
++        g = *src ++;
++        COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
++    }
++}
++
++/* No rotation */
++static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = {
++    NULL,
++    /* RGB 5:6:5*/
++    (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH),
++    /* RGB 6:6:6 mode 1 */
++    (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
++    /* RGB 8:8:8 mode 1 */
++    (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
++    NULL, NULL,
++    /* RGB 6:6:6 mode 2 */
++    (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
++    /* RGB 8:8:8 mode 2 */
++    (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
++    /* YUV 4:2:2 */
++    NULL,
++    /* YUV 4:2:0 */
++    NULL,
++    NULL, NULL, NULL, NULL, NULL, NULL,
++};
++
++/* 90deg, 180deg and 270deg rotation */
++static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = {
++    /* TODO */
++    [0 ... 0xf] = NULL,
++};
++
++#undef DEPTH
++#undef SKIP_PIXEL
++#undef COPY_PIXEL
++#undef COPY_PIXEL1
++#undef PIXEL_TYPE
++
++#undef SWAP_WORDS
+diff --git a/hw/boards.h b/hw/boards.h
+index affcaa6..408d1e8 100644
+--- a/hw/boards.h
++++ b/hw/boards.h
+@@ -80,6 +80,9 @@ extern QEMUMachine terrierpda_machine;
+ /* palm.c */
+ extern QEMUMachine palmte_machine;
++/* nseries.c */
++extern QEMUMachine n800_machine;
++
+ /* gumstix.c */
+ extern QEMUMachine connex_machine;
+ extern QEMUMachine verdex_machine;
+diff --git a/hw/cbus.c b/hw/cbus.c
+new file mode 100644
+index 0000000..001b007
+--- /dev/null
++++ b/hw/cbus.c
+@@ -0,0 +1,565 @@
++/*
++ * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma /
++ * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms.
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "qemu-common.h"
++#include "irq.h"
++#include "devices.h"
++#include "sysemu.h"
++
++//#define DEBUG
++
++struct cbus_slave_s;
++struct cbus_priv_s {
++    struct cbus_s cbus;
++
++    int sel;
++    int dat;
++    int clk;
++    int bit;
++    int dir;
++    uint16_t val;
++    qemu_irq dat_out;
++
++    int addr;
++    int reg;
++    int rw;
++    enum {
++        cbus_address,
++        cbus_value,
++    } cycle;
++
++    struct cbus_slave_s *slave[8];
++};
++
++struct cbus_slave_s {
++    void *opaque;
++    void (*io)(void *opaque, int rw, int reg, uint16_t *val);
++    int addr;
++};
++
++static void cbus_io(struct cbus_priv_s *s)
++{
++    if (s->slave[s->addr])
++        s->slave[s->addr]->io(s->slave[s->addr]->opaque,
++                        s->rw, s->reg, &s->val);
++    else
++        cpu_abort(cpu_single_env, "%s: bad slave address %i\n",
++                        __FUNCTION__, s->addr);
++}
++
++static void cbus_cycle(struct cbus_priv_s *s)
++{
++    switch (s->cycle) {
++    case cbus_address:
++        s->addr = (s->val >> 6) & 7;
++        s->rw =   (s->val >> 5) & 1;
++        s->reg =  (s->val >> 0) & 0x1f;
++
++        s->cycle = cbus_value;
++        s->bit = 15;
++        s->dir = !s->rw;
++        s->val = 0;
++
++        if (s->rw)
++            cbus_io(s);
++        break;
++
++    case cbus_value:
++        if (!s->rw)
++            cbus_io(s);
++
++        s->cycle = cbus_address;
++        s->bit = 8;
++        s->dir = 1;
++        s->val = 0;
++        break;
++    }
++}
++
++static void cbus_clk(void *opaque, int line, int level)
++{
++    struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
++
++    if (!s->sel && level && !s->clk) {
++        if (s->dir)
++            s->val |= s->dat << (s->bit --);
++        else
++            qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1);
++
++        if (s->bit < 0)
++            cbus_cycle(s);
++    }
++
++    s->clk = level;
++}
++
++static void cbus_dat(void *opaque, int line, int level)
++{
++    struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
++
++    s->dat = level;
++}
++
++static void cbus_sel(void *opaque, int line, int level)
++{
++    struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
++
++    if (!level) {
++        s->dir = 1;
++        s->bit = 8;
++        s->val = 0;
++    }
++
++    s->sel = level;
++}
++
++struct cbus_s *cbus_init(qemu_irq dat)
++{
++    struct cbus_priv_s *s = (struct cbus_priv_s *) qemu_mallocz(sizeof(*s));
++
++    s->dat_out = dat;
++    s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0];
++    s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0];
++    s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0];
++
++    s->sel = 1;
++    s->clk = 0;
++    s->dat = 0;
++
++    return &s->cbus;
++}
++
++void cbus_attach(struct cbus_s *bus, void *slave_opaque)
++{
++    struct cbus_slave_s *slave = (struct cbus_slave_s *) slave_opaque;
++    struct cbus_priv_s *s = (struct cbus_priv_s *) bus;
++
++    s->slave[slave->addr] = slave;
++}
++
++/* Retu/Vilma */
++struct cbus_retu_s {
++    uint16_t irqst;
++    uint16_t irqen;
++    uint16_t cc[2];
++    int channel;
++    uint16_t result[16];
++    uint16_t sample;
++
++    struct {
++        uint16_t cal;
++    } rtc;
++
++    int is_vilma;
++    qemu_irq irq;
++    struct cbus_slave_s cbus;
++};
++
++static void retu_interrupt_update(struct cbus_retu_s *s)
++{
++    qemu_set_irq(s->irq, s->irqst & ~s->irqen);
++}
++
++#define RETU_REG_ASICR                0x00    /* (RO) ASIC ID & revision */
++#define RETU_REG_IDR          0x01    /* (T)  Interrupt ID */
++#define RETU_REG_IMR          0x02    /* (RW) Interrupt mask */
++#define RETU_REG_RTCDSR               0x03    /* (RW) RTC seconds register */
++#define RETU_REG_RTCHMR               0x04    /* (RO) RTC hours and minutes reg */
++#define RETU_REG_RTCHMAR      0x05    /* (RW) RTC hours and minutes set reg */
++#define RETU_REG_RTCCALR      0x06    /* (RW) RTC calibration register */
++#define RETU_REG_ADCR         0x08    /* (RW) ADC result register */
++#define RETU_REG_ADCSCR               0x09    /* (RW) ADC sample control register */
++#define RETU_REG_AFCR         0x0a    /* (RW) AFC register */
++#define RETU_REG_ANTIFR               0x0b    /* (RW) AntiF register */
++#define RETU_REG_CALIBR               0x0c    /* (RW) CalibR register*/
++#define RETU_REG_CCR1         0x0d    /* (RW) Common control register 1 */
++#define RETU_REG_CCR2         0x0e    /* (RW) Common control register 2 */
++#define RETU_REG_RCTRL_CLR    0x0f    /* (T)  Regulator clear register */
++#define RETU_REG_RCTRL_SET    0x10    /* (T)  Regulator set register */
++#define RETU_REG_TXCR         0x11    /* (RW) TxC register */
++#define RETU_REG_STATUS               0x16    /* (RO) Status register */
++#define RETU_REG_WATCHDOG     0x17    /* (RW) Watchdog register */
++#define RETU_REG_AUDTXR               0x18    /* (RW) Audio Codec Tx register */
++#define RETU_REG_AUDPAR               0x19    /* (RW) AudioPA register */
++#define RETU_REG_AUDRXR1      0x1a    /* (RW) Audio receive register 1 */
++#define RETU_REG_AUDRXR2      0x1b    /* (RW) Autio receive register 2 */
++#define RETU_REG_SGR1         0x1c    /* (RW) */
++#define RETU_REG_SCR1         0x1d    /* (RW) */
++#define RETU_REG_SGR2         0x1e    /* (RW) */
++#define RETU_REG_SCR2         0x1f    /* (RW) */
++
++/* Retu Interrupt sources */
++enum {
++    retu_int_pwr      = 0,    /* Power */
++    retu_int_char     = 1,    /* Charger */
++    retu_int_rtcs     = 2,    /* Seconds */
++    retu_int_rtcm     = 3,    /* Minutes */
++    retu_int_rtcd     = 4,    /* Days */
++    retu_int_rtca     = 5,    /* Alarm */
++    retu_int_hook     = 6,    /* Hook */
++    retu_int_head     = 7,    /* Headset */
++    retu_int_adcs     = 8,    /* ADC sample */
++};
++
++/* Retu ADC channel wiring */
++enum {
++    retu_adc_bsi      = 1,    /* BSI */
++    retu_adc_batt_temp        = 2,    /* Battery temperature */
++    retu_adc_chg_volt = 3,    /* Charger voltage */
++    retu_adc_head_det = 4,    /* Headset detection */
++    retu_adc_hook_det = 5,    /* Hook detection */
++    retu_adc_rf_gp    = 6,    /* RF GP */
++    retu_adc_tx_det   = 7,    /* Wideband Tx detection */
++    retu_adc_batt_volt        = 8,    /* Battery voltage */
++    retu_adc_sens     = 10,   /* Light sensor */
++    retu_adc_sens_temp        = 11,   /* Light sensor temperature */
++    retu_adc_bbatt_volt       = 12,   /* Backup battery voltage */
++    retu_adc_self_temp        = 13,   /* RETU temperature */
++};
++
++static inline uint16_t retu_read(struct cbus_retu_s *s, int reg)
++{
++#ifdef DEBUG
++    printf("RETU read at %02x\n", reg);
++#endif
++
++    switch (reg) {
++    case RETU_REG_ASICR:
++        return 0x0015 | (s->is_vilma << 7);
++
++    case RETU_REG_IDR:
++        return s->irqst;
++
++    case RETU_REG_IMR:
++        return s->irqen;
++
++    case RETU_REG_RTCDSR:
++    case RETU_REG_RTCHMR:
++    case RETU_REG_RTCHMAR:
++        /* TODO */
++        return 0x0000;
++
++    case RETU_REG_RTCCALR:
++        return s->rtc.cal;
++
++    case RETU_REG_ADCR:
++        return (s->channel << 10) | s->result[s->channel];
++    case RETU_REG_ADCSCR:
++        return s->sample;
++
++    case RETU_REG_AFCR:
++    case RETU_REG_ANTIFR:
++    case RETU_REG_CALIBR:
++        /* TODO */
++        return 0x0000;
++
++    case RETU_REG_CCR1:
++        return s->cc[0];
++    case RETU_REG_CCR2:
++        return s->cc[1];
++
++    case RETU_REG_RCTRL_CLR:
++    case RETU_REG_RCTRL_SET:
++    case RETU_REG_TXCR:
++    case RETU_REG_STATUS:
++    case RETU_REG_WATCHDOG:
++    case RETU_REG_AUDTXR:
++    case RETU_REG_AUDPAR:
++    case RETU_REG_AUDRXR1:
++    case RETU_REG_AUDRXR2:
++    case RETU_REG_SGR1:
++    case RETU_REG_SCR1:
++    case RETU_REG_SGR2:
++    case RETU_REG_SCR2:
++        /* TODO */
++        return 0x0000;
++
++    default:
++        cpu_abort(cpu_single_env, "%s: bad register %02x\n",
++                        __FUNCTION__, reg);
++    }
++}
++
++static inline void retu_write(struct cbus_retu_s *s, int reg, uint16_t val)
++{
++#ifdef DEBUG
++    printf("RETU write of %04x at %02x\n", val, reg);
++#endif
++
++    switch (reg) {
++    case RETU_REG_IDR:
++        s->irqst ^= val;
++        retu_interrupt_update(s);
++        break;
++
++    case RETU_REG_IMR:
++        s->irqen = val;
++        retu_interrupt_update(s);
++        break;
++
++    case RETU_REG_RTCDSR:
++    case RETU_REG_RTCHMAR:
++        /* TODO */
++        break;
++
++    case RETU_REG_RTCCALR:
++        s->rtc.cal = val;
++        break;
++
++    case RETU_REG_ADCR:
++        s->channel = (val >> 10) & 0xf;
++        s->irqst |= 1 << retu_int_adcs;
++        retu_interrupt_update(s);
++        break;
++    case RETU_REG_ADCSCR:
++        s->sample &= ~val;
++        break;
++
++    case RETU_REG_AFCR:
++    case RETU_REG_ANTIFR:
++    case RETU_REG_CALIBR:
++
++    case RETU_REG_CCR1:
++        s->cc[0] = val;
++        break;
++    case RETU_REG_CCR2:
++        s->cc[1] = val;
++
++        break;
++    case RETU_REG_RCTRL_CLR:
++    case RETU_REG_RCTRL_SET:
++    case RETU_REG_STATUS:
++        /* TODO */
++        break;
++
++    case RETU_REG_WATCHDOG:
++        if (val == 0 && (s->cc[0] & 2))
++            qemu_system_shutdown_request();
++        break;
++
++    case RETU_REG_TXCR:
++    case RETU_REG_AUDTXR:
++    case RETU_REG_AUDPAR:
++    case RETU_REG_AUDRXR1:
++    case RETU_REG_AUDRXR2:
++    case RETU_REG_SGR1:
++    case RETU_REG_SCR1:
++    case RETU_REG_SGR2:
++    case RETU_REG_SCR2:
++        /* TODO */
++        break;
++
++    default:
++        cpu_abort(cpu_single_env, "%s: bad register %02x\n",
++                        __FUNCTION__, reg);
++    }
++}
++
++static void retu_io(void *opaque, int rw, int reg, uint16_t *val)
++{
++    struct cbus_retu_s *s = (struct cbus_retu_s *) opaque;
++
++    if (rw)
++        *val = retu_read(s, reg);
++    else
++        retu_write(s, reg, *val);
++}
++
++void *retu_init(qemu_irq irq, int vilma)
++{
++    struct cbus_retu_s *s = (struct cbus_retu_s *) qemu_mallocz(sizeof(*s));
++
++    s->irq = irq;
++    s->irqen = 0xffff;
++    s->irqst = 0x0000;
++    s->is_vilma = !!vilma;
++    s->rtc.cal = 0x01;
++    s->result[retu_adc_bsi] = 0x100;
++    s->result[retu_adc_batt_temp] = 0x100;
++    s->result[retu_adc_chg_volt] = 0x200;
++    s->result[retu_adc_batt_volt] = 0x240;
++    s->result[retu_adc_sens] = 0x100;
++    s->result[retu_adc_sens_temp] = 0x100;
++    s->result[retu_adc_bbatt_volt] = 0x200;
++    s->result[retu_adc_self_temp] = 0x100;
++
++    s->cbus.opaque = s;
++    s->cbus.io = retu_io;
++    s->cbus.addr = 1;
++
++    return &s->cbus;
++}
++
++/* Tahvo/Betty */
++struct cbus_tahvo_s {
++    uint16_t irqst;
++    uint16_t irqen;
++    uint8_t charger;
++    uint8_t backlight;
++    uint16_t usbr;
++    uint16_t power;
++
++    int is_betty;
++    qemu_irq irq;
++    struct cbus_slave_s cbus;
++};
++
++static void tahvo_interrupt_update(struct cbus_tahvo_s *s)
++{
++    qemu_set_irq(s->irq, s->irqst & ~s->irqen);
++}
++
++#define TAHVO_REG_ASICR               0x00    /* (RO) ASIC ID & revision */
++#define TAHVO_REG_IDR         0x01    /* (T)  Interrupt ID */
++#define TAHVO_REG_IDSR                0x02    /* (RO) Interrupt status */
++#define TAHVO_REG_IMR         0x03    /* (RW) Interrupt mask */
++#define TAHVO_REG_CHAPWMR     0x04    /* (RW) Charger PWM */
++#define TAHVO_REG_LEDPWMR     0x05    /* (RW) LED PWM */
++#define TAHVO_REG_USBR                0x06    /* (RW) USB control */
++#define TAHVO_REG_RCR         0x07    /* (RW) Some kind of power management */
++#define TAHVO_REG_CCR1                0x08    /* (RW) Common control register 1 */
++#define TAHVO_REG_CCR2                0x09    /* (RW) Common control register 2 */
++#define TAHVO_REG_TESTR1      0x0a    /* (RW) Test register 1 */
++#define TAHVO_REG_TESTR2      0x0b    /* (RW) Test register 2 */
++#define TAHVO_REG_NOPR                0x0c    /* (RW) Number of periods */
++#define TAHVO_REG_FRR         0x0d    /* (RO) FR */
++
++static inline uint16_t tahvo_read(struct cbus_tahvo_s *s, int reg)
++{
++#ifdef DEBUG
++    printf("TAHVO read at %02x\n", reg);
++#endif
++
++    switch (reg) {
++    case TAHVO_REG_ASICR:
++        return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300);
++
++    case TAHVO_REG_IDR:
++    case TAHVO_REG_IDSR:      /* XXX: what does this do?  */
++        return s->irqst;
++
++    case TAHVO_REG_IMR:
++        return s->irqen;
++
++    case TAHVO_REG_CHAPWMR:
++        return s->charger;
++
++    case TAHVO_REG_LEDPWMR:
++        return s->backlight;
++
++    case TAHVO_REG_USBR:
++        return s->usbr;
++
++    case TAHVO_REG_RCR:
++        return s->power;
++
++    case TAHVO_REG_CCR1:
++    case TAHVO_REG_CCR2:
++    case TAHVO_REG_TESTR1:
++    case TAHVO_REG_TESTR2:
++    case TAHVO_REG_NOPR:
++    case TAHVO_REG_FRR:
++        return 0x0000;
++
++    default:
++        cpu_abort(cpu_single_env, "%s: bad register %02x\n",
++                        __FUNCTION__, reg);
++    }
++}
++
++static inline void tahvo_write(struct cbus_tahvo_s *s, int reg, uint16_t val)
++{
++#ifdef DEBUG
++    printf("TAHVO write of %04x at %02x\n", val, reg);
++#endif
++
++    switch (reg) {
++    case TAHVO_REG_IDR:
++        s->irqst ^= val;
++        tahvo_interrupt_update(s);
++        break;
++
++    case TAHVO_REG_IMR:
++        s->irqen = val;
++        tahvo_interrupt_update(s);
++        break;
++
++    case TAHVO_REG_CHAPWMR:
++        s->charger = val;
++        break;
++
++    case TAHVO_REG_LEDPWMR:
++        if (s->backlight != (val & 0x7f)) {
++            s->backlight = val & 0x7f;
++            printf("%s: LCD backlight now at %i / 127\n",
++                            __FUNCTION__, s->backlight);
++        }
++        break;
++
++    case TAHVO_REG_USBR:
++        s->usbr = val;
++        break;
++
++    case TAHVO_REG_RCR:
++        s->power = val;
++        break;
++
++    case TAHVO_REG_CCR1:
++    case TAHVO_REG_CCR2:
++    case TAHVO_REG_TESTR1:
++    case TAHVO_REG_TESTR2:
++    case TAHVO_REG_NOPR:
++    case TAHVO_REG_FRR:
++        break;
++
++    default:
++        cpu_abort(cpu_single_env, "%s: bad register %02x\n",
++                        __FUNCTION__, reg);
++    }
++}
++
++static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val)
++{
++    struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) opaque;
++
++    if (rw)
++        *val = tahvo_read(s, reg);
++    else
++        tahvo_write(s, reg, *val);
++}
++
++void *tahvo_init(qemu_irq irq, int betty)
++{
++    struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) qemu_mallocz(sizeof(*s));
++
++    s->irq = irq;
++    s->irqen = 0xffff;
++    s->irqst = 0x0000;
++    s->is_betty = !!betty;
++
++    s->cbus.opaque = s;
++    s->cbus.io = tahvo_io;
++    s->cbus.addr = 2;
++
++    return &s->cbus;
++}
+diff --git a/hw/devices.h b/hw/devices.h
+index 07c673b..6f1d27b 100644
+--- a/hw/devices.h
++++ b/hw/devices.h
+@@ -16,7 +16,38 @@ uint32_t ads7846_read(void *opaque);
+ void ads7846_write(void *opaque, uint32_t value);
+ struct ads7846_state_s *ads7846_init(qemu_irq penirq);
++/* tsc210x.c */
++struct uwire_slave_s;
++struct mouse_transform_info_s;
++struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio);
++struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq,
++                qemu_irq dav, AudioState *audio);
++struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip);
++uint32_t tsc210x_txrx(void *opaque, uint32_t value);
++void tsc210x_set_transform(struct uwire_slave_s *chip,
++                struct mouse_transform_info_s *info);
++void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down);
++
+ /* stellaris_input.c */
+ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
++/* blizzard.c */
++void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds);
++void s1d13745_write(void *opaque, int dc, uint16_t value);
++void s1d13745_write_block(void *opaque, int dc,
++                void *buf, size_t len, int pitch);
++uint16_t s1d13745_read(void *opaque, int dc);
++
++/* cbus.c */
++struct cbus_s {
++    qemu_irq clk;
++    qemu_irq dat;
++    qemu_irq sel;
++};
++struct cbus_s *cbus_init(qemu_irq dat_out);
++void cbus_attach(struct cbus_s *bus, void *slave_opaque);
++
++void *retu_init(qemu_irq irq, int vilma);
++void *tahvo_init(qemu_irq irq, int betty);
++
+ #endif
+diff --git a/hw/flash.h b/hw/flash.h
+index 42d25fe..c000d33 100644
+--- a/hw/flash.h
++++ b/hw/flash.h
+@@ -34,6 +34,11 @@ uint8_t nand_getio(struct nand_flash_s *s);
+ #define NAND_MFR_HYNIX                0xad
+ #define NAND_MFR_MICRON               0x2c
++/* 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);
++
+ /* ecc.c */
+ struct ecc_state_s {
+     uint8_t cp;               /* Column parity */
+diff --git a/hw/i2c.h b/hw/i2c.h
+index 2897036..fae46b7 100644
+--- a/hw/i2c.h
++++ b/hw/i2c.h
+@@ -71,4 +71,14 @@ uint32_t wm8750_adc_dat(void *opaque);
+ /* ssd0303.c */
+ void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address);
++/* twl92230.c */
++i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq);
++qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c);
++void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
++
++/* tmp105.c */
++struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm);
++void tmp105_reset(i2c_slave *i2c);
++void tmp105_set(i2c_slave *i2c, int temp);
++
+ #endif
+diff --git a/hw/integratorcp.c b/hw/integratorcp.c
+index 549cc25..f6e6364 100644
+--- a/hw/integratorcp.c
++++ b/hw/integratorcp.c
+@@ -469,6 +469,11 @@ static void icp_control_init(uint32_t base)
+ /* Board init.  */
++static struct arm_boot_info integrator_binfo = {
++    .loader_start = 0x0,
++    .board_id = 0x113,
++};
++
+ static void integratorcp_init(int ram_size, int vga_ram_size,
+                      const char *boot_device, DisplayState *ds,
+                      const char *kernel_filename, const char *kernel_cmdline,
+@@ -527,8 +532,11 @@ static void integratorcp_init(int ram_size, int vga_ram_size,
+     }
+     pl110_init(ds, 0xc0000000, pic[22], 0);
+-    arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
+-                    initrd_filename, 0x113, 0x0);
++    integrator_binfo.ram_size = ram_size;
++    integrator_binfo.kernel_filename = kernel_filename;
++    integrator_binfo.kernel_cmdline = kernel_cmdline;
++    integrator_binfo.initrd_filename = initrd_filename;
++    arm_load_kernel(env, &integrator_binfo);
+ }
+ QEMUMachine integratorcp_machine = {
+diff --git a/hw/mainstone.c b/hw/mainstone.c
+index 5856791..9564fc3 100644
+--- a/hw/mainstone.c
++++ b/hw/mainstone.c
+@@ -59,12 +59,17 @@ static struct keymap map[0xE0] = {
+ enum mainstone_model_e { mainstone };
++static struct arm_boot_info mainstone_binfo = {
++    .loader_start = PXA2XX_SDRAM_BASE,
++    .ram_size = 0x04000000,
++};
++
+ static void mainstone_common_init(int ram_size, int vga_ram_size,
+                 DisplayState *ds, const char *kernel_filename,
+                 const char *kernel_cmdline, const char *initrd_filename,
+                 const char *cpu_model, enum mainstone_model_e model, int arm_id)
+ {
+-    uint32_t mainstone_ram   = 0x04000000;
++    uint32_t mainstone_ram   = mainstone_binfo.ram_size;
+     uint32_t mainstone_rom   = 0x00800000;
+     uint32_t mainstone_flash = 0x02000000;
+     uint32_t sector_len = 256 * 1024;
+@@ -90,7 +95,7 @@ static void mainstone_common_init(int ram_size, int vga_ram_size,
+                     qemu_ram_alloc(mainstone_rom) | IO_MEM_ROM);
+     /* Setup initial (reset) machine state */
+-    cpu->env->regs[15] = PXA2XX_SDRAM_BASE;
++    cpu->env->regs[15] = mainstone_binfo.loader_start;
+     /* There are two 32MiB flash devices on the board */
+     for (i = 0; i < 2; i ++) {
+@@ -121,8 +126,11 @@ static void mainstone_common_init(int ram_size, int vga_ram_size,
+     smc91c111_init(&nd_table[0], MST_ETH_PHYS, mst_irq[ETHERNET_IRQ]);
+-    arm_load_kernel(cpu->env, mainstone_ram, kernel_filename, kernel_cmdline,
+-                    initrd_filename, arm_id, PXA2XX_SDRAM_BASE);
++    mainstone_binfo.kernel_filename = kernel_filename;
++    mainstone_binfo.kernel_cmdline = kernel_cmdline;
++    mainstone_binfo.initrd_filename = initrd_filename;
++    mainstone_binfo.board_id = arm_id;
++    arm_load_kernel(cpu->env, &mainstone_binfo);
+ }
+ static void mainstone_init(int ram_size, int vga_ram_size,
+diff --git a/hw/max7310.c b/hw/max7310.c
+index 75e56c7..397950a 100644
+--- a/hw/max7310.c
++++ b/hw/max7310.c
+@@ -134,8 +134,8 @@ static void max7310_event(i2c_slave *i2c, enum i2c_event event)
+         s->i2c_command_byte = 1;
+         break;
+     case I2C_FINISH:
+-        if (s->len == 1)
+ #ifdef VERBOSE
++        if (s->len == 1)
+             printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
+ #endif
+         break;
+diff --git a/hw/nseries.c b/hw/nseries.c
+new file mode 100644
+index 0000000..0425d46
+--- /dev/null
++++ b/hw/nseries.c
+@@ -0,0 +1,870 @@
++/*
++ * Nokia N-series internet tablets.
++ *
++ * Copyright (C) 2007 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "qemu-common.h"
++#include "sysemu.h"
++#include "omap.h"
++#include "arm-misc.h"
++#include "irq.h"
++#include "console.h"
++#include "boards.h"
++#include "i2c.h"
++#include "devices.h"
++#include "flash.h"
++#include "hw.h"
++
++/* Nokia N800 support */
++struct n800_s {
++    struct omap_mpu_state_s *cpu;
++
++    struct rfbi_chip_s blizzard;
++    struct uwire_slave_s *ts;
++    i2c_bus *i2c;
++
++    int keymap[0x80];
++};
++
++#define N800_MMC2_WP_GPIO             8
++#define N800_CAM_TURN_GPIO            12
++#define N800_BLIZZARD_POWERDOWN_GPIO  15
++#define N800_MMC1_WP_GPIO             23
++#define N800_ONENAND_GPIO             26
++#define N800_BT_WKUP_GPIO             61
++#define N800_STI_GPIO                 62
++#define N800_CBUS_SEL_GPIO            64
++#define N800_CBUS_CLK_GPIO            65
++#define N800_CBUS_DAT_GPIO            66
++#define N800_WLAN_IRQ_GPIO            87
++#define N800_BT_RESET_GPIO            92
++#define N800_TEA5761_CS_GPIO          93
++#define N800_UNKNOWN_GPIO             94
++#define N800_CAM_ACT_GPIO             95
++#define N800_MMC_CS_GPIO              96
++#define N800_WLAN_PWR_GPIO            97
++#define N800_BT_HOST_WKUP_GPIO                98
++#define N800_TSC_TS_GPIO              103
++#define N800_HEADPHONE_GPIO           107
++#define N800_RETU_GPIO                        108
++#define N800_TSC_KP_IRQ_GPIO          109
++#define N800_BAT_COVER_GPIO           110
++#define N800_TAHVO_GPIO                       111
++#define N800_TSC_RESET_GPIO           119
++#define N800_TMP105_GPIO              125
++
++#define XLDR_LL_UART                  1
++
++#define N800_TMP105_ADDR              0x48
++#define N800_MENELAUS_ADDR            0x72
++
++static void n800_mmc_cs_cb(void *opaque, int line, int level)
++{
++    /* TODO: this seems to actually be connected to the menelaus, to
++     * which also both MMC slots connect.  */
++    omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
++
++    printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
++}
++
++static void n800_gpio_setup(struct n800_s *s)
++{
++    qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1);
++    omap2_gpio_out_set(s->cpu->gpif, N800_MMC_CS_GPIO, mmc_cs[0]);
++
++    qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]);
++}
++
++static void n800_nand_setup(struct n800_s *s)
++{
++    /* Either ec40xx or ec48xx are OK for the ID */
++    omap_gpmc_attach(s->cpu->gpmc, 0, 0, onenand_base_update,
++                    onenand_base_unmap,
++                    onenand_init(0xec4800, 1,
++                            omap2_gpio_in_get(s->cpu->gpif,
++                                    N800_ONENAND_GPIO)[0]));
++}
++
++static void n800_i2c_setup(struct n800_s *s)
++{
++    qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N800_TMP105_GPIO)[0];
++
++    /* Attach the CPU on one end of our I2C bus.  */
++    s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
++
++    /* Attach a menelaus PM chip */
++    i2c_set_slave_address(
++                    twl92230_init(s->i2c,
++                            s->cpu->irq[0][OMAP_INT_24XX_SYS_NIRQ]),
++                    N800_MENELAUS_ADDR);
++
++    /* Attach a TMP105 PM chip (A0 wired to ground) */
++    i2c_set_slave_address(tmp105_init(s->i2c, tmp_irq), N800_TMP105_ADDR);
++}
++
++/* Touchscreen and keypad controller */
++static void n800_key_event(void *opaque, int keycode)
++{
++    struct n800_s *s = (struct n800_s *) opaque;
++    int code = s->keymap[keycode & 0x7f];
++
++    if (code == -1)
++        return;
++
++    tsc210x_key_event(s->ts, code, !(keycode & 0x80));
++}
++
++static const int n800_keys[16] = {
++    -1,
++    72,       /* Up */
++    63,       /* Home (F5) */
++    -1,
++    75,       /* Left */
++    28,       /* Enter */
++    77,       /* Right */
++    -1,
++    1,        /* Cycle (ESC) */
++    80,       /* Down */
++    62,       /* Menu (F4) */
++    -1,
++    66,       /* Zoom- (F8) */
++    64,       /* FS (F6) */
++    65,       /* Zoom+ (F7) */
++    -1,
++};
++
++static struct mouse_transform_info_s n800_pointercal = {
++    .x = 800,
++    .y = 480,
++    .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
++};
++
++static void n800_tsc_setup(struct n800_s *s)
++{
++    int i;
++
++    /* XXX: are the three pins inverted inside the chip between the
++     * tsc and the cpu (N4111)?  */
++    qemu_irq penirq = 0;      /* NC */
++    qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0];
++    qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0];
++
++    s->ts = tsc2301_init(penirq, kbirq, dav, 0);
++
++    for (i = 0; i < 0x80; i ++)
++        s->keymap[i] = -1;
++    for (i = 0; i < 0x10; i ++)
++        if (n800_keys[i] >= 0)
++            s->keymap[n800_keys[i]] = i;
++
++    qemu_add_kbd_event_handler(n800_key_event, s);
++
++    tsc210x_set_transform(s->ts, &n800_pointercal);
++}
++
++/* LCD MIPI DBI-C controller (URAL) */
++struct mipid_s {
++    int resp[4];
++    int param[4];
++    int p;
++    int pm;
++    int cmd;
++
++    int sleep;
++    int booster;
++    int te;
++    int selfcheck;
++    int partial;
++    int normal;
++    int vscr;
++    int invert;
++    int onoff;
++    int gamma;
++    uint32_t id;
++};
++
++static void mipid_reset(struct mipid_s *s)
++{
++    if (!s->sleep)
++        fprintf(stderr, "%s: Display off\n", __FUNCTION__);
++
++    s->pm = 0;
++    s->cmd = 0;
++
++    s->sleep = 1;
++    s->booster = 0;
++    s->selfcheck =
++            (1 << 7) |        /* Register loading OK.  */
++            (1 << 5) |        /* The chip is attached.  */
++            (1 << 4); /* Display glass still in one piece.  */
++    s->te = 0;
++    s->partial = 0;
++    s->normal = 1;
++    s->vscr = 0;
++    s->invert = 0;
++    s->onoff = 1;
++    s->gamma = 0;
++}
++
++static uint32_t mipid_txrx(void *opaque, uint32_t cmd)
++{
++    struct mipid_s *s = (struct mipid_s *) opaque;
++    uint8_t ret;
++
++    if (s->p >= sizeof(s->resp) / sizeof(*s->resp))
++        ret = 0;
++    else
++        ret = s->resp[s->p ++];
++    if (s->pm --> 0)
++        s->param[s->pm] = cmd;
++    else
++        s->cmd = cmd;
++
++    switch (s->cmd) {
++    case 0x00:        /* NOP */
++        break;
++
++    case 0x01:        /* SWRESET */
++        mipid_reset(s);
++        break;
++
++    case 0x02:        /* BSTROFF */
++        s->booster = 0;
++        break;
++    case 0x03:        /* BSTRON */
++        s->booster = 1;
++        break;
++
++    case 0x04:        /* RDDID */
++        s->p = 0;
++        s->resp[0] = (s->id >> 16) & 0xff;
++        s->resp[1] = (s->id >>  8) & 0xff;
++        s->resp[2] = (s->id >>  0) & 0xff;
++        break;
++
++    case 0x06:        /* RD_RED */
++    case 0x07:        /* RD_GREEN */
++        /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so
++         * for the bootloader one needs to change this.  */
++    case 0x08:        /* RD_BLUE */
++        s->p = 0;
++        /* TODO: return first pixel components */
++        s->resp[0] = 0x01;
++        break;
++
++    case 0x09:        /* RDDST */
++        s->p = 0;
++        s->resp[0] = s->booster << 7;
++        s->resp[1] = (5 << 4) | (s->partial << 2) |
++                (s->sleep << 1) | s->normal;
++        s->resp[2] = (s->vscr << 7) | (s->invert << 5) |
++                (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2);
++        s->resp[3] = s->gamma << 6;
++        break;
++
++    case 0x0a:        /* RDDPM */
++        s->p = 0;
++        s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) |
++                (s->partial << 5) | (s->sleep << 6) | (s->booster << 7);
++        break;
++    case 0x0b:        /* RDDMADCTR */
++        s->p = 0;
++        s->resp[0] = 0;
++        break;
++    case 0x0c:        /* RDDCOLMOD */
++        s->p = 0;
++        s->resp[0] = 5;       /* 65K colours */
++        break;
++    case 0x0d:        /* RDDIM */
++        s->p = 0;
++        s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma;
++        break;
++    case 0x0e:        /* RDDSM */
++        s->p = 0;
++        s->resp[0] = s->te << 7;
++        break;
++    case 0x0f:        /* RDDSDR */
++        s->p = 0;
++        s->resp[0] = s->selfcheck;
++        break;
++
++    case 0x10:        /* SLPIN */
++        s->sleep = 1;
++        break;
++    case 0x11:        /* SLPOUT */
++        s->sleep = 0;
++        s->selfcheck ^= 1 << 6;       /* POFF self-diagnosis Ok */
++        break;
++
++    case 0x12:        /* PTLON */
++        s->partial = 1;
++        s->normal = 0;
++        s->vscr = 0;
++        break;
++    case 0x13:        /* NORON */
++        s->partial = 0;
++        s->normal = 1;
++        s->vscr = 0;
++        break;
++
++    case 0x20:        /* INVOFF */
++        s->invert = 0;
++        break;
++    case 0x21:        /* INVON */
++        s->invert = 1;
++        break;
++
++    case 0x22:        /* APOFF */
++    case 0x23:        /* APON */
++        goto bad_cmd;
++
++    case 0x25:        /* WRCNTR */
++        if (s->pm < 0)
++            s->pm = 1;
++        goto bad_cmd;
++
++    case 0x26:        /* GAMSET */
++        if (!s->pm)
++            s->gamma = ffs(s->param[0] & 0xf) - 1;
++        else if (s->pm < 0)
++            s->pm = 1;
++        break;
++
++    case 0x28:        /* DISPOFF */
++        s->onoff = 0;
++        fprintf(stderr, "%s: Display off\n", __FUNCTION__);
++        break;
++    case 0x29:        /* DISPON */
++        s->onoff = 1;
++        fprintf(stderr, "%s: Display on\n", __FUNCTION__);
++        break;
++
++    case 0x2a:        /* CASET */
++    case 0x2b:        /* RASET */
++    case 0x2c:        /* RAMWR */
++    case 0x2d:        /* RGBSET */
++    case 0x2e:        /* RAMRD */
++    case 0x30:        /* PTLAR */
++    case 0x33:        /* SCRLAR */
++        goto bad_cmd;
++
++    case 0x34:        /* TEOFF */
++        s->te = 0;
++        break;
++    case 0x35:        /* TEON */
++        if (!s->pm)
++            s->te = 1;
++        else if (s->pm < 0)
++            s->pm = 1;
++        break;
++
++    case 0x36:        /* MADCTR */
++        goto bad_cmd;
++
++    case 0x37:        /* VSCSAD */
++        s->partial = 0;
++        s->normal = 0;
++        s->vscr = 1;
++        break;
++
++    case 0x38:        /* IDMOFF */
++    case 0x39:        /* IDMON */
++    case 0x3a:        /* COLMOD */
++        goto bad_cmd;
++
++    case 0xb0:        /* CLKINT / DISCTL */
++    case 0xb1:        /* CLKEXT */
++        if (s->pm < 0)
++            s->pm = 2;
++        break;
++
++    case 0xb4:        /* FRMSEL */
++        break;
++
++    case 0xb5:        /* FRM8SEL */
++    case 0xb6:        /* TMPRNG / INIESC */
++    case 0xb7:        /* TMPHIS / NOP2 */
++    case 0xb8:        /* TMPREAD / MADCTL */
++    case 0xba:        /* DISTCTR */
++    case 0xbb:        /* EPVOL */
++        goto bad_cmd;
++
++    case 0xbd:        /* Unknown */
++        s->p = 0;
++        s->resp[0] = 0;
++        s->resp[1] = 1;
++        break;
++
++    case 0xc2:        /* IFMOD */
++        if (s->pm < 0)
++            s->pm = 2;
++        break;
++
++    case 0xc6:        /* PWRCTL */
++    case 0xc7:        /* PPWRCTL */
++    case 0xd0:        /* EPWROUT */
++    case 0xd1:        /* EPWRIN */
++    case 0xd4:        /* RDEV */
++    case 0xd5:        /* RDRR */
++        goto bad_cmd;
++
++    case 0xda:        /* RDID1 */
++        s->p = 0;
++        s->resp[0] = (s->id >> 16) & 0xff;
++        break;
++    case 0xdb:        /* RDID2 */
++        s->p = 0;
++        s->resp[0] = (s->id >>  8) & 0xff;
++        break;
++    case 0xdc:        /* RDID3 */
++        s->p = 0;
++        s->resp[0] = (s->id >>  0) & 0xff;
++        break;
++
++    default:
++    bad_cmd:
++        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd);
++        break;
++    }
++
++    return ret;
++}
++
++static void *mipid_init(void)
++{
++    struct mipid_s *s = (struct mipid_s *) qemu_mallocz(sizeof(*s));
++
++    s->id = 0x838f03;
++    mipid_reset(s);
++
++    return s;
++}
++
++static void n800_spi_setup(struct n800_s *s)
++{
++    void *tsc2301 = s->ts->opaque;
++    void *mipid = mipid_init();
++
++    omap_mcspi_attach(s->cpu->mcspi[0], tsc210x_txrx, tsc2301, 0);
++    omap_mcspi_attach(s->cpu->mcspi[0], mipid_txrx, mipid, 1);
++}
++
++/* This task is normally performed by the bootloader.  If we're loading
++ * a kernel directly, we need to enable the Blizzard ourselves.  */
++static void n800_dss_init(struct rfbi_chip_s *chip)
++{
++    chip->write(chip->opaque, 0, 0x2a);               /* LCD Width register */
++    chip->write(chip->opaque, 1, 0x64);
++    chip->write(chip->opaque, 0, 0x2c);               /* LCD HNDP register */
++    chip->write(chip->opaque, 1, 0x1e);
++    chip->write(chip->opaque, 0, 0x2e);               /* LCD Height 0 register */
++    chip->write(chip->opaque, 1, 0xe0);
++    chip->write(chip->opaque, 0, 0x30);               /* LCD Height 1 register */
++    chip->write(chip->opaque, 1, 0x01);
++    chip->write(chip->opaque, 0, 0x32);               /* LCD VNDP register */
++    chip->write(chip->opaque, 1, 0x06);
++    chip->write(chip->opaque, 0, 0x68);               /* Display Mode register */
++    chip->write(chip->opaque, 1, 1);          /* Enable bit */
++}
++
++static void n800_dss_setup(struct n800_s *s, DisplayState *ds)
++{
++    s->blizzard.opaque = s1d13745_init(0, ds);
++    s->blizzard.block = s1d13745_write_block;
++    s->blizzard.write = s1d13745_write;
++    s->blizzard.read = s1d13745_read;
++
++    omap_rfbi_attach(s->cpu->dss, 0, &s->blizzard);
++}
++
++static void n800_cbus_setup(struct n800_s *s)
++{
++    qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N800_CBUS_DAT_GPIO)[0];
++    qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N800_RETU_GPIO)[0];
++    qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N800_TAHVO_GPIO)[0];
++
++    struct cbus_s *cbus = cbus_init(dat_out);
++
++    omap2_gpio_out_set(s->cpu->gpif, N800_CBUS_CLK_GPIO, cbus->clk);
++    omap2_gpio_out_set(s->cpu->gpif, N800_CBUS_DAT_GPIO, cbus->dat);
++    omap2_gpio_out_set(s->cpu->gpif, N800_CBUS_SEL_GPIO, cbus->sel);
++
++    cbus_attach(cbus, retu_init(retu_irq, 1));
++    cbus_attach(cbus, tahvo_init(tahvo_irq, 1));
++}
++
++/* This task is normally performed by the bootloader.  If we're loading
++ * a kernel directly, we need to set up GPMC mappings ourselves.  */
++static void n800_gpmc_init(struct n800_s *s)
++{
++    uint32_t config7 =
++            (0xf << 8) |      /* MASKADDRESS */
++            (1 << 6) |                /* CSVALID */
++            (4 << 0);         /* BASEADDRESS */
++
++    cpu_physical_memory_write(0x6800a078,             /* GPMC_CONFIG7_0 */
++                    (void *) &config7, sizeof(config7));
++}
++
++#if 0
++static uint32_t n800_pinout[104] = {
++    0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0,
++    0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808,
++    0x08080808, 0x180800c4, 0x00b80000, 0x08080808,
++    0x080800bc, 0x00cc0808, 0x08081818, 0x18180128,
++    0x01241800, 0x18181818, 0x000000f0, 0x01300000,
++    0x00001b0b, 0x1b0f0138, 0x00e0181b, 0x1b031b0b,
++    0x180f0078, 0x00740018, 0x0f0f0f1a, 0x00000080,
++    0x007c0000, 0x00000000, 0x00000088, 0x00840000,
++    0x00000000, 0x00000094, 0x00980300, 0x0f180003,
++    0x0000008c, 0x00900f0f, 0x0f0f1b00, 0x0f00009c,
++    0x01140000, 0x1b1b0f18, 0x0818013c, 0x01400008,
++    0x00001818, 0x000b0110, 0x010c1800, 0x0b030b0f,
++    0x181800f4, 0x00f81818, 0x00000018, 0x000000fc,
++    0x00401808, 0x00000000, 0x0f1b0030, 0x003c0008,
++    0x00000000, 0x00000038, 0x00340000, 0x00000000,
++    0x1a080070, 0x00641a1a, 0x08080808, 0x08080060,
++    0x005c0808, 0x08080808, 0x08080058, 0x00540808,
++    0x08080808, 0x0808006c, 0x00680808, 0x08080808,
++    0x000000a8, 0x00b00000, 0x08080808, 0x000000a0,
++    0x00a40000, 0x00000000, 0x08ff0050, 0x004c0808,
++    0xffffffff, 0xffff0048, 0x0044ffff, 0xffffffff,
++    0x000000ac, 0x01040800, 0x08080b0f, 0x18180100,
++    0x01081818, 0x0b0b1808, 0x1a0300e4, 0x012c0b1a,
++    0x02020018, 0x0b000134, 0x011c0800, 0x0b1b1b00,
++    0x0f0000c8, 0x00ec181b, 0x000f0f02, 0x00180118,
++    0x01200000, 0x0f0b1b1b, 0x0f0200e8, 0x0000020b,
++};
++#endif
++
++/* Setup sequence done by the bootloader */
++static void n800_boot_init(void *opaque)
++{
++    struct n800_s *s = (struct n800_s *) opaque;
++    uint32_t buf;
++
++    /* PRCM setup */
++#define omap_writel(addr, val)        \
++    buf = (val);                      \
++    cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf))
++
++    omap_writel(0x48008060, 0x41);            /* PRCM_CLKSRC_CTRL */
++    omap_writel(0x48008070, 1);                       /* PRCM_CLKOUT_CTRL */
++    omap_writel(0x48008078, 0);                       /* PRCM_CLKEMUL_CTRL */
++    omap_writel(0x48008090, 0);                       /* PRCM_VOLTSETUP */
++    omap_writel(0x48008094, 0);                       /* PRCM_CLKSSETUP */
++    omap_writel(0x48008098, 0);                       /* PRCM_POLCTRL */
++    omap_writel(0x48008140, 2);                       /* CM_CLKSEL_MPU */
++    omap_writel(0x48008148, 0);                       /* CM_CLKSTCTRL_MPU */
++    omap_writel(0x48008158, 1);                       /* RM_RSTST_MPU */
++    omap_writel(0x480081c8, 0x15);            /* PM_WKDEP_MPU */
++    omap_writel(0x480081d4, 0x1d4);           /* PM_EVGENCTRL_MPU */
++    omap_writel(0x480081d8, 0);                       /* PM_EVEGENONTIM_MPU */
++    omap_writel(0x480081dc, 0);                       /* PM_EVEGENOFFTIM_MPU */
++    omap_writel(0x480081e0, 0xc);             /* PM_PWSTCTRL_MPU */
++    omap_writel(0x48008200, 0x047e7ff7);      /* CM_FCLKEN1_CORE */
++    omap_writel(0x48008204, 0x00000004);      /* CM_FCLKEN2_CORE */
++    omap_writel(0x48008210, 0x047e7ff1);      /* CM_ICLKEN1_CORE */
++    omap_writel(0x48008214, 0x00000004);      /* CM_ICLKEN2_CORE */
++    omap_writel(0x4800821c, 0x00000000);      /* CM_ICLKEN4_CORE */
++    omap_writel(0x48008230, 0);                       /* CM_AUTOIDLE1_CORE */
++    omap_writel(0x48008234, 0);                       /* CM_AUTOIDLE2_CORE */
++    omap_writel(0x48008238, 7);                       /* CM_AUTOIDLE3_CORE */
++    omap_writel(0x4800823c, 0);                       /* CM_AUTOIDLE4_CORE */
++    omap_writel(0x48008240, 0x04360626);      /* CM_CLKSEL1_CORE */
++    omap_writel(0x48008244, 0x00000014);      /* CM_CLKSEL2_CORE */
++    omap_writel(0x48008248, 0);                       /* CM_CLKSTCTRL_CORE */
++    omap_writel(0x48008300, 0x00000000);      /* CM_FCLKEN_GFX */
++    omap_writel(0x48008310, 0x00000000);      /* CM_ICLKEN_GFX */
++    omap_writel(0x48008340, 0x00000001);      /* CM_CLKSEL_GFX */
++    omap_writel(0x48008400, 0x00000004);      /* CM_FCLKEN_WKUP */
++    omap_writel(0x48008410, 0x00000004);      /* CM_ICLKEN_WKUP */
++    omap_writel(0x48008440, 0x00000000);      /* CM_CLKSEL_WKUP */
++    omap_writel(0x48008500, 0x000000cf);      /* CM_CLKEN_PLL */
++    omap_writel(0x48008530, 0x0000000c);      /* CM_AUTOIDLE_PLL */
++    omap_writel(0x48008540,                   /* CM_CLKSEL1_PLL */
++                    (0x78 << 12) | (6 << 8));
++    omap_writel(0x48008544, 2);                       /* CM_CLKSEL2_PLL */
++
++    /* GPMC setup */
++    n800_gpmc_init(s);
++
++    /* Video setup */
++    n800_dss_init(&s->blizzard);
++
++    /* CPU setup */
++    s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start;
++}
++
++#define OMAP_TAG_NOKIA_BT     0x4e01
++#define OMAP_TAG_WLAN_CX3110X 0x4e02
++#define OMAP_TAG_CBUS         0x4e03
++#define OMAP_TAG_EM_ASIC_BB5  0x4e04
++
++static int n800_atag_setup(struct arm_boot_info *info, void *p)
++{
++    uint8_t *b;
++    uint16_t *w;
++    uint32_t *l;
++
++    w = p;
++
++    stw_raw(w ++, OMAP_TAG_UART);             /* u16 tag */
++    stw_raw(w ++, 4);                         /* u16 len */
++    stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */
++    w ++;
++
++    stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5);      /* u16 tag */
++    stw_raw(w ++, 4);                         /* u16 len */
++    stw_raw(w ++, N800_RETU_GPIO);            /* s16 retu_irq_gpio */
++    stw_raw(w ++, N800_TAHVO_GPIO);           /* s16 tahvo_irq_gpio */
++
++    stw_raw(w ++, OMAP_TAG_CBUS);             /* u16 tag */
++    stw_raw(w ++, 8);                         /* u16 len */
++    stw_raw(w ++, N800_CBUS_CLK_GPIO);                /* s16 clk_gpio */
++    stw_raw(w ++, N800_CBUS_DAT_GPIO);                /* s16 dat_gpio */
++    stw_raw(w ++, N800_CBUS_SEL_GPIO);                /* s16 sel_gpio */
++    w ++;
++
++    stw_raw(w ++, OMAP_TAG_GPIO_SWITCH);      /* u16 tag */
++    stw_raw(w ++, 20);                                /* u16 len */
++    strcpy((void *) w, "bat_cover");          /* char name[12] */
++    w += 6;
++    stw_raw(w ++, N800_BAT_COVER_GPIO);               /* u16 gpio */
++    stw_raw(w ++, 0x01);
++    stw_raw(w ++, 0);
++    stw_raw(w ++, 0);
++
++    stw_raw(w ++, OMAP_TAG_GPIO_SWITCH);      /* u16 tag */
++    stw_raw(w ++, 20);                                /* u16 len */
++    strcpy((void *) w, "cam_act");            /* char name[12] */
++    w += 6;
++    stw_raw(w ++, N800_CAM_ACT_GPIO);         /* u16 gpio */
++    stw_raw(w ++, 0x20);
++    stw_raw(w ++, 0);
++    stw_raw(w ++, 0);
++
++    stw_raw(w ++, OMAP_TAG_GPIO_SWITCH);      /* u16 tag */
++    stw_raw(w ++, 20);                                /* u16 len */
++    strcpy((void *) w, "cam_turn");           /* char name[12] */
++    w += 6;
++    stw_raw(w ++, N800_CAM_TURN_GPIO);                /* u16 gpio */
++    stw_raw(w ++, 0x21);
++    stw_raw(w ++, 0);
++    stw_raw(w ++, 0);
++
++    stw_raw(w ++, OMAP_TAG_GPIO_SWITCH);      /* u16 tag */
++    stw_raw(w ++, 20);                                /* u16 len */
++    strcpy((void *) w, "headphone");          /* char name[12] */
++    w += 6;
++    stw_raw(w ++, N800_HEADPHONE_GPIO);               /* u16 gpio */
++    stw_raw(w ++, 0x11);
++    stw_raw(w ++, 0);
++    stw_raw(w ++, 0);
++
++    stw_raw(w ++, OMAP_TAG_NOKIA_BT);         /* u16 tag */
++    stw_raw(w ++, 12);                                /* u16 len */
++    b = (void *) w;
++    stb_raw(b ++, 0x01);                      /* u8 chip_type (CSR) */
++    stb_raw(b ++, N800_BT_WKUP_GPIO);         /* u8 bt_wakeup_gpio */
++    stb_raw(b ++, N800_BT_HOST_WKUP_GPIO);    /* u8 host_wakeup_gpio */
++    stb_raw(b ++, N800_BT_RESET_GPIO);                /* u8 reset_gpio */
++    stb_raw(b ++, 1);                         /* u8 bt_uart */
++    memset(b, 0, 6);                          /* u8 bd_addr[6] */
++    b += 6;
++    stb_raw(b ++, 0x02);                      /* u8 bt_sysclk (38.4) */
++    w = (void *) b;
++
++    stw_raw(w ++, OMAP_TAG_WLAN_CX3110X);     /* u16 tag */
++    stw_raw(w ++, 8);                         /* u16 len */
++    stw_raw(w ++, 0x25);                      /* u8 chip_type */
++    stw_raw(w ++, N800_WLAN_PWR_GPIO);                /* s16 power_gpio */
++    stw_raw(w ++, N800_WLAN_IRQ_GPIO);                /* s16 irq_gpio */
++    stw_raw(w ++, -1);                                /* s16 spi_cs_gpio */
++
++    stw_raw(w ++, OMAP_TAG_MMC);              /* u16 tag */
++    stw_raw(w ++, 16);                                /* u16 len */
++    stw_raw(w ++, 0xf);                               /* unsigned flags */
++    stw_raw(w ++, -1);                                /* s16 power_pin */
++    stw_raw(w ++, -1);                                /* s16 switch_pin */
++    stw_raw(w ++, -1);                                /* s16 wp_pin */
++    stw_raw(w ++, 0);                         /* unsigned flags */
++    stw_raw(w ++, 0);                         /* s16 power_pin */
++    stw_raw(w ++, 0);                         /* s16 switch_pin */
++    stw_raw(w ++, 0);                         /* s16 wp_pin */
++
++    stw_raw(w ++, OMAP_TAG_TEA5761);          /* u16 tag */
++    stw_raw(w ++, 4);                         /* u16 len */
++    stw_raw(w ++, N800_TEA5761_CS_GPIO);      /* u16 enable_gpio */
++    w ++;
++
++    stw_raw(w ++, OMAP_TAG_PARTITION);                /* u16 tag */
++    stw_raw(w ++, 28);                                /* u16 len */
++    strcpy((void *) w, "bootloader");         /* char name[16] */
++    l = (void *) (w + 8);
++    stl_raw(l ++, 0x00020000);                        /* unsigned int size */
++    stl_raw(l ++, 0x00000000);                        /* unsigned int offset */
++    stl_raw(l ++, 0x3);                               /* unsigned int mask_flags */
++    w = (void *) l;
++
++    stw_raw(w ++, OMAP_TAG_PARTITION);                /* u16 tag */
++    stw_raw(w ++, 28);                                /* u16 len */
++    strcpy((void *) w, "config");             /* char name[16] */
++    l = (void *) (w + 8);
++    stl_raw(l ++, 0x00060000);                        /* unsigned int size */
++    stl_raw(l ++, 0x00020000);                        /* unsigned int offset */
++    stl_raw(l ++, 0x0);                               /* unsigned int mask_flags */
++    w = (void *) l;
++
++    stw_raw(w ++, OMAP_TAG_PARTITION);                /* u16 tag */
++    stw_raw(w ++, 28);                                /* u16 len */
++    strcpy((void *) w, "kernel");             /* char name[16] */
++    l = (void *) (w + 8);
++    stl_raw(l ++, 0x00200000);                        /* unsigned int size */
++    stl_raw(l ++, 0x00080000);                        /* unsigned int offset */
++    stl_raw(l ++, 0x0);                               /* unsigned int mask_flags */
++    w = (void *) l;
++
++    stw_raw(w ++, OMAP_TAG_PARTITION);                /* u16 tag */
++    stw_raw(w ++, 28);                                /* u16 len */
++    strcpy((void *) w, "initfs");             /* char name[16] */
++    l = (void *) (w + 8);
++    stl_raw(l ++, 0x00200000);                        /* unsigned int size */
++    stl_raw(l ++, 0x00280000);                        /* unsigned int offset */
++    stl_raw(l ++, 0x3);                               /* unsigned int mask_flags */
++    w = (void *) l;
++
++    stw_raw(w ++, OMAP_TAG_PARTITION);                /* u16 tag */
++    stw_raw(w ++, 28);                                /* u16 len */
++    strcpy((void *) w, "rootfs");             /* char name[16] */
++    l = (void *) (w + 8);
++    stl_raw(l ++, 0x0fb80000);                        /* unsigned int size */
++    stl_raw(l ++, 0x00480000);                        /* unsigned int offset */
++    stl_raw(l ++, 0x3);                               /* unsigned int mask_flags */
++    w = (void *) l;
++
++    stw_raw(w ++, OMAP_TAG_BOOT_REASON);      /* u16 tag */
++    stw_raw(w ++, 12);                                /* u16 len */
++#if 0
++    strcpy((void *) w, "por");                        /* char reason_str[12] */
++    strcpy((void *) w, "charger");            /* char reason_str[12] */
++    strcpy((void *) w, "32wd_to");            /* char reason_str[12] */
++    strcpy((void *) w, "sw_rst");             /* char reason_str[12] */
++    strcpy((void *) w, "mbus");                       /* char reason_str[12] */
++    strcpy((void *) w, "unknown");            /* char reason_str[12] */
++    strcpy((void *) w, "swdg_to");            /* char reason_str[12] */
++    strcpy((void *) w, "sec_vio");            /* char reason_str[12] */
++    strcpy((void *) w, "pwr_key");            /* char reason_str[12] */
++    strcpy((void *) w, "rtc_alarm");          /* char reason_str[12] */
++#else
++    strcpy((void *) w, "pwr_key");            /* char reason_str[12] */
++#endif
++    w += 6;
++
++    stw_raw(w ++, OMAP_TAG_VERSION_STR);      /* u16 tag */
++    stw_raw(w ++, 24);                                /* u16 len */
++    strcpy((void *) w, "product");            /* char component[12] */
++    w += 6;
++    strcpy((void *) w, "RX-34");              /* char version[12] */
++    w += 6;
++
++    stw_raw(w ++, OMAP_TAG_VERSION_STR);      /* u16 tag */
++    stw_raw(w ++, 24);                                /* u16 len */
++    strcpy((void *) w, "hw-build");           /* char component[12] */
++    w += 6;
++    strcpy((void *) w, "QEMU");                       /* char version[12] */
++    w += 6;
++
++    stw_raw(w ++, OMAP_TAG_VERSION_STR);      /* u16 tag */
++    stw_raw(w ++, 24);                                /* u16 len */
++    strcpy((void *) w, "nolo");                       /* char component[12] */
++    w += 6;
++    strcpy((void *) w, "1.1.6-qemu");         /* char version[12] */
++    w += 6;
++
++    stw_raw(w ++, OMAP_TAG_LCD);              /* u16 tag */
++    stw_raw(w ++, 36);                                /* u16 len */
++    strcpy((void *) w, "QEMU LCD panel");     /* char panel_name[16] */
++    w += 8;
++    strcpy((void *) w, "blizzard");           /* char ctrl_name[16] */
++    w += 8;
++    stw_raw(w ++, 5);                         /* TODO s16 nreset_gpio */
++    stw_raw(w ++, 16);                                /* u8 data_lines */
++
++    return (void *) w - p;
++}
++
++static struct arm_boot_info n800_binfo = {
++    .loader_start = OMAP2_Q2_BASE,
++    /* Actually two chips of 0x4000000 bytes each */
++    .ram_size = 0x08000000,
++    .board_id = 0x4f7,
++    .atag_board = n800_atag_setup,
++};
++
++static void n800_init(int ram_size, int vga_ram_size,
++                const char *boot_device, DisplayState *ds,
++                const char *kernel_filename, const char *kernel_cmdline,
++                const char *initrd_filename, const char *cpu_model)
++{
++    struct n800_s *s = (struct n800_s *) qemu_mallocz(sizeof(*s));
++    int sdram_size = n800_binfo.ram_size;
++    int onenandram_size = 0x00010000;
++
++    if (ram_size < sdram_size + onenandram_size + OMAP242X_SRAM_SIZE) {
++        fprintf(stderr, "This architecture uses %i bytes of memory\n",
++                        sdram_size + onenandram_size + OMAP242X_SRAM_SIZE);
++        exit(1);
++    }
++
++    s->cpu = omap2420_mpu_init(sdram_size, NULL, cpu_model);
++
++    n800_gpio_setup(s);
++    n800_nand_setup(s);
++    n800_i2c_setup(s);
++    n800_tsc_setup(s);
++    n800_spi_setup(s);
++    n800_dss_setup(s, ds);
++    n800_cbus_setup(s);
++
++    /* Setup initial (reset) machine state */
++
++    /* Start at the OneNAND bootloader.  */
++    s->cpu->env->regs[15] = 0;
++
++    if (kernel_filename) {
++        /* Or at the linux loader.  */
++        n800_binfo.kernel_filename = kernel_filename;
++        n800_binfo.kernel_cmdline = kernel_cmdline;
++        n800_binfo.initrd_filename = initrd_filename;
++        arm_load_kernel(s->cpu->env, &n800_binfo);
++
++        qemu_register_reset(n800_boot_init, s);
++        n800_boot_init(s);
++    }
++
++    dpy_resize(ds, 800, 480);
++}
++
++QEMUMachine n800_machine = {
++    "n800",
++    "Nokia N800 aka. RX-34 tablet (OMAP2420)",
++    n800_init,
++};
+diff --git a/hw/omap.h b/hw/omap.h
+index ecfd54d..de838c9 100644
+--- a/hw/omap.h
++++ b/hw/omap.h
+@@ -22,6 +22,7 @@
+ # define hw_omap_h            "omap.h"
+ # define OMAP_EMIFS_BASE      0x00000000
++# define OMAP2_Q0_BASE                0x00000000
+ # define OMAP_CS0_BASE                0x00000000
+ # define OMAP_CS1_BASE                0x04000000
+ # define OMAP_CS2_BASE                0x08000000
+@@ -29,18 +30,26 @@
+ # define OMAP_EMIFF_BASE      0x10000000
+ # define OMAP_IMIF_BASE               0x20000000
+ # define OMAP_LOCALBUS_BASE   0x30000000
++# define OMAP2_Q1_BASE                0x40000000
++# define OMAP2_L4_BASE                0x48000000
++# define OMAP2_SRAM_BASE      0x40200000
++# define OMAP2_L3_BASE                0x68000000
++# define OMAP2_Q2_BASE                0x80000000
++# define OMAP2_Q3_BASE                0xc0000000
+ # define OMAP_MPUI_BASE               0xe1000000
+ # define OMAP730_SRAM_SIZE    0x00032000
+ # define OMAP15XX_SRAM_SIZE   0x00030000
+ # define OMAP16XX_SRAM_SIZE   0x00004000
+ # define OMAP1611_SRAM_SIZE   0x0003e800
++# define OMAP242X_SRAM_SIZE   0x000a0000
++# define OMAP243X_SRAM_SIZE   0x00010000
+ # define OMAP_CS0_SIZE                0x04000000
+ # define OMAP_CS1_SIZE                0x04000000
+ # define OMAP_CS2_SIZE                0x04000000
+ # define OMAP_CS3_SIZE                0x04000000
+-/* omap1_clk.c */
++/* omap_clk.c */
+ struct omap_mpu_state_s;
+ typedef struct clk *omap_clk;
+ omap_clk omap_findclk(struct omap_mpu_state_s *mpu, const char *name);
+@@ -55,14 +64,41 @@ int64_t omap_clk_getrate(omap_clk clk);
+ void omap_clk_reparent(omap_clk clk, omap_clk parent);
+ /* omap[123].c */
++struct omap_l4_s;
++struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num);
++
++struct omap_target_agent_s;
++struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, int cs);
++target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region,
++                int iotype);
++
+ struct omap_intr_handler_s;
+ struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+-                unsigned long size, unsigned char nbanks,
++                unsigned long size, unsigned char nbanks, qemu_irq **pins,
+                 qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk);
+-
+-struct omap_target_agent_s;
+-static inline target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta,
+-                int region, int iotype) { return 0; }
++struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base,
++                int size, int nbanks, qemu_irq **pins,
++                qemu_irq parent_irq, qemu_irq parent_fiq,
++                omap_clk fclk, omap_clk iclk);
++void omap_inth_reset(struct omap_intr_handler_s *s);
++
++struct omap_prcm_s;
++struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
++                qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
++                struct omap_mpu_state_s *mpu);
++
++struct omap_sysctl_s;
++struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
++                omap_clk iclk, struct omap_mpu_state_s *mpu);
++
++struct omap_sdrc_s;
++struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base);
++
++struct omap_gpmc_s;
++struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq);
++void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
++                void (*base_upd)(void *opaque, target_phys_addr_t new),
++                void (*unmap)(void *opaque), void *opaque);
+ /*
+  * Common IRQ numbers for level 1 interrupt handler
+@@ -295,10 +331,20 @@ static inline target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta,
+  * OMAP-24xx common IRQ numbers
+  */
+ # define OMAP_INT_24XX_SYS_NIRQ               7
++# define OMAP_INT_24XX_L3_IRQ         10
++# define OMAP_INT_24XX_PRCM_MPU_IRQ   11
+ # define OMAP_INT_24XX_SDMA_IRQ0      12
+ # define OMAP_INT_24XX_SDMA_IRQ1      13
+ # define OMAP_INT_24XX_SDMA_IRQ2      14
+ # define OMAP_INT_24XX_SDMA_IRQ3      15
++# define OMAP_INT_243X_MCBSP2_IRQ     16
++# define OMAP_INT_243X_MCBSP3_IRQ     17
++# define OMAP_INT_243X_MCBSP4_IRQ     18
++# define OMAP_INT_243X_MCBSP5_IRQ     19
++# define OMAP_INT_24XX_GPMC_IRQ               20
++# define OMAP_INT_24XX_GUFFAW_IRQ     21
++# define OMAP_INT_24XX_IVA_IRQ                22
++# define OMAP_INT_24XX_EAC_IRQ                23
+ # define OMAP_INT_24XX_CAM_IRQ                24
+ # define OMAP_INT_24XX_DSS_IRQ                25
+ # define OMAP_INT_24XX_MAIL_U0_MPU    26
+@@ -308,8 +354,10 @@ static inline target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta,
+ # define OMAP_INT_24XX_GPIO_BANK2     30
+ # define OMAP_INT_24XX_GPIO_BANK3     31
+ # define OMAP_INT_24XX_GPIO_BANK4     32
+-# define OMAP_INT_24XX_GPIO_BANK5     33
++# define OMAP_INT_243X_GPIO_BANK5     33
+ # define OMAP_INT_24XX_MAIL_U3_MPU    34
++# define OMAP_INT_24XX_WDT3           35
++# define OMAP_INT_24XX_WDT4           36
+ # define OMAP_INT_24XX_GPTIMER1               37
+ # define OMAP_INT_24XX_GPTIMER2               38
+ # define OMAP_INT_24XX_GPTIMER3               39
+@@ -322,10 +370,24 @@ static inline target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta,
+ # define OMAP_INT_24XX_GPTIMER10      46
+ # define OMAP_INT_24XX_GPTIMER11      47
+ # define OMAP_INT_24XX_GPTIMER12      48
++# define OMAP_INT_24XX_PKA_IRQ                50
++# define OMAP_INT_24XX_SHA1MD5_IRQ    51
++# define OMAP_INT_24XX_RNG_IRQ                52
++# define OMAP_INT_24XX_MG_IRQ         53
++# define OMAP_INT_24XX_I2C1_IRQ               56
++# define OMAP_INT_24XX_I2C2_IRQ               57
+ # define OMAP_INT_24XX_MCBSP1_IRQ_TX  59
+ # define OMAP_INT_24XX_MCBSP1_IRQ_RX  60
+ # define OMAP_INT_24XX_MCBSP2_IRQ_TX  62
+ # define OMAP_INT_24XX_MCBSP2_IRQ_RX  63
++# define OMAP_INT_243X_MCBSP1_IRQ     64
++# define OMAP_INT_24XX_MCSPI1_IRQ     65
++# define OMAP_INT_24XX_MCSPI2_IRQ     66
++# define OMAP_INT_24XX_SSI1_IRQ0      67
++# define OMAP_INT_24XX_SSI1_IRQ1      68
++# define OMAP_INT_24XX_SSI2_IRQ0      69
++# define OMAP_INT_24XX_SSI2_IRQ1      70
++# define OMAP_INT_24XX_SSI_GDD_IRQ    71
+ # define OMAP_INT_24XX_UART1_IRQ      72
+ # define OMAP_INT_24XX_UART2_IRQ      73
+ # define OMAP_INT_24XX_UART3_IRQ      74
+@@ -335,10 +397,15 @@ static inline target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta,
+ # define OMAP_INT_24XX_USB_IRQ_HGEN   78
+ # define OMAP_INT_24XX_USB_IRQ_HSOF   79
+ # define OMAP_INT_24XX_USB_IRQ_OTG    80
++# define OMAP_INT_24XX_VLYNQ_IRQ      81
+ # define OMAP_INT_24XX_MMC_IRQ                83
++# define OMAP_INT_24XX_MS_IRQ         84
++# define OMAP_INT_24XX_FAC_IRQ                85
++# define OMAP_INT_24XX_MCSPI3_IRQ     91
+ # define OMAP_INT_243X_HS_USB_MC      92
+ # define OMAP_INT_243X_HS_USB_DMA     93
+ # define OMAP_INT_243X_CARKIT         94
++# define OMAP_INT_34XX_GPTIMER12      95
+ /* omap_dma.c */
+ enum omap_dma_model {
+@@ -352,6 +419,9 @@ struct omap_dma_s;
+ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+                 qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk,
+                 enum omap_dma_model model);
++struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs,
++                struct omap_mpu_state_s *mpu, int fifo,
++                int chans, omap_clk iclk, omap_clk fclk);
+ void omap_dma_reset(struct omap_dma_s *s);
+ struct dma_irq_map {
+@@ -367,7 +437,7 @@ enum omap_dma_port {
+     tipb,
+     local,    /* omap16xx: ocp_t2 */
+     tipb_mpui,
+-    omap_dma_port_last,
++    __omap_dma_port_last,
+ };
+ typedef enum {
+@@ -488,11 +558,83 @@ struct omap_dma_lcd_channel_s {
+ # define OMAP_DMA_MMC2_RX             55
+ # define OMAP_DMA_CRYPTO_DES_OUT      56
++/*
++ * DMA request numbers for the OMAP2
++ */
++# define OMAP24XX_DMA_NO_DEVICE               0
++# define OMAP24XX_DMA_XTI_DMA         1       /* Not in OMAP2420 */
++# define OMAP24XX_DMA_EXT_DMAREQ0     2
++# define OMAP24XX_DMA_EXT_DMAREQ1     3
++# define OMAP24XX_DMA_GPMC            4
++# define OMAP24XX_DMA_GFX             5       /* Not in OMAP2420 */
++# define OMAP24XX_DMA_DSS             6
++# define OMAP24XX_DMA_VLYNQ_TX                7       /* Not in OMAP2420 */
++# define OMAP24XX_DMA_CWT             8       /* Not in OMAP2420 */
++# define OMAP24XX_DMA_AES_TX          9       /* Not in OMAP2420 */
++# define OMAP24XX_DMA_AES_RX          10      /* Not in OMAP2420 */
++# define OMAP24XX_DMA_DES_TX          11      /* Not in OMAP2420 */
++# define OMAP24XX_DMA_DES_RX          12      /* Not in OMAP2420 */
++# define OMAP24XX_DMA_SHA1MD5_RX      13      /* Not in OMAP2420 */
++# define OMAP24XX_DMA_EXT_DMAREQ2     14
++# define OMAP24XX_DMA_EXT_DMAREQ3     15
++# define OMAP24XX_DMA_EXT_DMAREQ4     16
++# define OMAP24XX_DMA_EAC_AC_RD               17
++# define OMAP24XX_DMA_EAC_AC_WR               18
++# define OMAP24XX_DMA_EAC_MD_UL_RD    19
++# define OMAP24XX_DMA_EAC_MD_UL_WR    20
++# define OMAP24XX_DMA_EAC_MD_DL_RD    21
++# define OMAP24XX_DMA_EAC_MD_DL_WR    22
++# define OMAP24XX_DMA_EAC_BT_UL_RD    23
++# define OMAP24XX_DMA_EAC_BT_UL_WR    24
++# define OMAP24XX_DMA_EAC_BT_DL_RD    25
++# define OMAP24XX_DMA_EAC_BT_DL_WR    26
++# define OMAP24XX_DMA_I2C1_TX         27
++# define OMAP24XX_DMA_I2C1_RX         28
++# define OMAP24XX_DMA_I2C2_TX         29
++# define OMAP24XX_DMA_I2C2_RX         30
++# define OMAP24XX_DMA_MCBSP1_TX               31
++# define OMAP24XX_DMA_MCBSP1_RX               32
++# define OMAP24XX_DMA_MCBSP2_TX               33
++# define OMAP24XX_DMA_MCBSP2_RX               34
++# define OMAP24XX_DMA_SPI1_TX0                35
++# define OMAP24XX_DMA_SPI1_RX0                36
++# define OMAP24XX_DMA_SPI1_TX1                37
++# define OMAP24XX_DMA_SPI1_RX1                38
++# define OMAP24XX_DMA_SPI1_TX2                39
++# define OMAP24XX_DMA_SPI1_RX2                40
++# define OMAP24XX_DMA_SPI1_TX3                41
++# define OMAP24XX_DMA_SPI1_RX3                42
++# define OMAP24XX_DMA_SPI2_TX0                43
++# define OMAP24XX_DMA_SPI2_RX0                44
++# define OMAP24XX_DMA_SPI2_TX1                45
++# define OMAP24XX_DMA_SPI2_RX1                46
++
++# define OMAP24XX_DMA_UART1_TX                49
++# define OMAP24XX_DMA_UART1_RX                50
++# define OMAP24XX_DMA_UART2_TX                51
++# define OMAP24XX_DMA_UART2_RX                52
++# define OMAP24XX_DMA_UART3_TX                53
++# define OMAP24XX_DMA_UART3_RX                54
++# define OMAP24XX_DMA_USB_W2FC_TX0    55
++# define OMAP24XX_DMA_USB_W2FC_RX0    56
++# define OMAP24XX_DMA_USB_W2FC_TX1    57
++# define OMAP24XX_DMA_USB_W2FC_RX1    58
++# define OMAP24XX_DMA_USB_W2FC_TX2    59
++# define OMAP24XX_DMA_USB_W2FC_RX2    60
++# define OMAP24XX_DMA_MMC1_TX         61
++# define OMAP24XX_DMA_MMC1_RX         62
++# define OMAP24XX_DMA_MS              63      /* Not in OMAP2420 */
++# define OMAP24XX_DMA_EXT_DMAREQ5     64
++
+ /* omap[123].c */
+ struct omap_mpu_timer_s;
+ struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base,
+                 qemu_irq irq, omap_clk clk);
++struct omap_gp_timer_s;
++struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
++                qemu_irq irq, omap_clk fclk, omap_clk iclk);
++
+ struct omap_watchdog_timer_s;
+ struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base,
+                 qemu_irq irq, omap_clk clk);
+@@ -501,13 +643,21 @@ struct omap_32khz_timer_s;
+ struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base,
+                 qemu_irq irq, omap_clk clk);
++void omap_synctimer_init(struct omap_target_agent_s *ta,
++                struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk);
++
+ struct omap_tipb_bridge_s;
+ struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base,
+                 qemu_irq abort_irq, omap_clk clk);
+ struct omap_uart_s;
+ struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+-                qemu_irq irq, omap_clk clk, CharDriverState *chr);
++                qemu_irq irq, omap_clk fclk, omap_clk iclk,
++                qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr);
++struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
++                qemu_irq irq, omap_clk fclk, omap_clk iclk,
++                qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr);
++void omap_uart_reset(struct omap_uart_s *s);
+ struct omap_mpuio_s;
+ struct omap_mpuio_s *omap_mpuio_init(target_phys_addr_t base,
+@@ -523,6 +673,12 @@ struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base,
+ qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s);
+ void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler);
++struct omap_gpif_s;
++struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
++                qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules);
++qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start);
++void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler);
++
+ struct uwire_slave_s {
+     uint16_t (*receive)(void *opaque);
+     void (*send)(void *opaque, uint16_t data);
+@@ -534,6 +690,13 @@ struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base,
+ void omap_uwire_attach(struct omap_uwire_s *s,
+                 struct uwire_slave_s *slave, int chipselect);
++struct omap_mcspi_s;
++struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
++                qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk);
++void omap_mcspi_attach(struct omap_mcspi_s *s,
++                uint32_t (*txrx)(void *opaque, uint32_t), void *opaque,
++                int chipselect);
++
+ struct omap_rtc_s;
+ struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base,
+                 qemu_irq *irq, omap_clk clk);
+@@ -570,6 +733,9 @@ void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave);
+ struct omap_lpg_s;
+ struct omap_lpg_s *omap_lpg_init(target_phys_addr_t base, omap_clk clk);
++void omap_tap_init(struct omap_target_agent_s *ta,
++                struct omap_mpu_state_s *mpu);
++
+ /* omap_lcdc.c */
+ struct omap_lcd_panel_s;
+ void omap_lcdc_reset(struct omap_lcd_panel_s *s);
+@@ -577,13 +743,33 @@ struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
+                 struct omap_dma_lcd_channel_s *dma, DisplayState *ds,
+                 ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk);
++/* omap_dss.c */
++struct rfbi_chip_s {
++    void *opaque;
++    void (*write)(void *opaque, int dc, uint16_t value);
++    void (*block)(void *opaque, int dc, void *buf, size_t len, int pitch);
++    uint16_t (*read)(void *opaque, int dc);
++};
++struct omap_dss_s;
++void omap_dss_reset(struct omap_dss_s *s);
++struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
++                target_phys_addr_t l3_base, DisplayState *ds,
++                qemu_irq irq, qemu_irq drq,
++                omap_clk fck1, omap_clk fck2, omap_clk ck54m,
++                omap_clk ick1, omap_clk ick2);
++void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip);
++
+ /* omap_mmc.c */
+ struct omap_mmc_s;
+ struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+                 BlockDriverState *bd,
+                 qemu_irq irq, qemu_irq dma[], omap_clk clk);
++struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
++                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
++                omap_clk fclk, omap_clk iclk);
+ void omap_mmc_reset(struct omap_mmc_s *s);
+ void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover);
++void omap_mmc_enable(struct omap_mmc_s *s, int enable);
+ /* omap_i2c.c */
+ struct omap_i2c_s;
+@@ -596,14 +782,37 @@ i2c_bus *omap_i2c_bus(struct omap_i2c_s *s);
+ # define cpu_is_omap310(cpu)          (cpu->mpu_model == omap310)
+ # define cpu_is_omap1510(cpu)         (cpu->mpu_model == omap1510)
++# define cpu_is_omap1610(cpu)         (cpu->mpu_model == omap1610)
++# define cpu_is_omap1710(cpu)         (cpu->mpu_model == omap1710)
++# define cpu_is_omap2410(cpu)         (cpu->mpu_model == omap2410)
++# define cpu_is_omap2420(cpu)         (cpu->mpu_model == omap2420)
++# define cpu_is_omap2430(cpu)         (cpu->mpu_model == omap2430)
++# define cpu_is_omap3430(cpu)         (cpu->mpu_model == omap3430)
++
+ # define cpu_is_omap15xx(cpu)         \
+         (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu))
+-# define cpu_class_omap1(cpu)         1
++# define cpu_is_omap16xx(cpu)         \
++        (cpu_is_omap1610(cpu) || cpu_is_omap1710(cpu))
++# define cpu_is_omap24xx(cpu)         \
++        (cpu_is_omap2410(cpu) || cpu_is_omap2420(cpu) || cpu_is_omap2430(cpu))
++
++# define cpu_class_omap1(cpu)         \
++        (cpu_is_omap15xx(cpu) || cpu_is_omap16xx(cpu))
++# define cpu_class_omap2(cpu)         cpu_is_omap24xx(cpu)
++# define cpu_class_omap3(cpu)         cpu_is_omap3430(cpu)
+ struct omap_mpu_state_s {
+-    enum omap1_mpu_model {
++    enum omap_mpu_model {
+         omap310,
+         omap1510,
++        omap1610,
++        omap1710,
++        omap2410,
++        omap2420,
++        omap2422,
++        omap2423,
++        omap2430,
++        omap3430,
+     } mpu_model;
+     CPUState *env;
+@@ -620,7 +829,7 @@ struct omap_mpu_state_s {
+                         target_phys_addr_t offset, uint32_t value);
+         int (*addr_valid)(struct omap_mpu_state_s *s,
+                         target_phys_addr_t addr);
+-    } port[omap_dma_port_last];
++    } port[__omap_dma_port_last];
+     unsigned long sdram_size;
+     unsigned long sram_size;
+@@ -656,7 +865,7 @@ struct omap_mpu_state_s {
+         omap_clk clk;
+     } pwt;
+-    struct omap_i2c_s *i2c;
++    struct omap_i2c_s *i2c[2];
+     struct omap_rtc_s *rtc;
+@@ -722,7 +931,38 @@ struct omap_mpu_state_s {
+         uint16_t dsp_idlect2;
+         uint16_t dsp_rstct2;
+     } clkm;
+-} *omap310_mpu_init(unsigned long sdram_size,
++
++    /* OMAP2-only peripherals */
++    struct omap_l4_s *l4;
++
++    struct omap_gp_timer_s *gptimer[12];
++
++    target_phys_addr_t tap_base;
++
++    struct omap_synctimer_s {
++        target_phys_addr_t base;
++        uint32_t val;
++        uint16_t readh;
++    } synctimer;
++
++    struct omap_prcm_s *prcm;
++    struct omap_sdrc_s *sdrc;
++    struct omap_gpmc_s *gpmc;
++    struct omap_sysctl_s *sysc;
++
++    struct omap_gpif_s *gpif;
++
++    struct omap_mcspi_s *mcspi[2];
++
++    struct omap_dss_s *dss;
++};
++
++/* omap1.c */
++struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
++                DisplayState *ds, const char *core);
++
++/* omap2.c */
++struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
+                 DisplayState *ds, const char *core);
+ # if TARGET_PHYS_ADDR_BITS == 32
+@@ -743,24 +983,46 @@ uint32_t omap_badwidth_read32(void *opaque, target_phys_addr_t addr);
+ void omap_badwidth_write32(void *opaque, target_phys_addr_t addr,
+                 uint32_t value);
++void omap_mpu_wakeup(void *opaque, int irq, int req);
++
+ # define OMAP_BAD_REG(paddr)          \
+-        printf("%s: Bad register " OMAP_FMT_plx "\n", __FUNCTION__, paddr)
++        fprintf(stderr, "%s: Bad register " OMAP_FMT_plx "\n",        \
++                        __FUNCTION__, paddr)
+ # define OMAP_RO_REG(paddr)           \
+-        printf("%s: Read-only register " OMAP_FMT_plx "\n",   \
++        fprintf(stderr, "%s: Read-only register " OMAP_FMT_plx "\n",  \
+                         __FUNCTION__, paddr)
++/* OMAP-specific Linux bootloader tags for the ATAG_BOARD area
++   (Board-specifc tags are not here)  */
++#define OMAP_TAG_CLOCK                0x4f01
++#define OMAP_TAG_MMC          0x4f02
++#define OMAP_TAG_SERIAL_CONSOLE       0x4f03
++#define OMAP_TAG_USB          0x4f04
++#define OMAP_TAG_LCD          0x4f05
++#define OMAP_TAG_GPIO_SWITCH  0x4f06
++#define OMAP_TAG_UART         0x4f07
++#define OMAP_TAG_FBMEM                0x4f08
++#define OMAP_TAG_STI_CONSOLE  0x4f09
++#define OMAP_TAG_CAMERA_SENSOR        0x4f0a
++#define OMAP_TAG_PARTITION    0x4f0b
++#define OMAP_TAG_TEA5761      0x4f10
++#define OMAP_TAG_TMP105               0x4f11
++#define OMAP_TAG_BOOT_REASON  0x4f80
++#define OMAP_TAG_FLASH_PART_STR       0x4f81
++#define OMAP_TAG_VERSION_STR  0x4f82
++
+ # define TCMI_VERBOSE                 1
+ //# define MEM_VERBOSE                        1
+ # ifdef TCMI_VERBOSE
+ #  define OMAP_8B_REG(paddr)          \
+-        printf("%s: 8-bit register " OMAP_FMT_plx "\n",       \
++        fprintf(stderr, "%s: 8-bit register " OMAP_FMT_plx "\n",      \
+                         __FUNCTION__, paddr)
+ #  define OMAP_16B_REG(paddr)         \
+-        printf("%s: 16-bit register " OMAP_FMT_plx "\n",      \
++        fprintf(stderr, "%s: 16-bit register " OMAP_FMT_plx "\n",     \
+                         __FUNCTION__, paddr)
+ #  define OMAP_32B_REG(paddr)         \
+-        printf("%s: 32-bit register " OMAP_FMT_plx "\n",      \
++        fprintf(stderr, "%s: 32-bit register " OMAP_FMT_plx "\n",     \
+                         __FUNCTION__, paddr)
+ # else
+ #  define OMAP_8B_REG(paddr)
+@@ -863,10 +1125,4 @@ inline static int debug_register_io_memory(int io_index,
+ #  define cpu_register_io_memory      debug_register_io_memory
+ # endif
+-/* Not really omap specific, but is the only thing that uses the
+-   uwire interface.  */
+-/* tsc210x.c */
+-struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio);
+-struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip);
+-
+ #endif /* hw_omap_h */
+diff --git a/hw/omap1.c b/hw/omap1.c
+index 3888e80..d81cbce 100644
+--- a/hw/omap1.c
++++ b/hw/omap1.c
+@@ -23,10 +23,11 @@
+ #include "omap.h"
+ #include "sysemu.h"
+ #include "qemu-timer.h"
++#include "qemu-char.h"
+ /* We use pc-style serial ports.  */
+ #include "pc.h"
+-/* Should signal the TCMI */
++/* Should signal the TCMI/GPMC */
+ uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr)
+ {
+     uint8_t ret;
+@@ -86,6 +87,7 @@ struct omap_intr_handler_bank_s {
+     uint32_t mask;
+     uint32_t fiq;
+     uint32_t sens_edge;
++    uint32_t swi;
+     unsigned char priority[32];
+ };
+@@ -94,11 +96,14 @@ struct omap_intr_handler_s {
+     qemu_irq parent_intr[2];
+     target_phys_addr_t base;
+     unsigned char nbanks;
++    int level_only;
+     /* state */
+     uint32_t new_agr[2];
+     int sir_intr[2];
+-    struct omap_intr_handler_bank_s banks[];
++    int autoidle;
++    uint32_t mask;
++    struct omap_intr_handler_bank_s bank[];
+ };
+ static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
+@@ -113,11 +118,11 @@ static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
+      * If all interrupts have the same priority, the default order is IRQ_N,
+      * IRQ_N-1,...,IRQ_0. */
+     for (j = 0; j < s->nbanks; ++j) {
+-        level = s->banks[j].irqs & ~s->banks[j].mask &
+-                (is_fiq ? s->banks[j].fiq : ~s->banks[j].fiq);
++        level = s->bank[j].irqs & ~s->bank[j].mask &
++                (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
+         for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f,
+                         level >>= f) {
+-            p = s->banks[j].priority[i];
++            p = s->bank[j].priority[i];
+             if (p <= p_intr) {
+                 p_intr = p;
+                 sir_intr = 32 * j + i;
+@@ -134,10 +139,10 @@ static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq)
+     uint32_t has_intr = 0;
+     for (i = 0; i < s->nbanks; ++i)
+-        has_intr |= s->banks[i].irqs & ~s->banks[i].mask &
+-                (is_fiq ? s->banks[i].fiq : ~s->banks[i].fiq);
++        has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
++                (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
+-    if (s->new_agr[is_fiq] && has_intr) {
++    if (s->new_agr[is_fiq] & has_intr & s->mask) {
+         s->new_agr[is_fiq] = 0;
+         omap_inth_sir_update(s, is_fiq);
+         qemu_set_irq(s->parent_intr[is_fiq], 1);
+@@ -152,13 +157,13 @@ static void omap_set_intr(void *opaque, int irq, int req)
+     struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
+     uint32_t rise;
+-    struct omap_intr_handler_bank_s *bank = &ih->banks[irq >> 5];
++    struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
+     int n = irq & 31;
+     if (req) {
+         rise = ~bank->irqs & (1 << n);
+         if (~bank->sens_edge & (1 << n))
+-            rise &= ~bank->inputs & (1 << n);
++            rise &= ~bank->inputs;
+         bank->inputs |= (1 << n);
+         if (rise) {
+@@ -173,13 +178,33 @@ static void omap_set_intr(void *opaque, int irq, int req)
+     }
+ }
++/* Simplified version with no edge detection */
++static void omap_set_intr_noedge(void *opaque, int irq, int req)
++{
++    struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
++    uint32_t rise;
++
++    struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
++    int n = irq & 31;
++
++    if (req) {
++        rise = ~bank->inputs & (1 << n);
++        if (rise) {
++            bank->irqs |= bank->inputs |= rise;
++            omap_inth_update(ih, 0);
++            omap_inth_update(ih, 1);
++        }
++    } else
++        bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi;
++}
++
+ static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr)
+ {
+     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+     int i, offset = addr - s->base;
+     int bank_no = offset >> 8;
+     int line_no;
+-    struct omap_intr_handler_bank_s *bank = &s->banks[bank_no];
++    struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
+     offset &= 0xff;
+     switch (offset) {
+@@ -194,7 +219,7 @@ static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr)
+         if (bank_no != 0)
+             break;
+         line_no = s->sir_intr[(offset - 0x10) >> 2];
+-        bank = &s->banks[line_no >> 5];
++        bank = &s->bank[line_no >> 5];
+         i = line_no & 31;
+         if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
+             bank->irqs &= ~(1 << i);
+@@ -256,7 +281,7 @@ static void omap_inth_write(void *opaque, target_phys_addr_t addr,
+     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+     int i, offset = addr - s->base;
+     int bank_no = offset >> 8;
+-    struct omap_intr_handler_bank_s *bank = &s->banks[bank_no];
++    struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
+     offset &= 0xff;
+     switch (offset) {
+@@ -360,25 +385,31 @@ void omap_inth_reset(struct omap_intr_handler_s *s)
+     int i;
+     for (i = 0; i < s->nbanks; ++i){
+-        s->banks[i].irqs = 0x00000000;
+-        s->banks[i].mask = 0xffffffff;
+-        s->banks[i].sens_edge = 0x00000000;
+-        s->banks[i].fiq = 0x00000000;
+-        s->banks[i].inputs = 0x00000000;
+-        memset(s->banks[i].priority, 0, sizeof(s->banks[i].priority));
++        s->bank[i].irqs = 0x00000000;
++        s->bank[i].mask = 0xffffffff;
++        s->bank[i].sens_edge = 0x00000000;
++        s->bank[i].fiq = 0x00000000;
++        s->bank[i].inputs = 0x00000000;
++        s->bank[i].swi = 0x00000000;
++        memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
++
++        if (s->level_only)
++            s->bank[i].sens_edge = 0xffffffff;
+     }
+     s->new_agr[0] = ~0;
+     s->new_agr[1] = ~0;
+     s->sir_intr[0] = 0;
+     s->sir_intr[1] = 0;
++    s->autoidle = 0;
++    s->mask = ~0;
+     qemu_set_irq(s->parent_intr[0], 0);
+     qemu_set_irq(s->parent_intr[1], 0);
+ }
+ struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+-                unsigned long size, unsigned char nbanks,
++                unsigned long size, unsigned char nbanks, qemu_irq **pins,
+                 qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk)
+ {
+     int iomemtype;
+@@ -391,6 +422,8 @@ struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+     s->base = base;
+     s->nbanks = nbanks;
+     s->pins = qemu_allocate_irqs(omap_set_intr, s, nbanks * 32);
++    if (pins)
++        *pins = s->pins;
+     omap_inth_reset(s);
+@@ -401,6 +434,227 @@ struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+     return s;
+ }
++static uint32_t omap2_inth_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
++    int offset = addr - s->base;
++    int bank_no, line_no;
++    struct omap_intr_handler_bank_s *bank = 0;
++
++    if ((offset & 0xf80) == 0x80) {
++        bank_no = (offset & 0x60) >> 5;
++        if (bank_no < s->nbanks) {
++            offset &= ~0x60;
++            bank = &s->bank[bank_no];
++        }
++    }
++
++    switch (offset) {
++    case 0x00:        /* INTC_REVISION */
++        return 0x21;
++
++    case 0x10:        /* INTC_SYSCONFIG */
++        return (s->autoidle >> 2) & 1;
++
++    case 0x14:        /* INTC_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x40:        /* INTC_SIR_IRQ */
++        return s->sir_intr[0];
++
++    case 0x44:        /* INTC_SIR_FIQ */
++        return s->sir_intr[1];
++
++    case 0x48:        /* INTC_CONTROL */
++        return (!s->mask) << 2;                                       /* GLOBALMASK */
++
++    case 0x4c:        /* INTC_PROTECTION */
++        return 0;
++
++    case 0x50:        /* INTC_IDLE */
++        return s->autoidle & 3;
++
++    /* Per-bank registers */
++    case 0x80:        /* INTC_ITR */
++        return bank->inputs;
++
++    case 0x84:        /* INTC_MIR */
++        return bank->mask;
++
++    case 0x88:        /* INTC_MIR_CLEAR */
++    case 0x8c:        /* INTC_MIR_SET */
++        return 0;
++
++    case 0x90:        /* INTC_ISR_SET */
++        return bank->swi;
++
++    case 0x94:        /* INTC_ISR_CLEAR */
++        return 0;
++
++    case 0x98:        /* INTC_PENDING_IRQ */
++        return bank->irqs & ~bank->mask & ~bank->fiq;
++
++    case 0x9c:        /* INTC_PENDING_FIQ */
++        return bank->irqs & ~bank->mask & bank->fiq;
++
++    /* Per-line registers */
++    case 0x100 ... 0x300:     /* INTC_ILR */
++        bank_no = (offset - 0x100) >> 7;
++        if (bank_no > s->nbanks)
++            break;
++        bank = &s->bank[bank_no];
++        line_no = (offset & 0x7f) >> 2;
++        return (bank->priority[line_no] << 2) |
++                ((bank->fiq >> line_no) & 1);
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap2_inth_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
++    int offset = addr - s->base;
++    int bank_no, line_no;
++    struct omap_intr_handler_bank_s *bank = 0;
++
++    if ((offset & 0xf80) == 0x80) {
++        bank_no = (offset & 0x60) >> 5;
++        if (bank_no < s->nbanks) {
++            offset &= ~0x60;
++            bank = &s->bank[bank_no];
++        }
++    }
++
++    switch (offset) {
++    case 0x10:        /* INTC_SYSCONFIG */
++        s->autoidle &= 4;
++        s->autoidle |= (value & 1) << 2;
++        if (value & 2)                                                /* SOFTRESET */
++            omap_inth_reset(s);
++        return;
++
++    case 0x48:        /* INTC_CONTROL */
++        s->mask = (value & 4) ? 0 : ~0;                               /* GLOBALMASK */
++        if (value & 2) {                                      /* NEWFIQAGR */
++            qemu_set_irq(s->parent_intr[1], 0);
++            s->new_agr[1] = ~0;
++            omap_inth_update(s, 1);
++        }
++        if (value & 1) {                                      /* NEWIRQAGR */
++            qemu_set_irq(s->parent_intr[0], 0);
++            s->new_agr[0] = ~0;
++            omap_inth_update(s, 0);
++        }
++        return;
++
++    case 0x4c:        /* INTC_PROTECTION */
++        /* TODO: Make a bitmap (or sizeof(char)map) of access privileges
++         * for every register, see Chapter 3 and 4 for privileged mode.  */
++        if (value & 1)
++            fprintf(stderr, "%s: protection mode enable attempt\n",
++                            __FUNCTION__);
++        return;
++
++    case 0x50:        /* INTC_IDLE */
++        s->autoidle &= ~3;
++        s->autoidle |= value & 3;
++        return;
++
++    /* Per-bank registers */
++    case 0x84:        /* INTC_MIR */
++        bank->mask = value;
++        omap_inth_update(s, 0);
++        omap_inth_update(s, 1);
++        return;
++
++    case 0x88:        /* INTC_MIR_CLEAR */
++        bank->mask &= ~value;
++        omap_inth_update(s, 0);
++        omap_inth_update(s, 1);
++        return;
++
++    case 0x8c:        /* INTC_MIR_SET */
++        bank->mask |= value;
++        return;
++
++    case 0x90:        /* INTC_ISR_SET */
++        bank->irqs |= bank->swi |= value;
++        omap_inth_update(s, 0);
++        omap_inth_update(s, 1);
++        return;
++
++    case 0x94:        /* INTC_ISR_CLEAR */
++        bank->swi &= ~value;
++        bank->irqs = bank->swi & bank->inputs;
++        return;
++
++    /* Per-line registers */
++    case 0x100 ... 0x300:     /* INTC_ILR */
++        bank_no = (offset - 0x100) >> 7;
++        if (bank_no > s->nbanks)
++            break;
++        bank = &s->bank[bank_no];
++        line_no = (offset & 0x7f) >> 2;
++        bank->priority[line_no] = (value >> 2) & 0x3f;
++        bank->fiq &= ~(1 << line_no);
++        bank->fiq |= (value & 1) << line_no;
++        return;
++
++    case 0x00:        /* INTC_REVISION */
++    case 0x14:        /* INTC_SYSSTATUS */
++    case 0x40:        /* INTC_SIR_IRQ */
++    case 0x44:        /* INTC_SIR_FIQ */
++    case 0x80:        /* INTC_ITR */
++    case 0x98:        /* INTC_PENDING_IRQ */
++    case 0x9c:        /* INTC_PENDING_FIQ */
++        OMAP_RO_REG(addr);
++        return;
++    }
++    OMAP_BAD_REG(addr);
++}
++
++static CPUReadMemoryFunc *omap2_inth_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap2_inth_read,
++};
++
++static CPUWriteMemoryFunc *omap2_inth_writefn[] = {
++    omap2_inth_write,
++    omap2_inth_write,
++    omap2_inth_write,
++};
++
++struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base,
++                int size, int nbanks, qemu_irq **pins,
++                qemu_irq parent_irq, qemu_irq parent_fiq,
++                omap_clk fclk, omap_clk iclk)
++{
++    int iomemtype;
++    struct omap_intr_handler_s *s = (struct omap_intr_handler_s *)
++            qemu_mallocz(sizeof(struct omap_intr_handler_s) +
++                            sizeof(struct omap_intr_handler_bank_s) * nbanks);
++
++    s->parent_intr[0] = parent_irq;
++    s->parent_intr[1] = parent_fiq;
++    s->base = base;
++    s->nbanks = nbanks;
++    s->level_only = 1;
++    s->pins = qemu_allocate_irqs(omap_set_intr_noedge, s, nbanks * 32);
++    if (pins)
++        *pins = s->pins;
++
++    omap_inth_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap2_inth_readfn,
++                    omap2_inth_writefn, s);
++    cpu_register_physical_memory(s->base, size, iomemtype);
++
++    return s;
++}
++
+ /* MPU OS timers */
+ struct omap_mpu_timer_s {
+     qemu_irq irq;
+@@ -1289,6 +1543,8 @@ static uint32_t omap_id_read(void *opaque, target_phys_addr_t addr)
+             return 0x03310315;
+         case omap1510:
+             return 0x03310115;
++        default:
++            cpu_abort(cpu_single_env, "%s: bad mpu model\n", __FUNCTION__);
+         }
+         break;
+@@ -1298,6 +1554,8 @@ static uint32_t omap_id_read(void *opaque, target_phys_addr_t addr)
+             return 0xfb57402f;
+         case omap1510:
+             return 0xfb47002f;
++        default:
++            cpu_abort(cpu_single_env, "%s: bad mpu model\n", __FUNCTION__);
+         }
+         break;
+     }
+@@ -1722,19 +1980,116 @@ static void omap_dpll_init(struct dpll_ctl_s *s, target_phys_addr_t base,
+ /* UARTs */
+ struct omap_uart_s {
+     SerialState *serial; /* TODO */
++    struct omap_target_agent_s *ta;
++    target_phys_addr_t base;
++
++    uint8_t eblr;
++    uint8_t syscontrol;
++    uint8_t wkup;
++    uint8_t cfps;
+ };
+-static void omap_uart_reset(struct omap_uart_s *s)
++void omap_uart_reset(struct omap_uart_s *s)
+ {
++    s->eblr = 0x00;
++    s->syscontrol = 0;
++    s->wkup = 0x3f;
++    s->cfps = 0x69;
+ }
+ struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+-                qemu_irq irq, omap_clk clk, CharDriverState *chr)
++                qemu_irq irq, omap_clk fclk, omap_clk iclk,
++                qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr)
+ {
+     struct omap_uart_s *s = (struct omap_uart_s *)
+             qemu_mallocz(sizeof(struct omap_uart_s));
+-    if (chr)
+-        s->serial = serial_mm_init(base, 2, irq, chr, 1);
++
++    s->serial = serial_mm_init(base, 2, irq, chr ?: qemu_chr_open("null"), 1);
++
++    return s;
++}
++
++static uint32_t omap_uart_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x48:        /* EBLR */
++        return s->eblr;
++    case 0x50:        /* MVR */
++        return 0x30;
++    case 0x54:        /* SYSC */
++        return s->syscontrol;
++    case 0x58:        /* SYSS */
++        return 1;
++    case 0x5c:        /* WER */
++        return s->wkup;
++    case 0x60:        /* CFPS */
++        return s->cfps;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_uart_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x48:        /* EBLR */
++        s->eblr = value & 0xff;
++        break;
++    case 0x50:        /* MVR */
++    case 0x58:        /* SYSS */
++        OMAP_RO_REG(addr);
++        break;
++    case 0x54:        /* SYSC */
++        s->syscontrol = value & 0x1d;
++        if (value & 2)
++            omap_uart_reset(s);
++        break;
++    case 0x5c:        /* WER */
++        s->wkup = value & 0x7f;
++        break;
++    case 0x60:        /* CFPS */
++        s->cfps = value & 0xff;
++        break;
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_uart_readfn[] = {
++    omap_uart_read,
++    omap_uart_read,
++    omap_badwidth_read8,
++};
++
++static CPUWriteMemoryFunc *omap_uart_writefn[] = {
++    omap_uart_write,
++    omap_uart_write,
++    omap_badwidth_write8,
++};
++
++struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
++                qemu_irq irq, omap_clk fclk, omap_clk iclk,
++                qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr)
++{
++    target_phys_addr_t base = omap_l4_attach(ta, 0, 0);
++    struct omap_uart_s *s = omap_uart_init(base, irq,
++                    fclk, iclk, txdma, rxdma, chr);
++    int iomemtype = cpu_register_io_memory(0, omap_uart_readfn,
++                    omap_uart_writefn, s);
++
++    s->ta = ta;
++    s->base = base;
++
++    cpu_register_physical_memory(s->base + 0x20, 0x100, iomemtype);
++
+     return s;
+ }
+@@ -2778,9 +3133,11 @@ struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base,
+ void omap_uwire_attach(struct omap_uwire_s *s,
+                 struct uwire_slave_s *slave, int chipselect)
+ {
+-    if (chipselect < 0 || chipselect > 3)
+-        cpu_abort(cpu_single_env, "%s: Bad chipselect %i\n", __FUNCTION__,
+-                        chipselect);
++    if (chipselect < 0 || chipselect > 3) {
++        fprintf(stderr, "%s: Bad chipselect %i\n",
++                        __FUNCTION__, chipselect);
++        exit(-1);
++    }
+     s->chip[chipselect] = slave;
+ }
+@@ -4123,7 +4481,7 @@ static void omap_setup_mpui_io(struct omap_mpu_state_s *mpu)
+ }
+ /* General chip reset */
+-static void omap_mpu_reset(void *opaque)
++static void omap1_mpu_reset(void *opaque)
+ {
+     struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+@@ -4153,7 +4511,7 @@ static void omap_mpu_reset(void *opaque)
+     omap_uwire_reset(mpu->microwire);
+     omap_pwl_reset(mpu);
+     omap_pwt_reset(mpu);
+-    omap_i2c_reset(mpu->i2c);
++    omap_i2c_reset(mpu->i2c[0]);
+     omap_rtc_reset(mpu->rtc);
+     omap_mcbsp_reset(mpu->mcbsp1);
+     omap_mcbsp_reset(mpu->mcbsp2);
+@@ -4205,7 +4563,7 @@ static void omap_setup_dsp_mapping(const struct omap_map_s *map)
+     }
+ }
+-static void omap_mpu_wakeup(void *opaque, int irq, int req)
++void omap_mpu_wakeup(void *opaque, int irq, int req)
+ {
+     struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+@@ -4213,7 +4571,7 @@ static void omap_mpu_wakeup(void *opaque, int irq, int req)
+         cpu_interrupt(mpu->env, CPU_INTERRUPT_EXITTB);
+ }
+-static const struct dma_irq_map omap_dma_irq_map[] = {
++static const struct dma_irq_map omap1_dma_irq_map[] = {
+     { 0, OMAP_INT_DMA_CH0_6 },
+     { 0, OMAP_INT_DMA_CH1_7 },
+     { 0, OMAP_INT_DMA_CH2_8 },
+@@ -4307,17 +4665,16 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+     omap_clkm_init(0xfffece00, 0xe1008000, s);
+     cpu_irq = arm_pic_init_cpu(s->env);
+-    s->ih[0] = omap_inth_init(0xfffecb00, 0x100, 1,
++    s->ih[0] = omap_inth_init(0xfffecb00, 0x100, 1, &s->irq[0],
+                     cpu_irq[ARM_PIC_CPU_IRQ], cpu_irq[ARM_PIC_CPU_FIQ],
+                     omap_findclk(s, "arminth_ck"));
+-    s->ih[1] = omap_inth_init(0xfffe0000, 0x800, 1,
++    s->ih[1] = omap_inth_init(0xfffe0000, 0x800, 1, &s->irq[1],
+                     s->ih[0]->pins[OMAP_INT_15XX_IH2_IRQ], NULL,
+                     omap_findclk(s, "arminth_ck"));
+-    s->irq[0] = s->ih[0]->pins;
+-    s->irq[1] = s->ih[1]->pins;
+     for (i = 0; i < 6; i ++)
+-        dma_irqs[i] = s->irq[omap_dma_irq_map[i].ih][omap_dma_irq_map[i].intr];
++        dma_irqs[i] =
++                s->irq[omap1_dma_irq_map[i].ih][omap1_dma_irq_map[i].intr];
+     s->dma = omap_dma_init(0xfffed800, dma_irqs, s->irq[0][OMAP_INT_DMA_LCD],
+                            s, omap_findclk(s, "dma_ck"), omap_dma_3_1);
+@@ -4367,12 +4724,18 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+     s->uart[0] = omap_uart_init(0xfffb0000, s->irq[1][OMAP_INT_UART1],
+                     omap_findclk(s, "uart1_ck"),
++                    omap_findclk(s, "uart1_ck"),
++                    s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX],
+                     serial_hds[0]);
+     s->uart[1] = omap_uart_init(0xfffb0800, s->irq[1][OMAP_INT_UART2],
+                     omap_findclk(s, "uart2_ck"),
++                    omap_findclk(s, "uart2_ck"),
++                    s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX],
+                     serial_hds[0] ? serial_hds[1] : 0);
+     s->uart[2] = omap_uart_init(0xe1019800, s->irq[0][OMAP_INT_UART3],
+                     omap_findclk(s, "uart3_ck"),
++                    omap_findclk(s, "uart3_ck"),
++                    s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX],
+                     serial_hds[0] && serial_hds[1] ? serial_hds[2] : 0);
+     omap_dpll_init(&s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1"));
+@@ -4401,7 +4764,7 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+     omap_pwl_init(0xfffb5800, s, omap_findclk(s, "armxor_ck"));
+     omap_pwt_init(0xfffb6000, s, omap_findclk(s, "armxor_ck"));
+-    s->i2c = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C],
++    s->i2c[0] = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C],
+                     &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck"));
+     s->rtc = omap_rtc_init(0xfffb4800, &s->irq[1][OMAP_INT_RTC_TIMER],
+@@ -4435,7 +4798,7 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+     omap_setup_dsp_mapping(omap15xx_dsp_mm);
+     omap_setup_mpui_io(s);
+-    qemu_register_reset(omap_mpu_reset, s);
++    qemu_register_reset(omap1_mpu_reset, s);
+     return s;
+ }
+diff --git a/hw/omap2.c b/hw/omap2.c
+new file mode 100644
+index 0000000..1e51197
+--- /dev/null
++++ b/hw/omap2.c
+@@ -0,0 +1,3872 @@
++/*
++ * TI OMAP processors emulation.
++ *
++ * Copyright (C) 2007-2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++#include "hw.h"
++#include "arm-misc.h"
++#include "omap.h"
++#include "sysemu.h"
++#include "qemu-timer.h"
++#include "qemu-char.h"
++#include "flash.h"
++/* We use pc-style serial ports.  */
++#include "pc.h"
++
++/* GP timers */
++struct omap_gp_timer_s {
++    qemu_irq irq;
++    qemu_irq wkup;
++    qemu_irq in;
++    qemu_irq out;
++    omap_clk clk;
++    target_phys_addr_t base;
++    QEMUTimer *timer;
++    QEMUTimer *match;
++    struct omap_target_agent_s *ta;
++
++    int in_val;
++    int out_val;
++    int64_t time;
++    int64_t rate;
++    int64_t ticks_per_sec;
++
++    int16_t config;
++    int status;
++    int it_ena;
++    int wu_ena;
++    int enable;
++    int inout;
++    int capt2;
++    int pt;
++    enum {
++        gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
++    } trigger;
++    enum {
++        gpt_capture_none, gpt_capture_rising,
++        gpt_capture_falling, gpt_capture_both
++    } capture;
++    int scpwm;
++    int ce;
++    int pre;
++    int ptv;
++    int ar;
++    int st;
++    int posted;
++    uint32_t val;
++    uint32_t load_val;
++    uint32_t capture_val[2];
++    uint32_t match_val;
++    int capt_num;
++
++    uint16_t writeh;  /* LSB */
++    uint16_t readh;   /* MSB */
++};
++
++#define GPT_TCAR_IT   (1 << 2)
++#define GPT_OVF_IT    (1 << 1)
++#define GPT_MAT_IT    (1 << 0)
++
++static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
++{
++    if (timer->it_ena & it) {
++        if (!timer->status)
++            qemu_irq_raise(timer->irq);
++
++        timer->status |= it;
++        /* Or are the status bits set even when masked?
++         * i.e. is masking applied before or after the status register?  */
++    }
++
++    if (timer->wu_ena & it)
++        qemu_irq_pulse(timer->wkup);
++}
++
++static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
++{
++    if (!timer->inout && timer->out_val != level) {
++        timer->out_val = level;
++        qemu_set_irq(timer->out, level);
++    }
++}
++
++static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
++{
++    uint64_t distance;
++
++    if (timer->st && timer->rate) {
++        distance = qemu_get_clock(vm_clock) - timer->time;
++        distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
++
++        if (distance >= 0xffffffff - timer->val)
++            return 0xffffffff;
++        else
++            return timer->val + distance;
++    } else
++        return timer->val;
++}
++
++static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
++{
++    if (timer->st) {
++        timer->val = omap_gp_timer_read(timer);
++        timer->time = qemu_get_clock(vm_clock);
++    }
++}
++
++static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
++{
++    int64_t expires, matches;
++
++    if (timer->st && timer->rate) {
++        expires = muldiv64(0x100000000ll - timer->val,
++                        timer->ticks_per_sec, timer->rate);
++        qemu_mod_timer(timer->timer, timer->time + expires);
++
++        if (timer->ce && timer->match_val >= timer->val) {
++            matches = muldiv64(timer->match_val - timer->val,
++                            timer->ticks_per_sec, timer->rate);
++            qemu_mod_timer(timer->match, timer->time + matches);
++        } else
++            qemu_del_timer(timer->match);
++    } else {
++        qemu_del_timer(timer->timer);
++        qemu_del_timer(timer->match);
++        omap_gp_timer_out(timer, timer->scpwm);
++    }
++}
++
++static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
++{
++    if (timer->pt)
++        /* TODO in overflow-and-match mode if the first event to
++         * occurs is the match, don't toggle.  */
++        omap_gp_timer_out(timer, !timer->out_val);
++    else
++        /* TODO inverted pulse on timer->out_val == 1?  */
++        qemu_irq_pulse(timer->out);
++}
++
++static void omap_gp_timer_tick(void *opaque)
++{
++    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
++
++    if (!timer->ar) {
++        timer->st = 0;
++        timer->val = 0;
++    } else {
++        timer->val = timer->load_val;
++        timer->time = qemu_get_clock(vm_clock);
++    }
++
++    if (timer->trigger == gpt_trigger_overflow ||
++                    timer->trigger == gpt_trigger_both)
++        omap_gp_timer_trigger(timer);
++
++    omap_gp_timer_intr(timer, GPT_OVF_IT);
++    omap_gp_timer_update(timer);
++}
++
++static void omap_gp_timer_match(void *opaque)
++{
++    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
++
++    if (timer->trigger == gpt_trigger_both)
++        omap_gp_timer_trigger(timer);
++
++    omap_gp_timer_intr(timer, GPT_MAT_IT);
++}
++
++static void omap_gp_timer_input(void *opaque, int line, int on)
++{
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
++    int trigger;
++
++    switch (s->capture) {
++    default:
++    case gpt_capture_none:
++        trigger = 0;
++        break;
++    case gpt_capture_rising:
++        trigger = !s->in_val && on;
++        break;
++    case gpt_capture_falling:
++        trigger = s->in_val && !on;
++        break;
++    case gpt_capture_both:
++        trigger = (s->in_val == !on);
++        break;
++    }
++    s->in_val = on;
++
++    if (s->inout && trigger && s->capt_num < 2) {
++        s->capture_val[s->capt_num] = omap_gp_timer_read(s);
++
++        if (s->capt2 == s->capt_num ++)
++            omap_gp_timer_intr(s, GPT_TCAR_IT);
++    }
++}
++
++static void omap_gp_timer_clk_update(void *opaque, int line, int on)
++{
++    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
++
++    omap_gp_timer_sync(timer);
++    timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
++    omap_gp_timer_update(timer);
++}
++
++static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
++{
++    omap_clk_adduser(timer->clk,
++                    qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]);
++    timer->rate = omap_clk_getrate(timer->clk);
++}
++
++static void omap_gp_timer_reset(struct omap_gp_timer_s *s)
++{
++    s->config = 0x000;
++    s->status = 0;
++    s->it_ena = 0;
++    s->wu_ena = 0;
++    s->inout = 0;
++    s->capt2 = 0;
++    s->capt_num = 0;
++    s->pt = 0;
++    s->trigger = gpt_trigger_none;
++    s->capture = gpt_capture_none;
++    s->scpwm = 0;
++    s->ce = 0;
++    s->pre = 0;
++    s->ptv = 0;
++    s->ar = 0;
++    s->st = 0;
++    s->posted = 1;
++    s->val = 0x00000000;
++    s->load_val = 0x00000000;
++    s->capture_val[0] = 0x00000000;
++    s->capture_val[1] = 0x00000000;
++    s->match_val = 0x00000000;
++    omap_gp_timer_update(s);
++}
++
++static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* TIDR */
++        return 0x21;
++
++    case 0x10:        /* TIOCP_CFG */
++        return s->config;
++
++    case 0x14:        /* TISTAT */
++        /* ??? When's this bit reset? */
++        return 1;                                             /* RESETDONE */
++
++    case 0x18:        /* TISR */
++        return s->status;
++
++    case 0x1c:        /* TIER */
++        return s->it_ena;
++
++    case 0x20:        /* TWER */
++        return s->wu_ena;
++
++    case 0x24:        /* TCLR */
++        return (s->inout << 14) |
++                (s->capt2 << 13) |
++                (s->pt << 12) |
++                (s->trigger << 10) |
++                (s->capture << 8) |
++                (s->scpwm << 7) |
++                (s->ce << 6) |
++                (s->pre << 5) |
++                (s->ptv << 2) |
++                (s->ar << 1) |
++                (s->st << 0);
++
++    case 0x28:        /* TCRR */
++        return omap_gp_timer_read(s);
++
++    case 0x2c:        /* TLDR */
++        return s->load_val;
++
++    case 0x30:        /* TTGR */
++        return 0xffffffff;
++
++    case 0x34:        /* TWPS */
++        return 0x00000000;    /* No posted writes pending.  */
++
++    case 0x38:        /* TMAR */
++        return s->match_val;
++
++    case 0x3c:        /* TCAR1 */
++        return s->capture_val[0];
++
++    case 0x40:        /* TSICR */
++        return s->posted << 2;
++
++    case 0x44:        /* TCAR2 */
++        return s->capture_val[1];
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
++    uint32_t ret;
++
++    if (addr & 2)
++        return s->readh;
++    else {
++        ret = omap_gp_timer_readw(opaque, addr);
++        s->readh = ret >> 16;
++        return ret & 0xffff;
++    }
++}
++
++static CPUReadMemoryFunc *omap_gp_timer_readfn[] = {
++    omap_badwidth_read32,
++    omap_gp_timer_readh,
++    omap_gp_timer_readw,
++};
++
++static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* TIDR */
++    case 0x14:        /* TISTAT */
++    case 0x34:        /* TWPS */
++    case 0x3c:        /* TCAR1 */
++    case 0x44:        /* TCAR2 */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x10:        /* TIOCP_CFG */
++        s->config = value & 0x33d;
++        if (((value >> 3) & 3) == 3)                          /* IDLEMODE */
++            fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
++                            __FUNCTION__);
++        if (value & 2)                                                /* SOFTRESET */
++            omap_gp_timer_reset(s);
++        break;
++
++    case 0x18:        /* TISR */
++        if (value & GPT_TCAR_IT)
++            s->capt_num = 0;
++        if (s->status && !(s->status &= ~value))
++            qemu_irq_lower(s->irq);
++        break;
++
++    case 0x1c:        /* TIER */
++        s->it_ena = value & 7;
++        break;
++
++    case 0x20:        /* TWER */
++        s->wu_ena = value & 7;
++        break;
++
++    case 0x24:        /* TCLR */
++        omap_gp_timer_sync(s);
++        s->inout = (value >> 14) & 1;
++        s->capt2 = (value >> 13) & 1;
++        s->pt = (value >> 12) & 1;
++        s->trigger = (value >> 10) & 3;
++        if (s->capture == gpt_capture_none &&
++                        ((value >> 8) & 3) != gpt_capture_none)
++            s->capt_num = 0;
++        s->capture = (value >> 8) & 3;
++        s->scpwm = (value >> 7) & 1;
++        s->ce = (value >> 6) & 1;
++        s->pre = (value >> 5) & 1;
++        s->ptv = (value >> 2) & 7;
++        s->ar = (value >> 1) & 1;
++        s->st = (value >> 0) & 1;
++        if (s->inout && s->trigger != gpt_trigger_none)
++            fprintf(stderr, "%s: GP timer pin must be an output "
++                            "for this trigger mode\n", __FUNCTION__);
++        if (!s->inout && s->capture != gpt_capture_none)
++            fprintf(stderr, "%s: GP timer pin must be an input "
++                            "for this capture mode\n", __FUNCTION__);
++        if (s->trigger == gpt_trigger_none)
++            omap_gp_timer_out(s, s->scpwm);
++        /* TODO: make sure this doesn't overflow 32-bits */
++        s->ticks_per_sec = ticks_per_sec << (s->pre ? s->ptv + 1 : 0);
++        omap_gp_timer_update(s);
++        break;
++
++    case 0x28:        /* TCRR */
++        s->time = qemu_get_clock(vm_clock);
++        s->val = value;
++        omap_gp_timer_update(s);
++        break;
++
++    case 0x2c:        /* TLDR */
++        s->load_val = value;
++        break;
++
++    case 0x30:        /* TTGR */
++        s->time = qemu_get_clock(vm_clock);
++        s->val = s->load_val;
++        omap_gp_timer_update(s);
++        break;
++
++    case 0x38:        /* TMAR */
++        omap_gp_timer_sync(s);
++        s->match_val = value;
++        omap_gp_timer_update(s);
++        break;
++
++    case 0x40:        /* TSICR */
++        s->posted = (value >> 2) & 1;
++        if (value & 2)        /* How much exactly are we supposed to reset? */
++            omap_gp_timer_reset(s);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
++
++    if (addr & 2)
++        return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
++    else
++        s->writeh = (uint16_t) value;
++}
++
++static CPUWriteMemoryFunc *omap_gp_timer_writefn[] = {
++    omap_badwidth_write32,
++    omap_gp_timer_writeh,
++    omap_gp_timer_write,
++};
++
++struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
++                qemu_irq irq, omap_clk fclk, omap_clk iclk)
++{
++    int iomemtype;
++    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
++            qemu_mallocz(sizeof(struct omap_gp_timer_s));
++
++    s->ta = ta;
++    s->irq = irq;
++    s->clk = fclk;
++    s->timer = qemu_new_timer(vm_clock, omap_gp_timer_tick, s);
++    s->match = qemu_new_timer(vm_clock, omap_gp_timer_match, s);
++    s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0];
++    omap_gp_timer_reset(s);
++    omap_gp_timer_clk_setup(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_gp_timer_readfn,
++                    omap_gp_timer_writefn, s);
++    s->base = omap_l4_attach(ta, 0, iomemtype);
++
++    return s;
++}
++
++/* 32-kHz Sync Timer of the OMAP2 */
++static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) {
++    return muldiv64(qemu_get_clock(vm_clock), 0x8000, ticks_per_sec);
++}
++
++static void omap_synctimer_reset(struct omap_synctimer_s *s)
++{
++    s->val = omap_synctimer_read(s);
++}
++
++static uint32_t omap_synctimer_readw(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* 32KSYNCNT_REV */
++        return 0x21;
++
++    case 0x10:        /* CR */
++        return omap_synctimer_read(s) - s->val;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static uint32_t omap_synctimer_readh(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
++    uint32_t ret;
++
++    if (addr & 2)
++        return s->readh;
++    else {
++        ret = omap_synctimer_readw(opaque, addr);
++        s->readh = ret >> 16;
++        return ret & 0xffff;
++    }
++}
++
++static CPUReadMemoryFunc *omap_synctimer_readfn[] = {
++    omap_badwidth_read32,
++    omap_synctimer_readh,
++    omap_synctimer_readw,
++};
++
++static void omap_synctimer_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    OMAP_BAD_REG(addr);
++}
++
++static CPUWriteMemoryFunc *omap_synctimer_writefn[] = {
++    omap_badwidth_write32,
++    omap_synctimer_write,
++    omap_synctimer_write,
++};
++
++void omap_synctimer_init(struct omap_target_agent_s *ta,
++                struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk)
++{
++    struct omap_synctimer_s *s = &mpu->synctimer;
++
++    omap_synctimer_reset(s);
++    s->base = omap_l4_attach(ta, 0, cpu_register_io_memory(0,
++                            omap_synctimer_readfn, omap_synctimer_writefn, s));
++}
++
++/* General-Purpose Interface of OMAP2 */
++struct omap2_gpio_s {
++    target_phys_addr_t base;
++    qemu_irq irq[2];
++    qemu_irq wkup;
++    qemu_irq *in;
++    qemu_irq handler[32];
++
++    uint8_t config[2];
++    uint32_t inputs;
++    uint32_t outputs;
++    uint32_t dir;
++    uint32_t level[2];
++    uint32_t edge[2];
++    uint32_t mask[2];
++    uint32_t wumask;
++    uint32_t ints[2];
++    uint32_t debounce;
++    uint8_t delay;
++};
++
++static inline void omap_gpio_module_int_update(struct omap2_gpio_s *s,
++                int line)
++{
++    qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]);
++}
++
++static void omap_gpio_module_wake(struct omap2_gpio_s *s, int line)
++{
++    if (!(s->config[0] & (1 << 2)))                   /* ENAWAKEUP */
++        return;
++    if (!(s->config[0] & (3 << 3)))                   /* Force Idle */
++        return;
++    if (!(s->wumask & (1 << line)))
++        return;
++
++    qemu_irq_raise(s->wkup);
++}
++
++static inline void omap_gpio_module_out_update(struct omap2_gpio_s *s,
++                uint32_t diff)
++{
++    int ln;
++
++    s->outputs ^= diff;
++    diff &= ~s->dir;
++    while ((ln = ffs(diff))) {
++        ln --;
++        qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1);
++        diff &= ~(1 << ln);
++    }
++}
++
++static void omap_gpio_module_level_update(struct omap2_gpio_s *s, int line)
++{
++    s->ints[line] |= s->dir &
++            ((s->inputs & s->level[1]) | (~s->inputs & s->level[0]));
++    omap_gpio_module_int_update(s, line);
++}
++
++static inline void omap_gpio_module_int(struct omap2_gpio_s *s, int line)
++{
++    s->ints[0] |= 1 << line;
++    omap_gpio_module_int_update(s, 0);
++    s->ints[1] |= 1 << line;
++    omap_gpio_module_int_update(s, 1);
++    omap_gpio_module_wake(s, line);
++}
++
++static void omap_gpio_module_set(void *opaque, int line, int level)
++{
++    struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
++
++    if (level) {
++        if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1]))
++            omap_gpio_module_int(s, line);
++        s->inputs |= 1 << line;
++    } else {
++        if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0]))
++            omap_gpio_module_int(s, line);
++        s->inputs &= ~(1 << line);
++    }
++}
++
++static void omap_gpio_module_reset(struct omap2_gpio_s *s)
++{
++    s->config[0] = 0;
++    s->config[1] = 2;
++    s->ints[0] = 0;
++    s->ints[1] = 0;
++    s->mask[0] = 0;
++    s->mask[1] = 0;
++    s->wumask = 0;
++    s->dir = ~0;
++    s->level[0] = 0;
++    s->level[1] = 0;
++    s->edge[0] = 0;
++    s->edge[1] = 0;
++    s->debounce = 0;
++    s->delay = 0;
++}
++
++static uint32_t omap_gpio_module_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* GPIO_REVISION */
++        return 0x18;
++
++    case 0x10:        /* GPIO_SYSCONFIG */
++        return s->config[0];
++
++    case 0x14:        /* GPIO_SYSSTATUS */
++        return 0x01;
++
++    case 0x18:        /* GPIO_IRQSTATUS1 */
++        return s->ints[0];
++
++    case 0x1c:        /* GPIO_IRQENABLE1 */
++    case 0x60:        /* GPIO_CLEARIRQENABLE1 */
++    case 0x64:        /* GPIO_SETIRQENABLE1 */
++        return s->mask[0];
++
++    case 0x20:        /* GPIO_WAKEUPENABLE */
++    case 0x80:        /* GPIO_CLEARWKUENA */
++    case 0x84:        /* GPIO_SETWKUENA */
++        return s->wumask;
++
++    case 0x28:        /* GPIO_IRQSTATUS2 */
++        return s->ints[1];
++
++    case 0x2c:        /* GPIO_IRQENABLE2 */
++    case 0x70:        /* GPIO_CLEARIRQENABLE2 */
++    case 0x74:        /* GPIO_SETIREQNEABLE2 */
++        return s->mask[1];
++
++    case 0x30:        /* GPIO_CTRL */
++        return s->config[1];
++
++    case 0x34:        /* GPIO_OE */
++        return s->dir;
++
++    case 0x38:        /* GPIO_DATAIN */
++        return s->inputs;
++
++    case 0x3c:        /* GPIO_DATAOUT */
++    case 0x90:        /* GPIO_CLEARDATAOUT */
++    case 0x94:        /* GPIO_SETDATAOUT */
++        return s->outputs;
++
++    case 0x40:        /* GPIO_LEVELDETECT0 */
++        return s->level[0];
++
++    case 0x44:        /* GPIO_LEVELDETECT1 */
++        return s->level[1];
++
++    case 0x48:        /* GPIO_RISINGDETECT */
++        return s->edge[0];
++
++    case 0x4c:        /* GPIO_FALLINGDETECT */
++        return s->edge[1];
++
++    case 0x50:        /* GPIO_DEBOUNCENABLE */
++        return s->debounce;
++
++    case 0x54:        /* GPIO_DEBOUNCINGTIME */
++        return s->delay;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_gpio_module_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
++    int offset = addr - s->base;
++    uint32_t diff;
++    int ln;
++
++    switch (offset) {
++    case 0x00:        /* GPIO_REVISION */
++    case 0x14:        /* GPIO_SYSSTATUS */
++    case 0x38:        /* GPIO_DATAIN */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x10:        /* GPIO_SYSCONFIG */
++        if (((value >> 3) & 3) == 3)
++            fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__);
++        if (value & 2)
++            omap_gpio_module_reset(s);
++        s->config[0] = value & 0x1d;
++        break;
++
++    case 0x18:        /* GPIO_IRQSTATUS1 */
++        if (s->ints[0] & value) {
++            s->ints[0] &= ~value;
++            omap_gpio_module_level_update(s, 0);
++        }
++        break;
++
++    case 0x1c:        /* GPIO_IRQENABLE1 */
++        s->mask[0] = value;
++        omap_gpio_module_int_update(s, 0);
++        break;
++
++    case 0x20:        /* GPIO_WAKEUPENABLE */
++        s->wumask = value;
++        break;
++
++    case 0x28:        /* GPIO_IRQSTATUS2 */
++        if (s->ints[1] & value) {
++            s->ints[1] &= ~value;
++            omap_gpio_module_level_update(s, 1);
++        }
++        break;
++
++    case 0x2c:        /* GPIO_IRQENABLE2 */
++        s->mask[1] = value;
++        omap_gpio_module_int_update(s, 1);
++        break;
++
++    case 0x30:        /* GPIO_CTRL */
++        s->config[1] = value & 7;
++        break;
++
++    case 0x34:        /* GPIO_OE */
++        diff = s->outputs & (s->dir ^ value);
++        s->dir = value;
++
++        value = s->outputs & ~s->dir;
++        while ((ln = ffs(diff))) {
++            diff &= ~(1 <<-- ln);
++            qemu_set_irq(s->handler[ln], (value >> ln) & 1);
++        }
++
++        omap_gpio_module_level_update(s, 0);
++        omap_gpio_module_level_update(s, 1);
++        break;
++
++    case 0x3c:        /* GPIO_DATAOUT */
++        omap_gpio_module_out_update(s, s->outputs ^ value);
++        break;
++
++    case 0x40:        /* GPIO_LEVELDETECT0 */
++        s->level[0] = value;
++        omap_gpio_module_level_update(s, 0);
++        omap_gpio_module_level_update(s, 1);
++        break;
++
++    case 0x44:        /* GPIO_LEVELDETECT1 */
++        s->level[1] = value;
++        omap_gpio_module_level_update(s, 0);
++        omap_gpio_module_level_update(s, 1);
++        break;
++
++    case 0x48:        /* GPIO_RISINGDETECT */
++        s->edge[0] = value;
++        break;
++
++    case 0x4c:        /* GPIO_FALLINGDETECT */
++        s->edge[1] = value;
++        break;
++
++    case 0x50:        /* GPIO_DEBOUNCENABLE */
++        s->debounce = value;
++        break;
++
++    case 0x54:        /* GPIO_DEBOUNCINGTIME */
++        s->delay = value;
++        break;
++
++    case 0x60:        /* GPIO_CLEARIRQENABLE1 */
++        s->mask[0] &= ~value;
++        omap_gpio_module_int_update(s, 0);
++        break;
++
++    case 0x64:        /* GPIO_SETIRQENABLE1 */
++        s->mask[0] |= value;
++        omap_gpio_module_int_update(s, 0);
++        break;
++
++    case 0x70:        /* GPIO_CLEARIRQENABLE2 */
++        s->mask[1] &= ~value;
++        omap_gpio_module_int_update(s, 1);
++        break;
++
++    case 0x74:        /* GPIO_SETIREQNEABLE2 */
++        s->mask[1] |= value;
++        omap_gpio_module_int_update(s, 1);
++        break;
++
++    case 0x80:        /* GPIO_CLEARWKUENA */
++        s->wumask &= ~value;
++        break;
++
++    case 0x84:        /* GPIO_SETWKUENA */
++        s->wumask |= value;
++        break;
++
++    case 0x90:        /* GPIO_CLEARDATAOUT */
++        omap_gpio_module_out_update(s, s->outputs & value);
++        break;
++
++    case 0x94:        /* GPIO_SETDATAOUT */
++        omap_gpio_module_out_update(s, ~s->outputs & value);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static uint32_t omap_gpio_module_readp(void *opaque, target_phys_addr_t addr)
++{
++    return omap_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3);
++}
++
++static void omap_gpio_module_writep(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
++    int offset = addr - s->base;
++    uint32_t cur = 0;
++    uint32_t mask = 0xffff;
++
++    switch (offset & ~3) {
++    case 0x00:        /* GPIO_REVISION */
++    case 0x14:        /* GPIO_SYSSTATUS */
++    case 0x38:        /* GPIO_DATAIN */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x10:        /* GPIO_SYSCONFIG */
++    case 0x1c:        /* GPIO_IRQENABLE1 */
++    case 0x20:        /* GPIO_WAKEUPENABLE */
++    case 0x2c:        /* GPIO_IRQENABLE2 */
++    case 0x30:        /* GPIO_CTRL */
++    case 0x34:        /* GPIO_OE */
++    case 0x3c:        /* GPIO_DATAOUT */
++    case 0x40:        /* GPIO_LEVELDETECT0 */
++    case 0x44:        /* GPIO_LEVELDETECT1 */
++    case 0x48:        /* GPIO_RISINGDETECT */
++    case 0x4c:        /* GPIO_FALLINGDETECT */
++    case 0x50:        /* GPIO_DEBOUNCENABLE */
++    case 0x54:        /* GPIO_DEBOUNCINGTIME */
++        cur = omap_gpio_module_read(opaque, addr & ~3) &
++                ~(mask << ((addr & 3) << 3));
++
++        /* Fall through.  */
++    case 0x18:        /* GPIO_IRQSTATUS1 */
++    case 0x28:        /* GPIO_IRQSTATUS2 */
++    case 0x60:        /* GPIO_CLEARIRQENABLE1 */
++    case 0x64:        /* GPIO_SETIRQENABLE1 */
++    case 0x70:        /* GPIO_CLEARIRQENABLE2 */
++    case 0x74:        /* GPIO_SETIREQNEABLE2 */
++    case 0x80:        /* GPIO_CLEARWKUENA */
++    case 0x84:        /* GPIO_SETWKUENA */
++    case 0x90:        /* GPIO_CLEARDATAOUT */
++    case 0x94:        /* GPIO_SETDATAOUT */
++        value <<= (addr & 3) << 3;
++        omap_gpio_module_write(opaque, addr, cur | value);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_gpio_module_readfn[] = {
++    omap_gpio_module_readp,
++    omap_gpio_module_readp,
++    omap_gpio_module_read,
++};
++
++static CPUWriteMemoryFunc *omap_gpio_module_writefn[] = {
++    omap_gpio_module_writep,
++    omap_gpio_module_writep,
++    omap_gpio_module_write,
++};
++
++static void omap_gpio_module_init(struct omap2_gpio_s *s,
++                struct omap_target_agent_s *ta, int region,
++                qemu_irq mpu, qemu_irq dsp, qemu_irq wkup,
++                omap_clk fclk, omap_clk iclk)
++{
++    int iomemtype;
++
++    s->irq[0] = mpu;
++    s->irq[1] = dsp;
++    s->wkup = wkup;
++    s->in = qemu_allocate_irqs(omap_gpio_module_set, s, 32);
++
++    iomemtype = cpu_register_io_memory(0, omap_gpio_module_readfn,
++                    omap_gpio_module_writefn, s);
++    s->base = omap_l4_attach(ta, region, iomemtype);
++}
++
++struct omap_gpif_s {
++    struct omap2_gpio_s module[5];
++    int modules;
++
++    target_phys_addr_t topbase;
++    int autoidle;
++    int gpo;
++};
++
++static void omap_gpif_reset(struct omap_gpif_s *s)
++{
++    int i;
++
++    for (i = 0; i < s->modules; i ++)
++        omap_gpio_module_reset(s->module + i);
++
++    s->autoidle = 0;
++    s->gpo = 0;
++}
++
++static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
++    int offset = addr - s->topbase;
++
++    switch (offset) {
++    case 0x00:        /* IPGENERICOCPSPL_REVISION */
++        return 0x18;
++
++    case 0x10:        /* IPGENERICOCPSPL_SYSCONFIG */
++        return s->autoidle;
++
++    case 0x14:        /* IPGENERICOCPSPL_SYSSTATUS */
++        return 0x01;
++
++    case 0x18:        /* IPGENERICOCPSPL_IRQSTATUS */
++        return 0x00;
++
++    case 0x40:        /* IPGENERICOCPSPL_GPO */
++        return s->gpo;
++
++    case 0x50:        /* IPGENERICOCPSPL_GPI */
++        return 0x00;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
++    int offset = addr - s->topbase;
++
++    switch (offset) {
++    case 0x00:        /* IPGENERICOCPSPL_REVISION */
++    case 0x14:        /* IPGENERICOCPSPL_SYSSTATUS */
++    case 0x18:        /* IPGENERICOCPSPL_IRQSTATUS */
++    case 0x50:        /* IPGENERICOCPSPL_GPI */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x10:        /* IPGENERICOCPSPL_SYSCONFIG */
++        if (value & (1 << 1))                                 /* SOFTRESET */
++            omap_gpif_reset(s);
++        s->autoidle = value & 1;
++        break;
++
++    case 0x40:        /* IPGENERICOCPSPL_GPO */
++        s->gpo = value & 1;
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_gpif_top_readfn[] = {
++    omap_gpif_top_read,
++    omap_gpif_top_read,
++    omap_gpif_top_read,
++};
++
++static CPUWriteMemoryFunc *omap_gpif_top_writefn[] = {
++    omap_gpif_top_write,
++    omap_gpif_top_write,
++    omap_gpif_top_write,
++};
++
++struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
++                qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules)
++{
++    int iomemtype, i;
++    struct omap_gpif_s *s = (struct omap_gpif_s *)
++            qemu_mallocz(sizeof(struct omap_gpif_s));
++    int region[4] = { 0, 2, 4, 5 };
++
++    s->modules = modules;
++    for (i = 0; i < modules; i ++)
++        omap_gpio_module_init(s->module + i, ta, region[i],
++                        irq[i], 0, 0, fclk[i], iclk);
++
++    omap_gpif_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_gpif_top_readfn,
++                    omap_gpif_top_writefn, s);
++    s->topbase = omap_l4_attach(ta, 1, iomemtype);
++
++    return s;
++}
++
++qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start)
++{
++    if (start >= s->modules * 32 || start < 0)
++        cpu_abort(cpu_single_env, "%s: No GPIO line %i\n",
++                        __FUNCTION__, start);
++    return s->module[start >> 5].in + (start & 31);
++}
++
++void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler)
++{
++    if (line >= s->modules * 32 || line < 0)
++        cpu_abort(cpu_single_env, "%s: No GPIO line %i\n", __FUNCTION__, line);
++    s->module[line >> 5].handler[line & 31] = handler;
++}
++
++/* Multichannel SPI */
++struct omap_mcspi_s {
++    target_phys_addr_t base;
++    qemu_irq irq;
++    int chnum;
++
++    uint32_t sysconfig;
++    uint32_t systest;
++    uint32_t irqst;
++    uint32_t irqen;
++    uint32_t wken;
++    uint32_t control;
++
++    struct omap_mcspi_ch_s {
++        qemu_irq txdrq;
++        qemu_irq rxdrq;
++        uint32_t (*txrx)(void *opaque, uint32_t);
++        void *opaque;
++
++        uint32_t tx;
++        uint32_t rx;
++
++        uint32_t config;
++        uint32_t status;
++        uint32_t control;
++    } ch[4];
++};
++
++static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
++{
++    qemu_set_irq(s->irq, s->irqst & s->irqen);
++}
++
++static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
++{
++    qemu_set_irq(ch->txdrq,
++                    (ch->control & 1) &&              /* EN */
++                    (ch->config & (1 << 14)) &&               /* DMAW */
++                    (ch->status & (1 << 1)) &&                /* TXS */
++                    ((ch->config >> 12) & 3) != 1);   /* TRM */
++    qemu_set_irq(ch->rxdrq,
++                    (ch->control & 1) &&              /* EN */
++                    (ch->config & (1 << 15)) &&               /* DMAW */
++                    (ch->status & (1 << 0)) &&                /* RXS */
++                    ((ch->config >> 12) & 3) != 2);   /* TRM */
++}
++
++static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
++{
++    struct omap_mcspi_ch_s *ch = s->ch + chnum;
++
++    if (!(ch->control & 1))                           /* EN */
++        return;
++    if ((ch->status & (1 << 0)) &&                    /* RXS */
++                    ((ch->config >> 12) & 3) != 2 &&  /* TRM */
++                    !(ch->config & (1 << 19)))                /* TURBO */
++        goto intr_update;
++    if ((ch->status & (1 << 1)) &&                    /* TXS */
++                    ((ch->config >> 12) & 3) != 1)    /* TRM */
++        goto intr_update;
++
++    if (!(s->control & 1) ||                          /* SINGLE */
++                    (ch->config & (1 << 20))) {               /* FORCE */
++        if (ch->txrx)
++            ch->rx = ch->txrx(ch->opaque, ch->tx);
++    }
++
++    ch->tx = 0;
++    ch->status |= 1 << 2;                             /* EOT */
++    ch->status |= 1 << 1;                             /* TXS */
++    if (((ch->config >> 12) & 3) != 2)                        /* TRM */
++        ch->status |= 1 << 0;                         /* RXS */
++
++intr_update:
++    if ((ch->status & (1 << 0)) &&                    /* RXS */
++                    ((ch->config >> 12) & 3) != 2 &&  /* TRM */
++                    !(ch->config & (1 << 19)))                /* TURBO */
++        s->irqst |= 1 << (2 + 4 * chnum);             /* RX_FULL */
++    if ((ch->status & (1 << 1)) &&                    /* TXS */
++                    ((ch->config >> 12) & 3) != 1)    /* TRM */
++        s->irqst |= 1 << (0 + 4 * chnum);             /* TX_EMPTY */
++    omap_mcspi_interrupt_update(s);
++    omap_mcspi_dmarequest_update(ch);
++}
++
++static void omap_mcspi_reset(struct omap_mcspi_s *s)
++{
++    int ch;
++
++    s->sysconfig = 0;
++    s->systest = 0;
++    s->irqst = 0;
++    s->irqen = 0;
++    s->wken = 0;
++    s->control = 4;
++
++    for (ch = 0; ch < 4; ch ++) {
++        s->ch[ch].config = 0x060000;
++        s->ch[ch].status = 2;                         /* TXS */
++        s->ch[ch].control = 0;
++
++        omap_mcspi_dmarequest_update(s->ch + ch);
++    }
++
++    omap_mcspi_interrupt_update(s);
++}
++
++static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
++    int offset = addr - s->base;
++    int ch = 0;
++    uint32_t ret;
++
++    switch (offset) {
++    case 0x00:        /* MCSPI_REVISION */
++        return 0x91;
++
++    case 0x10:        /* MCSPI_SYSCONFIG */
++        return s->sysconfig;
++
++    case 0x14:        /* MCSPI_SYSSTATUS */
++        return 1;                                     /* RESETDONE */
++
++    case 0x18:        /* MCSPI_IRQSTATUS */
++        return s->irqst;
++
++    case 0x1c:        /* MCSPI_IRQENABLE */
++        return s->irqen;
++
++    case 0x20:        /* MCSPI_WAKEUPENABLE */
++        return s->wken;
++
++    case 0x24:        /* MCSPI_SYST */
++        return s->systest;
++
++    case 0x28:        /* MCSPI_MODULCTRL */
++        return s->control;
++
++    case 0x68: ch ++;
++    case 0x54: ch ++;
++    case 0x40: ch ++;
++    case 0x2c:        /* MCSPI_CHCONF */
++        return s->ch[ch].config;
++
++    case 0x6c: ch ++;
++    case 0x58: ch ++;
++    case 0x44: ch ++;
++    case 0x30:        /* MCSPI_CHSTAT */
++        return s->ch[ch].status;
++
++    case 0x70: ch ++;
++    case 0x5c: ch ++;
++    case 0x48: ch ++;
++    case 0x34:        /* MCSPI_CHCTRL */
++        return s->ch[ch].control;
++
++    case 0x74: ch ++;
++    case 0x60: ch ++;
++    case 0x4c: ch ++;
++    case 0x38:        /* MCSPI_TX */
++        return s->ch[ch].tx;
++
++    case 0x78: ch ++;
++    case 0x64: ch ++;
++    case 0x50: ch ++;
++    case 0x3c:        /* MCSPI_RX */
++        s->ch[ch].status &= ~(1 << 0);                        /* RXS */
++        ret = s->ch[ch].rx;
++        omap_mcspi_transfer_run(s, ch);
++        return ret;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_mcspi_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
++    int offset = addr - s->base;
++    int ch = 0;
++
++    switch (offset) {
++    case 0x00:        /* MCSPI_REVISION */
++    case 0x14:        /* MCSPI_SYSSTATUS */
++    case 0x30:        /* MCSPI_CHSTAT0 */
++    case 0x3c:        /* MCSPI_RX0 */
++    case 0x44:        /* MCSPI_CHSTAT1 */
++    case 0x50:        /* MCSPI_RX1 */
++    case 0x58:        /* MCSPI_CHSTAT2 */
++    case 0x64:        /* MCSPI_RX2 */
++    case 0x6c:        /* MCSPI_CHSTAT3 */
++    case 0x78:        /* MCSPI_RX3 */
++        OMAP_RO_REG(addr);
++        return;
++
++    case 0x10:        /* MCSPI_SYSCONFIG */
++        if (value & (1 << 1))                         /* SOFTRESET */
++            omap_mcspi_reset(s);
++        s->sysconfig = value & 0x31d;
++        break;
++
++    case 0x18:        /* MCSPI_IRQSTATUS */
++        if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
++            s->irqst &= ~value;
++            omap_mcspi_interrupt_update(s);
++        }
++        break;
++
++    case 0x1c:        /* MCSPI_IRQENABLE */
++        s->irqen = value & 0x1777f;
++        omap_mcspi_interrupt_update(s);
++        break;
++
++    case 0x20:        /* MCSPI_WAKEUPENABLE */
++        s->wken = value & 1;
++        break;
++
++    case 0x24:        /* MCSPI_SYST */
++        if (s->control & (1 << 3))                    /* SYSTEM_TEST */
++            if (value & (1 << 11)) {                  /* SSB */
++                s->irqst |= 0x1777f;
++                omap_mcspi_interrupt_update(s);
++            }
++        s->systest = value & 0xfff;
++        break;
++
++    case 0x28:        /* MCSPI_MODULCTRL */
++        if (value & (1 << 3))                         /* SYSTEM_TEST */
++            if (s->systest & (1 << 11)) {             /* SSB */
++                s->irqst |= 0x1777f;
++                omap_mcspi_interrupt_update(s);
++            }
++        s->control = value & 0xf;
++        break;
++
++    case 0x68: ch ++;
++    case 0x54: ch ++;
++    case 0x40: ch ++;
++    case 0x2c:        /* MCSPI_CHCONF */
++        if ((value ^ s->ch[ch].config) & (3 << 14))   /* DMAR | DMAW */
++            omap_mcspi_dmarequest_update(s->ch + ch);
++        if (((value >> 12) & 3) == 3)                 /* TRM */
++            fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__);
++        if (((value >> 7) & 0x1f) < 3)                        /* WL */
++            fprintf(stderr, "%s: invalid WL value (%i)\n",
++                            __FUNCTION__, (value >> 7) & 0x1f);
++        s->ch[ch].config = value & 0x7fffff;
++        break;
++
++    case 0x70: ch ++;
++    case 0x5c: ch ++;
++    case 0x48: ch ++;
++    case 0x34:        /* MCSPI_CHCTRL */
++        if (value & ~s->ch[ch].control & 1) {         /* EN */
++            s->ch[ch].control |= 1;
++            omap_mcspi_transfer_run(s, ch);
++        } else
++            s->ch[ch].control = value & 1;
++        break;
++
++    case 0x74: ch ++;
++    case 0x60: ch ++;
++    case 0x4c: ch ++;
++    case 0x38:        /* MCSPI_TX */
++        s->ch[ch].tx = value;
++        s->ch[ch].status &= ~(1 << 1);                        /* TXS */
++        omap_mcspi_transfer_run(s, ch);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_mcspi_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_mcspi_read,
++};
++
++static CPUWriteMemoryFunc *omap_mcspi_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_mcspi_write,
++};
++
++struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
++                qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
++{
++    int iomemtype;
++    struct omap_mcspi_s *s = (struct omap_mcspi_s *)
++            qemu_mallocz(sizeof(struct omap_mcspi_s));
++    struct omap_mcspi_ch_s *ch = s->ch;
++
++    s->irq = irq;
++    s->chnum = chnum;
++    while (chnum --) {
++        ch->txdrq = *drq ++;
++        ch->rxdrq = *drq ++;
++        ch ++;
++    }
++    omap_mcspi_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_mcspi_readfn,
++                    omap_mcspi_writefn, s);
++    s->base = omap_l4_attach(ta, 0, iomemtype);
++
++    return s;
++}
++
++void omap_mcspi_attach(struct omap_mcspi_s *s,
++                uint32_t (*txrx)(void *opaque, uint32_t), void *opaque,
++                int chipselect)
++{
++    if (chipselect < 0 || chipselect >= s->chnum)
++        cpu_abort(cpu_single_env, "%s: Bad chipselect %i\n",
++                        __FUNCTION__, chipselect);
++
++    s->ch[chipselect].txrx = txrx;
++    s->ch[chipselect].opaque = opaque;
++}
++
++/* L4 Interconnect */
++struct omap_target_agent_s {
++    struct omap_l4_s *bus;
++    int regions;
++    struct omap_l4_region_s *start;
++    target_phys_addr_t base;
++    uint32_t component;
++    uint32_t control;
++    uint32_t status;
++};
++
++struct omap_l4_s {
++    target_phys_addr_t base;
++    int ta_num;
++    struct omap_target_agent_s ta[0];
++};
++
++struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num)
++{
++    struct omap_l4_s *bus = qemu_mallocz(
++                    sizeof(*bus) + ta_num * sizeof(*bus->ta));
++
++    bus->ta_num = ta_num;
++    bus->base = base;
++
++    return bus;
++}
++
++static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque;
++    target_phys_addr_t reg = addr - s->base;
++
++    switch (reg) {
++    case 0x00:        /* COMPONENT */
++        return s->component;
++
++    case 0x20:        /* AGENT_CONTROL */
++        return s->control;
++
++    case 0x28:        /* AGENT_STATUS */
++        return s->status;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_l4ta_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque;
++    target_phys_addr_t reg = addr - s->base;
++
++    switch (reg) {
++    case 0x00:        /* COMPONENT */
++    case 0x28:        /* AGENT_STATUS */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x20:        /* AGENT_CONTROL */
++        s->control = value & 0x01000700;
++        if (value & 1)                                        /* OCP_RESET */
++            s->status &= ~1;                          /* REQ_TIMEOUT */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_l4ta_readfn[] = {
++    omap_badwidth_read16,
++    omap_l4ta_read,
++    omap_badwidth_read16,
++};
++
++static CPUWriteMemoryFunc *omap_l4ta_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_l4ta_write,
++};
++
++#define L4TA(n)               (n)
++#define L4TAO(n)      ((n) + 39)
++
++static struct omap_l4_region_s {
++    target_phys_addr_t offset;
++    size_t size;
++    int access;
++} omap_l4_region[125] = {
++    [  1] = { 0x40800,  0x800, 32          }, /* Initiator agent */
++    [  2] = { 0x41000, 0x1000, 32          }, /* Link agent */
++    [  0] = { 0x40000,  0x800, 32          }, /* Address and protection */
++    [  3] = { 0x00000, 0x1000, 32 | 16 | 8 }, /* System Control and Pinout */
++    [  4] = { 0x01000, 0x1000, 32 | 16 | 8 }, /* L4TAO1 */
++    [  5] = { 0x04000, 0x1000, 32 | 16     }, /* 32K Timer */
++    [  6] = { 0x05000, 0x1000, 32 | 16 | 8 }, /* L4TAO2 */
++    [  7] = { 0x08000,  0x800, 32          }, /* PRCM Region A */
++    [  8] = { 0x08800,  0x800, 32          }, /* PRCM Region B */
++    [  9] = { 0x09000, 0x1000, 32 | 16 | 8 }, /* L4TAO */
++    [ 10] = { 0x12000, 0x1000, 32 | 16 | 8 }, /* Test (BCM) */
++    [ 11] = { 0x13000, 0x1000, 32 | 16 | 8 }, /* L4TA1 */
++    [ 12] = { 0x14000, 0x1000, 32          }, /* Test/emulation (TAP) */
++    [ 13] = { 0x15000, 0x1000, 32 | 16 | 8 }, /* L4TA2 */
++    [ 14] = { 0x18000, 0x1000, 32 | 16 | 8 }, /* GPIO1 */
++    [ 16] = { 0x1a000, 0x1000, 32 | 16 | 8 }, /* GPIO2 */
++    [ 18] = { 0x1c000, 0x1000, 32 | 16 | 8 }, /* GPIO3 */
++    [ 19] = { 0x1e000, 0x1000, 32 | 16 | 8 }, /* GPIO4 */
++    [ 15] = { 0x19000, 0x1000, 32 | 16 | 8 }, /* Quad GPIO TOP */
++    [ 17] = { 0x1b000, 0x1000, 32 | 16 | 8 }, /* L4TA3 */
++    [ 20] = { 0x20000, 0x1000, 32 | 16 | 8 }, /* WD Timer 1 (Secure) */
++    [ 22] = { 0x22000, 0x1000, 32 | 16 | 8 }, /* WD Timer 2 (OMAP) */
++    [ 21] = { 0x21000, 0x1000, 32 | 16 | 8 }, /* Dual WD timer TOP */
++    [ 23] = { 0x23000, 0x1000, 32 | 16 | 8 }, /* L4TA4 */
++    [ 24] = { 0x28000, 0x1000, 32 | 16 | 8 }, /* GP Timer 1 */
++    [ 25] = { 0x29000, 0x1000, 32 | 16 | 8 }, /* L4TA7 */
++    [ 26] = { 0x48000, 0x2000, 32 | 16 | 8 }, /* Emulation (ARM11ETB) */
++    [ 27] = { 0x4a000, 0x1000, 32 | 16 | 8 }, /* L4TA9 */
++    [ 28] = { 0x50000,  0x400, 32 | 16 | 8 }, /* Display top */
++    [ 29] = { 0x50400,  0x400, 32 | 16 | 8 }, /* Display control */
++    [ 30] = { 0x50800,  0x400, 32 | 16 | 8 }, /* Display RFBI */
++    [ 31] = { 0x50c00,  0x400, 32 | 16 | 8 }, /* Display encoder */
++    [ 32] = { 0x51000, 0x1000, 32 | 16 | 8 }, /* L4TA10 */
++    [ 33] = { 0x52000,  0x400, 32 | 16 | 8 }, /* Camera top */
++    [ 34] = { 0x52400,  0x400, 32 | 16 | 8 }, /* Camera core */
++    [ 35] = { 0x52800,  0x400, 32 | 16 | 8 }, /* Camera DMA */
++    [ 36] = { 0x52c00,  0x400, 32 | 16 | 8 }, /* Camera MMU */
++    [ 37] = { 0x53000, 0x1000, 32 | 16 | 8 }, /* L4TA11 */
++    [ 38] = { 0x56000, 0x1000, 32 | 16 | 8 }, /* sDMA */
++    [ 39] = { 0x57000, 0x1000, 32 | 16 | 8 }, /* L4TA12 */
++    [ 40] = { 0x58000, 0x1000, 32 | 16 | 8 }, /* SSI top */
++    [ 41] = { 0x59000, 0x1000, 32 | 16 | 8 }, /* SSI GDD */
++    [ 42] = { 0x5a000, 0x1000, 32 | 16 | 8 }, /* SSI Port1 */
++    [ 43] = { 0x5b000, 0x1000, 32 | 16 | 8 }, /* SSI Port2 */
++    [ 44] = { 0x5c000, 0x1000, 32 | 16 | 8 }, /* L4TA13 */
++    [ 45] = { 0x5e000, 0x1000, 32 | 16 | 8 }, /* USB OTG */
++    [ 46] = { 0x5f000, 0x1000, 32 | 16 | 8 }, /* L4TAO4 */
++    [ 47] = { 0x60000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER1SDRC) */
++    [ 48] = { 0x61000, 0x1000, 32 | 16 | 8 }, /* L4TA14 */
++    [ 49] = { 0x62000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER2GPMC) */
++    [ 50] = { 0x63000, 0x1000, 32 | 16 | 8 }, /* L4TA15 */
++    [ 51] = { 0x64000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER3OCM) */
++    [ 52] = { 0x65000, 0x1000, 32 | 16 | 8 }, /* L4TA16 */
++    [ 53] = { 0x66000,  0x300, 32 | 16 | 8 }, /* Emulation (WIN_TRACER4L4) */
++    [ 54] = { 0x67000, 0x1000, 32 | 16 | 8 }, /* L4TA17 */
++    [ 55] = { 0x68000, 0x1000, 32 | 16 | 8 }, /* Emulation (XTI) */
++    [ 56] = { 0x69000, 0x1000, 32 | 16 | 8 }, /* L4TA18 */
++    [ 57] = { 0x6a000, 0x1000,      16 | 8 }, /* UART1 */
++    [ 58] = { 0x6b000, 0x1000, 32 | 16 | 8 }, /* L4TA19 */
++    [ 59] = { 0x6c000, 0x1000,      16 | 8 }, /* UART2 */
++    [ 60] = { 0x6d000, 0x1000, 32 | 16 | 8 }, /* L4TA20 */
++    [ 61] = { 0x6e000, 0x1000,      16 | 8 }, /* UART3 */
++    [ 62] = { 0x6f000, 0x1000, 32 | 16 | 8 }, /* L4TA21 */
++    [ 63] = { 0x70000, 0x1000,      16     }, /* I2C1 */
++    [ 64] = { 0x71000, 0x1000, 32 | 16 | 8 }, /* L4TAO5 */
++    [ 65] = { 0x72000, 0x1000,      16     }, /* I2C2 */
++    [ 66] = { 0x73000, 0x1000, 32 | 16 | 8 }, /* L4TAO6 */
++    [ 67] = { 0x74000, 0x1000,      16     }, /* McBSP1 */
++    [ 68] = { 0x75000, 0x1000, 32 | 16 | 8 }, /* L4TAO7 */
++    [ 69] = { 0x76000, 0x1000,      16     }, /* McBSP2 */
++    [ 70] = { 0x77000, 0x1000, 32 | 16 | 8 }, /* L4TAO8 */
++    [ 71] = { 0x24000, 0x1000, 32 | 16 | 8 }, /* WD Timer 3 (DSP) */
++    [ 72] = { 0x25000, 0x1000, 32 | 16 | 8 }, /* L4TA5 */
++    [ 73] = { 0x26000, 0x1000, 32 | 16 | 8 }, /* WD Timer 4 (IVA) */
++    [ 74] = { 0x27000, 0x1000, 32 | 16 | 8 }, /* L4TA6 */
++    [ 75] = { 0x2a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 2 */
++    [ 76] = { 0x2b000, 0x1000, 32 | 16 | 8 }, /* L4TA8 */
++    [ 77] = { 0x78000, 0x1000, 32 | 16 | 8 }, /* GP Timer 3 */
++    [ 78] = { 0x79000, 0x1000, 32 | 16 | 8 }, /* L4TA22 */
++    [ 79] = { 0x7a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 4 */
++    [ 80] = { 0x7b000, 0x1000, 32 | 16 | 8 }, /* L4TA23 */
++    [ 81] = { 0x7c000, 0x1000, 32 | 16 | 8 }, /* GP Timer 5 */
++    [ 82] = { 0x7d000, 0x1000, 32 | 16 | 8 }, /* L4TA24 */
++    [ 83] = { 0x7e000, 0x1000, 32 | 16 | 8 }, /* GP Timer 6 */
++    [ 84] = { 0x7f000, 0x1000, 32 | 16 | 8 }, /* L4TA25 */
++    [ 85] = { 0x80000, 0x1000, 32 | 16 | 8 }, /* GP Timer 7 */
++    [ 86] = { 0x81000, 0x1000, 32 | 16 | 8 }, /* L4TA26 */
++    [ 87] = { 0x82000, 0x1000, 32 | 16 | 8 }, /* GP Timer 8 */
++    [ 88] = { 0x83000, 0x1000, 32 | 16 | 8 }, /* L4TA27 */
++    [ 89] = { 0x84000, 0x1000, 32 | 16 | 8 }, /* GP Timer 9 */
++    [ 90] = { 0x85000, 0x1000, 32 | 16 | 8 }, /* L4TA28 */
++    [ 91] = { 0x86000, 0x1000, 32 | 16 | 8 }, /* GP Timer 10 */
++    [ 92] = { 0x87000, 0x1000, 32 | 16 | 8 }, /* L4TA29 */
++    [ 93] = { 0x88000, 0x1000, 32 | 16 | 8 }, /* GP Timer 11 */
++    [ 94] = { 0x89000, 0x1000, 32 | 16 | 8 }, /* L4TA30 */
++    [ 95] = { 0x8a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 12 */
++    [ 96] = { 0x8b000, 0x1000, 32 | 16 | 8 }, /* L4TA31 */
++    [ 97] = { 0x90000, 0x1000,      16     }, /* EAC */
++    [ 98] = { 0x91000, 0x1000, 32 | 16 | 8 }, /* L4TA32 */
++    [ 99] = { 0x92000, 0x1000,      16     }, /* FAC */
++    [100] = { 0x93000, 0x1000, 32 | 16 | 8 }, /* L4TA33 */
++    [101] = { 0x94000, 0x1000, 32 | 16 | 8 }, /* IPC (MAILBOX) */
++    [102] = { 0x95000, 0x1000, 32 | 16 | 8 }, /* L4TA34 */
++    [103] = { 0x98000, 0x1000, 32 | 16 | 8 }, /* SPI1 */
++    [104] = { 0x99000, 0x1000, 32 | 16 | 8 }, /* L4TA35 */
++    [105] = { 0x9a000, 0x1000, 32 | 16 | 8 }, /* SPI2 */
++    [106] = { 0x9b000, 0x1000, 32 | 16 | 8 }, /* L4TA36 */
++    [107] = { 0x9c000, 0x1000,      16 | 8 }, /* MMC SDIO */
++    [108] = { 0x9d000, 0x1000, 32 | 16 | 8 }, /* L4TAO9 */
++    [109] = { 0x9e000, 0x1000, 32 | 16 | 8 }, /* MS_PRO */
++    [110] = { 0x9f000, 0x1000, 32 | 16 | 8 }, /* L4TAO10 */
++    [111] = { 0xa0000, 0x1000, 32          }, /* RNG */
++    [112] = { 0xa1000, 0x1000, 32 | 16 | 8 }, /* L4TAO11 */
++    [113] = { 0xa2000, 0x1000, 32          }, /* DES3DES */
++    [114] = { 0xa3000, 0x1000, 32 | 16 | 8 }, /* L4TAO12 */
++    [115] = { 0xa4000, 0x1000, 32          }, /* SHA1MD5 */
++    [116] = { 0xa5000, 0x1000, 32 | 16 | 8 }, /* L4TAO13 */
++    [117] = { 0xa6000, 0x1000, 32          }, /* AES */
++    [118] = { 0xa7000, 0x1000, 32 | 16 | 8 }, /* L4TA37 */
++    [119] = { 0xa8000, 0x2000, 32          }, /* PKA */
++    [120] = { 0xaa000, 0x1000, 32 | 16 | 8 }, /* L4TA38 */
++    [121] = { 0xb0000, 0x1000, 32          }, /* MG */
++    [122] = { 0xb1000, 0x1000, 32 | 16 | 8 },
++    [123] = { 0xb2000, 0x1000, 32          }, /* HDQ/1-Wire */
++    [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */
++};
++
++static struct omap_l4_agent_info_s {
++    int ta;
++    int region;
++    int regions;
++    int ta_region;
++} omap_l4_agent_info[54] = {
++    { 0,           0, 3, 2 }, /* L4IA initiatior agent */
++    { L4TAO(1),    3, 2, 1 }, /* Control and pinout module */
++    { L4TAO(2),    5, 2, 1 }, /* 32K timer */
++    { L4TAO(3),    7, 3, 2 }, /* PRCM */
++    { L4TA(1),    10, 2, 1 }, /* BCM */
++    { L4TA(2),    12, 2, 1 }, /* Test JTAG */
++    { L4TA(3),    14, 6, 3 }, /* Quad GPIO */
++    { L4TA(4),    20, 4, 3 }, /* WD timer 1/2 */
++    { L4TA(7),    24, 2, 1 }, /* GP timer 1 */
++    { L4TA(9),    26, 2, 1 }, /* ATM11 ETB */
++    { L4TA(10),   28, 5, 4 }, /* Display subsystem */
++    { L4TA(11),   33, 5, 4 }, /* Camera subsystem */
++    { L4TA(12),   38, 2, 1 }, /* sDMA */
++    { L4TA(13),   40, 5, 4 }, /* SSI */
++    { L4TAO(4),   45, 2, 1 }, /* USB */
++    { L4TA(14),   47, 2, 1 }, /* Win Tracer1 */
++    { L4TA(15),   49, 2, 1 }, /* Win Tracer2 */
++    { L4TA(16),   51, 2, 1 }, /* Win Tracer3 */
++    { L4TA(17),   53, 2, 1 }, /* Win Tracer4 */
++    { L4TA(18),   55, 2, 1 }, /* XTI */
++    { L4TA(19),   57, 2, 1 }, /* UART1 */
++    { L4TA(20),   59, 2, 1 }, /* UART2 */
++    { L4TA(21),   61, 2, 1 }, /* UART3 */
++    { L4TAO(5),   63, 2, 1 }, /* I2C1 */
++    { L4TAO(6),   65, 2, 1 }, /* I2C2 */
++    { L4TAO(7),   67, 2, 1 }, /* McBSP1 */
++    { L4TAO(8),   69, 2, 1 }, /* McBSP2 */
++    { L4TA(5),    71, 2, 1 }, /* WD Timer 3 (DSP) */
++    { L4TA(6),    73, 2, 1 }, /* WD Timer 4 (IVA) */
++    { L4TA(8),    75, 2, 1 }, /* GP Timer 2 */
++    { L4TA(22),   77, 2, 1 }, /* GP Timer 3 */
++    { L4TA(23),   79, 2, 1 }, /* GP Timer 4 */
++    { L4TA(24),   81, 2, 1 }, /* GP Timer 5 */
++    { L4TA(25),   83, 2, 1 }, /* GP Timer 6 */
++    { L4TA(26),   85, 2, 1 }, /* GP Timer 7 */
++    { L4TA(27),   87, 2, 1 }, /* GP Timer 8 */
++    { L4TA(28),   89, 2, 1 }, /* GP Timer 9 */
++    { L4TA(29),   91, 2, 1 }, /* GP Timer 10 */
++    { L4TA(30),   93, 2, 1 }, /* GP Timer 11 */
++    { L4TA(31),   95, 2, 1 }, /* GP Timer 12 */
++    { L4TA(32),   97, 2, 1 }, /* EAC */
++    { L4TA(33),   99, 2, 1 }, /* FAC */
++    { L4TA(34),  101, 2, 1 }, /* IPC */
++    { L4TA(35),  103, 2, 1 }, /* SPI1 */
++    { L4TA(36),  105, 2, 1 }, /* SPI2 */
++    { L4TAO(9),  107, 2, 1 }, /* MMC SDIO */
++    { L4TAO(10), 109, 2, 1 },
++    { L4TAO(11), 111, 2, 1 }, /* RNG */
++    { L4TAO(12), 113, 2, 1 }, /* DES3DES */
++    { L4TAO(13), 115, 2, 1 }, /* SHA1MD5 */
++    { L4TA(37),  117, 2, 1 }, /* AES */
++    { L4TA(38),  119, 2, 1 }, /* PKA */
++    { -1,        121, 2, 1 },
++    { L4TA(39),  123, 2, 1 }, /* HDQ/1-Wire */
++};
++
++#define omap_l4ta(bus, cs)    omap_l4ta_get(bus, L4TA(cs))
++#define omap_l4tao(bus, cs)   omap_l4ta_get(bus, L4TAO(cs))
++
++struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, int cs)
++{
++    int i, iomemtype;
++    struct omap_target_agent_s *ta = 0;
++    struct omap_l4_agent_info_s *info = 0;
++
++    for (i = 0; i < bus->ta_num; i ++)
++        if (omap_l4_agent_info[i].ta == cs) {
++            ta = &bus->ta[i];
++            info = &omap_l4_agent_info[i];
++            break;
++        }
++    if (!ta) {
++        fprintf(stderr, "%s: bad target agent (%i)\n", __FUNCTION__, cs);
++        exit(-1);
++    }
++
++    ta->bus = bus;
++    ta->start = &omap_l4_region[info->region];
++    ta->regions = info->regions;
++    ta->base = bus->base + ta->start[info->ta_region].offset;
++
++    ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
++    ta->status = 0x00000000;
++    ta->control = 0x00000200; /* XXX 01000200 for L4TAO */
++
++    iomemtype = cpu_register_io_memory(0, omap_l4ta_readfn,
++                    omap_l4ta_writefn, ta);
++    cpu_register_physical_memory(ta->base, 0x200, iomemtype);
++
++    return ta;
++}
++
++target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region,
++                int iotype)
++{
++    target_phys_addr_t base;
++    size_t size;
++
++    if (region < 0 || region >= ta->regions) {
++        fprintf(stderr, "%s: bad io region (%i)\n", __FUNCTION__, region);
++        exit(-1);
++    }
++
++    base = ta->bus->base + ta->start[region].offset;
++    size = ta->start[region].size;
++    if (iotype)
++        cpu_register_physical_memory(base, size, iotype);
++
++    return base;
++}
++
++/* TEST-Chip-level TAP */
++static uint32_t omap_tap_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
++    target_phys_addr_t reg = addr - s->tap_base;
++
++    switch (reg) {
++    case 0x204:       /* IDCODE_reg */
++        switch (s->mpu_model) {
++        case omap2420:
++        case omap2422:
++        case omap2423:
++            return 0x5b5d902f;        /* ES 2.2 */
++        case omap2430:
++            return 0x5b68a02f;        /* ES 2.2 */
++        case omap3430:
++            return 0x1b7ae02f;        /* ES 2 */
++        default:
++            cpu_abort(cpu_single_env, "%s: Bad mpu model\n", __FUNCTION__);
++        }
++
++    case 0x208:       /* PRODUCTION_ID_reg for OMAP2 */
++    case 0x210:       /* PRODUCTION_ID_reg for OMAP3 */
++        switch (s->mpu_model) {
++        case omap2420:
++            return 0x000200f0;        /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */
++        case omap2422:
++            return 0x000400f0;
++        case omap2423:
++            return 0x000800f0;
++        case omap2430:
++            return 0x000000f0;
++        case omap3430:
++            return 0x000000f0;
++        default:
++            cpu_abort(cpu_single_env, "%s: Bad mpu model\n", __FUNCTION__);
++        }
++
++    case 0x218:       /* DIE_ID_reg */
++        return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
++    case 0x21c:       /* DIE_ID_reg */
++        return (  5 << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
++    case 0x220:       /* DIE_ID_reg */
++        return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
++    case 0x224:       /* DIE_ID_reg */
++        return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_tap_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    OMAP_BAD_REG(addr);
++}
++
++static CPUReadMemoryFunc *omap_tap_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_tap_read,
++};
++
++static CPUWriteMemoryFunc *omap_tap_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_tap_write,
++};
++
++void omap_tap_init(struct omap_target_agent_s *ta,
++                struct omap_mpu_state_s *mpu)
++{
++    mpu->tap_base = omap_l4_attach(ta, 0, cpu_register_io_memory(0,
++                            omap_tap_readfn, omap_tap_writefn, mpu));
++}
++
++/* Power, Reset, and Clock Management */
++struct omap_prcm_s {
++    target_phys_addr_t base;
++    qemu_irq irq[3];
++    struct omap_mpu_state_s *mpu;
++
++    uint32_t irqst[3];
++    uint32_t irqen[3];
++
++    uint32_t sysconfig;
++    uint32_t voltctrl;
++    uint32_t scratch[20];
++
++    uint32_t clksrc[1];
++    uint32_t clkout[1];
++    uint32_t clkemul[1];
++    uint32_t clkpol[1];
++    uint32_t clksel[8];
++    uint32_t clken[12];
++    uint32_t clkctrl[4];
++    uint32_t clkidle[7];
++    uint32_t setuptime[2];
++
++    uint32_t wkup[3];
++    uint32_t wken[3];
++    uint32_t wkst[3];
++    uint32_t rst[4];
++    uint32_t rstctrl[1];
++    uint32_t power[4];
++    uint32_t rsttime_wkup;
++
++    uint32_t ev;
++    uint32_t evtime[2];
++};
++
++static void omap_prcm_int_update(struct omap_prcm_s *s, int dom)
++{
++    qemu_set_irq(s->irq[dom], s->irqst[dom] & s->irqen[dom]);
++    /* XXX or is the mask applied before PRCM_IRQSTATUS_* ? */
++}
++
++static uint32_t omap_prcm_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x000:       /* PRCM_REVISION */
++        return 0x10;
++
++    case 0x010:       /* PRCM_SYSCONFIG */
++        return s->sysconfig;
++
++    case 0x018:       /* PRCM_IRQSTATUS_MPU */
++        return s->irqst[0];
++
++    case 0x01c:       /* PRCM_IRQENABLE_MPU */
++        return s->irqen[0];
++
++    case 0x050:       /* PRCM_VOLTCTRL */
++        return s->voltctrl;
++    case 0x054:       /* PRCM_VOLTST */
++        return s->voltctrl & 3;
++
++    case 0x060:       /* PRCM_CLKSRC_CTRL */
++        return s->clksrc[0];
++    case 0x070:       /* PRCM_CLKOUT_CTRL */
++        return s->clkout[0];
++    case 0x078:       /* PRCM_CLKEMUL_CTRL */
++        return s->clkemul[0];
++    case 0x080:       /* PRCM_CLKCFG_CTRL */
++    case 0x084:       /* PRCM_CLKCFG_STATUS */
++        return 0;
++
++    case 0x090:       /* PRCM_VOLTSETUP */
++        return s->setuptime[0];
++
++    case 0x094:       /* PRCM_CLKSSETUP */
++        return s->setuptime[1];
++
++    case 0x098:       /* PRCM_POLCTRL */
++        return s->clkpol[0];
++
++    case 0x0b0:       /* GENERAL_PURPOSE1 */
++    case 0x0b4:       /* GENERAL_PURPOSE2 */
++    case 0x0b8:       /* GENERAL_PURPOSE3 */
++    case 0x0bc:       /* GENERAL_PURPOSE4 */
++    case 0x0c0:       /* GENERAL_PURPOSE5 */
++    case 0x0c4:       /* GENERAL_PURPOSE6 */
++    case 0x0c8:       /* GENERAL_PURPOSE7 */
++    case 0x0cc:       /* GENERAL_PURPOSE8 */
++    case 0x0d0:       /* GENERAL_PURPOSE9 */
++    case 0x0d4:       /* GENERAL_PURPOSE10 */
++    case 0x0d8:       /* GENERAL_PURPOSE11 */
++    case 0x0dc:       /* GENERAL_PURPOSE12 */
++    case 0x0e0:       /* GENERAL_PURPOSE13 */
++    case 0x0e4:       /* GENERAL_PURPOSE14 */
++    case 0x0e8:       /* GENERAL_PURPOSE15 */
++    case 0x0ec:       /* GENERAL_PURPOSE16 */
++    case 0x0f0:       /* GENERAL_PURPOSE17 */
++    case 0x0f4:       /* GENERAL_PURPOSE18 */
++    case 0x0f8:       /* GENERAL_PURPOSE19 */
++    case 0x0fc:       /* GENERAL_PURPOSE20 */
++        return s->scratch[(offset - 0xb0) >> 2];
++
++    case 0x140:       /* CM_CLKSEL_MPU */
++        return s->clksel[0];
++    case 0x148:       /* CM_CLKSTCTRL_MPU */
++        return s->clkctrl[0];
++
++    case 0x158:       /* RM_RSTST_MPU */
++        return s->rst[0];
++    case 0x1c8:       /* PM_WKDEP_MPU */
++        return s->wkup[0];
++    case 0x1d4:       /* PM_EVGENCTRL_MPU */
++        return s->ev;
++    case 0x1d8:       /* PM_EVEGENONTIM_MPU */
++        return s->evtime[0];
++    case 0x1dc:       /* PM_EVEGENOFFTIM_MPU */
++        return s->evtime[1];
++    case 0x1e0:       /* PM_PWSTCTRL_MPU */
++        return s->power[0];
++    case 0x1e4:       /* PM_PWSTST_MPU */
++        return 0;
++
++    case 0x200:       /* CM_FCLKEN1_CORE */
++        return s->clken[0];
++    case 0x204:       /* CM_FCLKEN2_CORE */
++        return s->clken[1];
++    case 0x210:       /* CM_ICLKEN1_CORE */
++        return s->clken[2];
++    case 0x214:       /* CM_ICLKEN2_CORE */
++        return s->clken[3];
++    case 0x21c:       /* CM_ICLKEN4_CORE */
++        return s->clken[4];
++
++    case 0x220:       /* CM_IDLEST1_CORE */
++        /* TODO: check the actual iclk status */
++        return 0x7ffffff9;
++    case 0x224:       /* CM_IDLEST2_CORE */
++        /* TODO: check the actual iclk status */
++        return 0x00000007;
++    case 0x22c:       /* CM_IDLEST4_CORE */
++        /* TODO: check the actual iclk status */
++        return 0x0000001f;
++
++    case 0x230:       /* CM_AUTOIDLE1_CORE */
++        return s->clkidle[0];
++    case 0x234:       /* CM_AUTOIDLE2_CORE */
++        return s->clkidle[1];
++    case 0x238:       /* CM_AUTOIDLE3_CORE */
++        return s->clkidle[2];
++    case 0x23c:       /* CM_AUTOIDLE4_CORE */
++        return s->clkidle[3];
++
++    case 0x240:       /* CM_CLKSEL1_CORE */
++        return s->clksel[1];
++    case 0x244:       /* CM_CLKSEL2_CORE */
++        return s->clksel[2];
++
++    case 0x248:       /* CM_CLKSTCTRL_CORE */
++        return s->clkctrl[1];
++
++    case 0x2a0:       /* PM_WKEN1_CORE */
++        return s->wken[0];
++    case 0x2a4:       /* PM_WKEN2_CORE */
++        return s->wken[1];
++
++    case 0x2b0:       /* PM_WKST1_CORE */
++        return s->wkst[0];
++    case 0x2b4:       /* PM_WKST2_CORE */
++        return s->wkst[1];
++    case 0x2c8:       /* PM_WKDEP_CORE */
++        return 0x1e;
++
++    case 0x2e0:       /* PM_PWSTCTRL_CORE */
++        return s->power[1];
++    case 0x2e4:       /* PM_PWSTST_CORE */
++        return 0x000030 | (s->power[1] & 0xfc00);
++
++    case 0x300:       /* CM_FCLKEN_GFX */
++        return s->clken[5];
++    case 0x310:       /* CM_ICLKEN_GFX */
++        return s->clken[6];
++    case 0x320:       /* CM_IDLEST_GFX */
++        /* TODO: check the actual iclk status */
++        return 0x00000001;
++    case 0x340:       /* CM_CLKSEL_GFX */
++        return s->clksel[3];
++    case 0x348:       /* CM_CLKSTCTRL_GFX */
++        return s->clkctrl[2];
++    case 0x350:       /* RM_RSTCTRL_GFX */
++        return s->rstctrl[0];
++    case 0x358:       /* RM_RSTST_GFX */
++        return s->rst[1];
++    case 0x3c8:       /* PM_WKDEP_GFX */
++        return s->wkup[1];
++
++    case 0x3e0:       /* PM_PWSTCTRL_GFX */
++        return s->power[2];
++    case 0x3e4:       /* PM_PWSTST_GFX */
++        return s->power[2] & 3;
++
++    case 0x400:       /* CM_FCLKEN_WKUP */
++        return s->clken[7];
++    case 0x410:       /* CM_ICLKEN_WKUP */
++        return s->clken[8];
++    case 0x420:       /* CM_IDLEST_WKUP */
++        /* TODO: check the actual iclk status */
++        return 0x0000003f;
++    case 0x430:       /* CM_AUTOIDLE_WKUP */
++        return s->clkidle[4];
++    case 0x440:       /* CM_CLKSEL_WKUP */
++        return s->clksel[4];
++    case 0x450:       /* RM_RSTCTRL_WKUP */
++        return 0;
++    case 0x454:       /* RM_RSTTIME_WKUP */
++        return s->rsttime_wkup;
++    case 0x458:       /* RM_RSTST_WKUP */
++        return s->rst[2];
++    case 0x4a0:       /* PM_WKEN_WKUP */
++        return s->wken[2];
++    case 0x4b0:       /* PM_WKST_WKUP */
++        return s->wkst[2];
++
++    case 0x500:       /* CM_CLKEN_PLL */
++        return s->clken[9];
++    case 0x520:       /* CM_IDLEST_CKGEN */
++        /* Core uses 32-kHz clock */
++        if (!(s->clksel[6] & 3))
++            return 0x00000377;
++        /* DPLL not in lock mode, core uses ref_clk */
++        if ((s->clken[9] & 3) != 3)
++            return 0x00000375;
++        /* Core uses DPLL */
++        return 0x00000376;
++    case 0x530:       /* CM_AUTOIDLE_PLL */
++        return s->clkidle[5];
++    case 0x540:       /* CM_CLKSEL1_PLL */
++        return s->clksel[5];
++    case 0x544:       /* CM_CLKSEL2_PLL */
++        return s->clksel[6];
++
++    case 0x800:       /* CM_FCLKEN_DSP */
++        return s->clken[10];
++    case 0x810:       /* CM_ICLKEN_DSP */
++        return s->clken[11];
++    case 0x820:       /* CM_IDLEST_DSP */
++        /* TODO: check the actual iclk status */
++        return 0x00000103;
++    case 0x830:       /* CM_AUTOIDLE_DSP */
++        return s->clkidle[6];
++    case 0x840:       /* CM_CLKSEL_DSP */
++        return s->clksel[7];
++    case 0x848:       /* CM_CLKSTCTRL_DSP */
++        return s->clkctrl[3];
++    case 0x850:       /* RM_RSTCTRL_DSP */
++        return 0;
++    case 0x858:       /* RM_RSTST_DSP */
++        return s->rst[3];
++    case 0x8c8:       /* PM_WKDEP_DSP */
++        return s->wkup[2];
++    case 0x8e0:       /* PM_PWSTCTRL_DSP */
++        return s->power[3];
++    case 0x8e4:       /* PM_PWSTST_DSP */
++        return 0x008030 | (s->power[3] & 0x3003);
++
++    case 0x8f0:       /* PRCM_IRQSTATUS_DSP */
++        return s->irqst[1];
++    case 0x8f4:       /* PRCM_IRQENABLE_DSP */
++        return s->irqen[1];
++
++    case 0x8f8:       /* PRCM_IRQSTATUS_IVA */
++        return s->irqst[2];
++    case 0x8fc:       /* PRCM_IRQENABLE_IVA */
++        return s->irqen[2];
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_prcm_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x000:       /* PRCM_REVISION */
++    case 0x054:       /* PRCM_VOLTST */
++    case 0x084:       /* PRCM_CLKCFG_STATUS */
++    case 0x1e4:       /* PM_PWSTST_MPU */
++    case 0x220:       /* CM_IDLEST1_CORE */
++    case 0x224:       /* CM_IDLEST2_CORE */
++    case 0x22c:       /* CM_IDLEST4_CORE */
++    case 0x2c8:       /* PM_WKDEP_CORE */
++    case 0x2e4:       /* PM_PWSTST_CORE */
++    case 0x320:       /* CM_IDLEST_GFX */
++    case 0x3e4:       /* PM_PWSTST_GFX */
++    case 0x420:       /* CM_IDLEST_WKUP */
++    case 0x520:       /* CM_IDLEST_CKGEN */
++    case 0x820:       /* CM_IDLEST_DSP */
++    case 0x8e4:       /* PM_PWSTST_DSP */
++        OMAP_RO_REG(addr);
++        return;
++
++    case 0x010:       /* PRCM_SYSCONFIG */
++        s->sysconfig = value & 1;
++        break;
++
++    case 0x018:       /* PRCM_IRQSTATUS_MPU */
++        s->irqst[0] &= ~value;
++        omap_prcm_int_update(s, 0);
++        break;
++    case 0x01c:       /* PRCM_IRQENABLE_MPU */
++        s->irqen[0] = value & 0x3f;
++        omap_prcm_int_update(s, 0);
++        break;
++
++    case 0x050:       /* PRCM_VOLTCTRL */
++        s->voltctrl = value & 0xf1c3;
++        break;
++
++    case 0x060:       /* PRCM_CLKSRC_CTRL */
++        s->clksrc[0] = value & 0xdb;
++        /* TODO update clocks */
++        break;
++
++    case 0x070:       /* PRCM_CLKOUT_CTRL */
++        s->clkout[0] = value & 0xbbbb;
++        /* TODO update clocks */
++        break;
++
++    case 0x078:       /* PRCM_CLKEMUL_CTRL */
++        s->clkemul[0] = value & 1;
++        /* TODO update clocks */
++        break;
++
++    case 0x080:       /* PRCM_CLKCFG_CTRL */
++        break;
++
++    case 0x090:       /* PRCM_VOLTSETUP */
++        s->setuptime[0] = value & 0xffff;
++        break;
++    case 0x094:       /* PRCM_CLKSSETUP */
++        s->setuptime[1] = value & 0xffff;
++        break;
++
++    case 0x098:       /* PRCM_POLCTRL */
++        s->clkpol[0] = value & 0x701;
++        break;
++
++    case 0x0b0:       /* GENERAL_PURPOSE1 */
++    case 0x0b4:       /* GENERAL_PURPOSE2 */
++    case 0x0b8:       /* GENERAL_PURPOSE3 */
++    case 0x0bc:       /* GENERAL_PURPOSE4 */
++    case 0x0c0:       /* GENERAL_PURPOSE5 */
++    case 0x0c4:       /* GENERAL_PURPOSE6 */
++    case 0x0c8:       /* GENERAL_PURPOSE7 */
++    case 0x0cc:       /* GENERAL_PURPOSE8 */
++    case 0x0d0:       /* GENERAL_PURPOSE9 */
++    case 0x0d4:       /* GENERAL_PURPOSE10 */
++    case 0x0d8:       /* GENERAL_PURPOSE11 */
++    case 0x0dc:       /* GENERAL_PURPOSE12 */
++    case 0x0e0:       /* GENERAL_PURPOSE13 */
++    case 0x0e4:       /* GENERAL_PURPOSE14 */
++    case 0x0e8:       /* GENERAL_PURPOSE15 */
++    case 0x0ec:       /* GENERAL_PURPOSE16 */
++    case 0x0f0:       /* GENERAL_PURPOSE17 */
++    case 0x0f4:       /* GENERAL_PURPOSE18 */
++    case 0x0f8:       /* GENERAL_PURPOSE19 */
++    case 0x0fc:       /* GENERAL_PURPOSE20 */
++        s->scratch[(offset - 0xb0) >> 2] = value;
++        break;
++
++    case 0x140:       /* CM_CLKSEL_MPU */
++        s->clksel[0] = value & 0x1f;
++        /* TODO update clocks */
++        break;
++    case 0x148:       /* CM_CLKSTCTRL_MPU */
++        s->clkctrl[0] = value & 0x1f;
++        break;
++
++    case 0x158:       /* RM_RSTST_MPU */
++        s->rst[0] &= ~value;
++        break;
++    case 0x1c8:       /* PM_WKDEP_MPU */
++        s->wkup[0] = value & 0x15;
++        break;
++
++    case 0x1d4:       /* PM_EVGENCTRL_MPU */
++        s->ev = value & 0x1f;
++        break;
++    case 0x1d8:       /* PM_EVEGENONTIM_MPU */
++        s->evtime[0] = value;
++        break;
++    case 0x1dc:       /* PM_EVEGENOFFTIM_MPU */
++        s->evtime[1] = value;
++        break;
++
++    case 0x1e0:       /* PM_PWSTCTRL_MPU */
++        s->power[0] = value & 0xc0f;
++        break;
++
++    case 0x200:       /* CM_FCLKEN1_CORE */
++        s->clken[0] = value & 0xbfffffff;
++        /* TODO update clocks */
++        break;
++    case 0x204:       /* CM_FCLKEN2_CORE */
++        s->clken[1] = value & 0x00000007;
++        /* TODO update clocks */
++        break;
++    case 0x210:       /* CM_ICLKEN1_CORE */
++        s->clken[2] = value & 0xfffffff9;
++        /* TODO update clocks */
++        break;
++    case 0x214:       /* CM_ICLKEN2_CORE */
++        s->clken[3] = value & 0x00000007;
++        /* TODO update clocks */
++        break;
++    case 0x21c:       /* CM_ICLKEN4_CORE */
++        s->clken[4] = value & 0x0000001f;
++        /* TODO update clocks */
++        break;
++
++    case 0x230:       /* CM_AUTOIDLE1_CORE */
++        s->clkidle[0] = value & 0xfffffff9;
++        /* TODO update clocks */
++        break;
++    case 0x234:       /* CM_AUTOIDLE2_CORE */
++        s->clkidle[1] = value & 0x00000007;
++        /* TODO update clocks */
++        break;
++    case 0x238:       /* CM_AUTOIDLE3_CORE */
++        s->clkidle[2] = value & 0x00000007;
++        /* TODO update clocks */
++        break;
++    case 0x23c:       /* CM_AUTOIDLE4_CORE */
++        s->clkidle[3] = value & 0x0000001f;
++        /* TODO update clocks */
++        break;
++
++    case 0x240:       /* CM_CLKSEL1_CORE */
++        s->clksel[1] = value & 0x0fffbf7f;
++        /* TODO update clocks */
++        break;
++
++    case 0x244:       /* CM_CLKSEL2_CORE */
++        s->clksel[2] = value & 0x00fffffc;
++        /* TODO update clocks */
++        break;
++
++    case 0x248:       /* CM_CLKSTCTRL_CORE */
++        s->clkctrl[1] = value & 0x7;
++        break;
++
++    case 0x2a0:       /* PM_WKEN1_CORE */
++        s->wken[0] = value & 0x04667ff8;
++        break;
++    case 0x2a4:       /* PM_WKEN2_CORE */
++        s->wken[1] = value & 0x00000005;
++        break;
++
++    case 0x2b0:       /* PM_WKST1_CORE */
++        s->wkst[0] &= ~value;
++        break;
++    case 0x2b4:       /* PM_WKST2_CORE */
++        s->wkst[1] &= ~value;
++        break;
++
++    case 0x2e0:       /* PM_PWSTCTRL_CORE */
++        s->power[1] = (value & 0x00fc3f) | (1 << 2);
++        break;
++
++    case 0x300:       /* CM_FCLKEN_GFX */
++        s->clken[5] = value & 6;
++        /* TODO update clocks */
++        break;
++    case 0x310:       /* CM_ICLKEN_GFX */
++        s->clken[6] = value & 1;
++        /* TODO update clocks */
++        break;
++    case 0x340:       /* CM_CLKSEL_GFX */
++        s->clksel[3] = value & 7;
++        /* TODO update clocks */
++        break;
++    case 0x348:       /* CM_CLKSTCTRL_GFX */
++        s->clkctrl[2] = value & 1;
++        break;
++    case 0x350:       /* RM_RSTCTRL_GFX */
++        s->rstctrl[0] = value & 1;
++        /* TODO: reset */
++        break;
++    case 0x358:       /* RM_RSTST_GFX */
++        s->rst[1] &= ~value;
++        break;
++    case 0x3c8:       /* PM_WKDEP_GFX */
++        s->wkup[1] = value & 0x13;
++        break;
++    case 0x3e0:       /* PM_PWSTCTRL_GFX */
++        s->power[2] = (value & 0x00c0f) | (3 << 2);
++        break;
++
++    case 0x400:       /* CM_FCLKEN_WKUP */
++        s->clken[7] = value & 0xd;
++        /* TODO update clocks */
++        break;
++    case 0x410:       /* CM_ICLKEN_WKUP */
++        s->clken[8] = value & 0x3f;
++        /* TODO update clocks */
++        break;
++    case 0x430:       /* CM_AUTOIDLE_WKUP */
++        s->clkidle[4] = value & 0x0000003f;
++        /* TODO update clocks */
++        break;
++    case 0x440:       /* CM_CLKSEL_WKUP */
++        s->clksel[4] = value & 3;
++        /* TODO update clocks */
++        break;
++    case 0x450:       /* RM_RSTCTRL_WKUP */
++        /* TODO: reset */
++        if (value & 2)
++            qemu_system_reset_request();
++        break;
++    case 0x454:       /* RM_RSTTIME_WKUP */
++        s->rsttime_wkup = value & 0x1fff;
++        break;
++    case 0x458:       /* RM_RSTST_WKUP */
++        s->rst[2] &= ~value;
++        break;
++    case 0x4a0:       /* PM_WKEN_WKUP */
++        s->wken[2] = value & 0x00000005;
++        break;
++    case 0x4b0:       /* PM_WKST_WKUP */
++        s->wkst[2] &= ~value;
++        break;
++
++    case 0x500:       /* CM_CLKEN_PLL */
++        s->clken[9] = value & 0xcf;
++        /* TODO update clocks */
++        break;
++    case 0x530:       /* CM_AUTOIDLE_PLL */
++        s->clkidle[5] = value & 0x000000cf;
++        /* TODO update clocks */
++        break;
++    case 0x540:       /* CM_CLKSEL1_PLL */
++        s->clksel[5] = value & 0x03bfff28;
++        /* TODO update clocks */
++        break;
++    case 0x544:       /* CM_CLKSEL2_PLL */
++        s->clksel[6] = value & 3;
++        /* TODO update clocks */
++        break;
++
++    case 0x800:       /* CM_FCLKEN_DSP */
++        s->clken[10] = value & 0x501;
++        /* TODO update clocks */
++        break;
++    case 0x810:       /* CM_ICLKEN_DSP */
++        s->clken[11] = value & 0x2;
++        /* TODO update clocks */
++        break;
++    case 0x830:       /* CM_AUTOIDLE_DSP */
++        s->clkidle[6] = value & 0x2;
++        /* TODO update clocks */
++        break;
++    case 0x840:       /* CM_CLKSEL_DSP */
++        s->clksel[7] = value & 0x3fff;
++        /* TODO update clocks */
++        break;
++    case 0x848:       /* CM_CLKSTCTRL_DSP */
++        s->clkctrl[3] = value & 0x101;
++        break;
++    case 0x850:       /* RM_RSTCTRL_DSP */
++        /* TODO: reset */
++        break;
++    case 0x858:       /* RM_RSTST_DSP */
++        s->rst[3] &= ~value;
++        break;
++    case 0x8c8:       /* PM_WKDEP_DSP */
++        s->wkup[2] = value & 0x13;
++        break;
++    case 0x8e0:       /* PM_PWSTCTRL_DSP */
++        s->power[3] = (value & 0x03017) | (3 << 2);
++        break;
++
++    case 0x8f0:       /* PRCM_IRQSTATUS_DSP */
++        s->irqst[1] &= ~value;
++        omap_prcm_int_update(s, 1);
++        break;
++    case 0x8f4:       /* PRCM_IRQENABLE_DSP */
++        s->irqen[1] = value & 0x7;
++        omap_prcm_int_update(s, 1);
++        break;
++
++    case 0x8f8:       /* PRCM_IRQSTATUS_IVA */
++        s->irqst[2] &= ~value;
++        omap_prcm_int_update(s, 2);
++        break;
++    case 0x8fc:       /* PRCM_IRQENABLE_IVA */
++        s->irqen[2] = value & 0x7;
++        omap_prcm_int_update(s, 2);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_prcm_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_prcm_read,
++};
++
++static CPUWriteMemoryFunc *omap_prcm_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_prcm_write,
++};
++
++static void omap_prcm_reset(struct omap_prcm_s *s)
++{
++    s->sysconfig = 0;
++    s->irqst[0] = 0;
++    s->irqst[1] = 0;
++    s->irqst[2] = 0;
++    s->irqen[0] = 0;
++    s->irqen[1] = 0;
++    s->irqen[2] = 0;
++    s->voltctrl = 0x1040;
++    s->ev = 0x14;
++    s->evtime[0] = 0;
++    s->evtime[1] = 0;
++    s->clkctrl[0] = 0;
++    s->clkctrl[1] = 0;
++    s->clkctrl[2] = 0;
++    s->clkctrl[3] = 0;
++    s->clken[1] = 7;
++    s->clken[3] = 7;
++    s->clken[4] = 0;
++    s->clken[5] = 0;
++    s->clken[6] = 0;
++    s->clken[7] = 0xc;
++    s->clken[8] = 0x3e;
++    s->clken[9] = 0x0d;
++    s->clken[10] = 0;
++    s->clken[11] = 0;
++    s->clkidle[0] = 0;
++    s->clkidle[2] = 7;
++    s->clkidle[3] = 0;
++    s->clkidle[4] = 0;
++    s->clkidle[5] = 0x0c;
++    s->clkidle[6] = 0;
++    s->clksel[0] = 0x01;
++    s->clksel[1] = 0x02100121;
++    s->clksel[2] = 0x00000000;
++    s->clksel[3] = 0x01;
++    s->clksel[4] = 0;
++    s->clksel[7] = 0x0121;
++    s->wkup[0] = 0x15;
++    s->wkup[1] = 0x13;
++    s->wkup[2] = 0x13;
++    s->wken[0] = 0x04667ff8;
++    s->wken[1] = 0x00000005;
++    s->wken[2] = 5;
++    s->wkst[0] = 0;
++    s->wkst[1] = 0;
++    s->wkst[2] = 0;
++    s->power[0] = 0x00c;
++    s->power[1] = 4;
++    s->power[2] = 0x0000c;
++    s->power[3] = 0x14;
++    s->rstctrl[0] = 1;
++    s->rst[3] = 1;
++}
++
++static void omap_prcm_coldreset(struct omap_prcm_s *s)
++{
++    s->setuptime[0] = 0;
++    s->setuptime[1] = 0;
++    memset(&s->scratch, 0, sizeof(s->scratch));
++    s->rst[0] = 0x01;
++    s->rst[1] = 0x00;
++    s->rst[2] = 0x01;
++    s->clken[0] = 0;
++    s->clken[2] = 0;
++    s->clkidle[1] = 0;
++    s->clksel[5] = 0;
++    s->clksel[6] = 2;
++    s->clksrc[0] = 0x43;
++    s->clkout[0] = 0x0303;
++    s->clkemul[0] = 0;
++    s->clkpol[0] = 0x100;
++    s->rsttime_wkup = 0x1002;
++
++    omap_prcm_reset(s);
++}
++
++struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
++                qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
++                struct omap_mpu_state_s *mpu)
++{
++    int iomemtype;
++    struct omap_prcm_s *s = (struct omap_prcm_s *)
++            qemu_mallocz(sizeof(struct omap_prcm_s));
++
++    s->irq[0] = mpu_int;
++    s->irq[1] = dsp_int;
++    s->irq[2] = iva_int;
++    s->mpu = mpu;
++    omap_prcm_coldreset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_prcm_readfn,
++                    omap_prcm_writefn, s);
++    s->base = omap_l4_attach(ta, 0, iomemtype);
++    omap_l4_attach(ta, 1, iomemtype);
++
++    return s;
++}
++
++/* System and Pinout control */
++struct omap_sysctl_s {
++    target_phys_addr_t base;
++    struct omap_mpu_state_s *mpu;
++
++    uint32_t sysconfig;
++    uint32_t devconfig;
++    uint32_t psaconfig;
++    uint32_t padconf[0x45];
++    uint8_t obs;
++    uint32_t msuspendmux[5];
++};
++
++static uint32_t omap_sysctl_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x000:       /* CONTROL_REVISION */
++        return 0x20;
++
++    case 0x010:       /* CONTROL_SYSCONFIG */
++        return s->sysconfig;
++
++    case 0x030 ... 0x140:     /* CONTROL_PADCONF - only used in the POP */
++        return s->padconf[(offset - 0x30) >> 2];
++
++    case 0x270:       /* CONTROL_DEBOBS */
++        return s->obs;
++
++    case 0x274:       /* CONTROL_DEVCONF */
++        return s->devconfig;
++
++    case 0x28c:       /* CONTROL_EMU_SUPPORT */
++        return 0;
++
++    case 0x290:       /* CONTROL_MSUSPENDMUX_0 */
++        return s->msuspendmux[0];
++    case 0x294:       /* CONTROL_MSUSPENDMUX_1 */
++        return s->msuspendmux[1];
++    case 0x298:       /* CONTROL_MSUSPENDMUX_2 */
++        return s->msuspendmux[2];
++    case 0x29c:       /* CONTROL_MSUSPENDMUX_3 */
++        return s->msuspendmux[3];
++    case 0x2a0:       /* CONTROL_MSUSPENDMUX_4 */
++        return s->msuspendmux[4];
++    case 0x2a4:       /* CONTROL_MSUSPENDMUX_5 */
++        return 0;
++
++    case 0x2b8:       /* CONTROL_PSA_CTRL */
++        return s->psaconfig;
++    case 0x2bc:       /* CONTROL_PSA_CMD */
++    case 0x2c0:       /* CONTROL_PSA_VALUE */
++        return 0;
++
++    case 0x2b0:       /* CONTROL_SEC_CTRL */
++        return 0x800000f1;
++    case 0x2d0:       /* CONTROL_SEC_EMU */
++        return 0x80000015;
++    case 0x2d4:       /* CONTROL_SEC_TAP */
++        return 0x8000007f;
++    case 0x2b4:       /* CONTROL_SEC_TEST */
++    case 0x2f0:       /* CONTROL_SEC_STATUS */
++    case 0x2f4:       /* CONTROL_SEC_ERR_STATUS */
++        /* Secure mode is not present on general-pusrpose device.  Outside
++         * secure mode these values cannot be read or written.  */
++        return 0;
++
++    case 0x2d8:       /* CONTROL_OCM_RAM_PERM */
++        return 0xff;
++    case 0x2dc:       /* CONTROL_OCM_PUB_RAM_ADD */
++    case 0x2e0:       /* CONTROL_EXT_SEC_RAM_START_ADD */
++    case 0x2e4:       /* CONTROL_EXT_SEC_RAM_STOP_ADD */
++        /* No secure mode so no Extended Secure RAM present.  */
++        return 0;
++
++    case 0x2f8:       /* CONTROL_STATUS */
++        /* Device Type => General-purpose */
++        return 0x0300;
++    case 0x2fc:       /* CONTROL_GENERAL_PURPOSE_STATUS */
++
++    case 0x300:       /* CONTROL_RPUB_KEY_H_0 */
++    case 0x304:       /* CONTROL_RPUB_KEY_H_1 */
++    case 0x308:       /* CONTROL_RPUB_KEY_H_2 */
++    case 0x30c:       /* CONTROL_RPUB_KEY_H_3 */
++        return 0xdecafbad;
++
++    case 0x310:       /* CONTROL_RAND_KEY_0 */
++    case 0x314:       /* CONTROL_RAND_KEY_1 */
++    case 0x318:       /* CONTROL_RAND_KEY_2 */
++    case 0x31c:       /* CONTROL_RAND_KEY_3 */
++    case 0x320:       /* CONTROL_CUST_KEY_0 */
++    case 0x324:       /* CONTROL_CUST_KEY_1 */
++    case 0x330:       /* CONTROL_TEST_KEY_0 */
++    case 0x334:       /* CONTROL_TEST_KEY_1 */
++    case 0x338:       /* CONTROL_TEST_KEY_2 */
++    case 0x33c:       /* CONTROL_TEST_KEY_3 */
++    case 0x340:       /* CONTROL_TEST_KEY_4 */
++    case 0x344:       /* CONTROL_TEST_KEY_5 */
++    case 0x348:       /* CONTROL_TEST_KEY_6 */
++    case 0x34c:       /* CONTROL_TEST_KEY_7 */
++    case 0x350:       /* CONTROL_TEST_KEY_8 */
++    case 0x354:       /* CONTROL_TEST_KEY_9 */
++        /* Can only be accessed in secure mode and when C_FieldAccEnable
++         * bit is set in CONTROL_SEC_CTRL.
++         * TODO: otherwise an interconnect access error is generated.  */
++        return 0;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_sysctl_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x000:       /* CONTROL_REVISION */
++    case 0x2a4:       /* CONTROL_MSUSPENDMUX_5 */
++    case 0x2c0:       /* CONTROL_PSA_VALUE */
++    case 0x2f8:       /* CONTROL_STATUS */
++    case 0x2fc:       /* CONTROL_GENERAL_PURPOSE_STATUS */
++    case 0x300:       /* CONTROL_RPUB_KEY_H_0 */
++    case 0x304:       /* CONTROL_RPUB_KEY_H_1 */
++    case 0x308:       /* CONTROL_RPUB_KEY_H_2 */
++    case 0x30c:       /* CONTROL_RPUB_KEY_H_3 */
++    case 0x310:       /* CONTROL_RAND_KEY_0 */
++    case 0x314:       /* CONTROL_RAND_KEY_1 */
++    case 0x318:       /* CONTROL_RAND_KEY_2 */
++    case 0x31c:       /* CONTROL_RAND_KEY_3 */
++    case 0x320:       /* CONTROL_CUST_KEY_0 */
++    case 0x324:       /* CONTROL_CUST_KEY_1 */
++    case 0x330:       /* CONTROL_TEST_KEY_0 */
++    case 0x334:       /* CONTROL_TEST_KEY_1 */
++    case 0x338:       /* CONTROL_TEST_KEY_2 */
++    case 0x33c:       /* CONTROL_TEST_KEY_3 */
++    case 0x340:       /* CONTROL_TEST_KEY_4 */
++    case 0x344:       /* CONTROL_TEST_KEY_5 */
++    case 0x348:       /* CONTROL_TEST_KEY_6 */
++    case 0x34c:       /* CONTROL_TEST_KEY_7 */
++    case 0x350:       /* CONTROL_TEST_KEY_8 */
++    case 0x354:       /* CONTROL_TEST_KEY_9 */
++        OMAP_RO_REG(addr);
++        return;
++
++    case 0x010:       /* CONTROL_SYSCONFIG */
++        s->sysconfig = value & 0x1e;
++        break;
++
++    case 0x030 ... 0x140:     /* CONTROL_PADCONF - only used in the POP */
++        /* XXX: should check constant bits */
++        s->padconf[(offset - 0x30) >> 2] = value & 0x1f1f1f1f;
++        break;
++
++    case 0x270:       /* CONTROL_DEBOBS */
++        s->obs = value & 0xff;
++        break;
++
++    case 0x274:       /* CONTROL_DEVCONF */
++        s->devconfig = value & 0xffffc7ff;
++        break;
++
++    case 0x28c:       /* CONTROL_EMU_SUPPORT */
++        break;
++
++    case 0x290:       /* CONTROL_MSUSPENDMUX_0 */
++        s->msuspendmux[0] = value & 0x3fffffff;
++        break;
++    case 0x294:       /* CONTROL_MSUSPENDMUX_1 */
++        s->msuspendmux[1] = value & 0x3fffffff;
++        break;
++    case 0x298:       /* CONTROL_MSUSPENDMUX_2 */
++        s->msuspendmux[2] = value & 0x3fffffff;
++        break;
++    case 0x29c:       /* CONTROL_MSUSPENDMUX_3 */
++        s->msuspendmux[3] = value & 0x3fffffff;
++        break;
++    case 0x2a0:       /* CONTROL_MSUSPENDMUX_4 */
++        s->msuspendmux[4] = value & 0x3fffffff;
++        break;
++
++    case 0x2b8:       /* CONTROL_PSA_CTRL */
++        s->psaconfig = value & 0x1c;
++        s->psaconfig |= (value & 0x20) ? 2 : 1;
++        break;
++    case 0x2bc:       /* CONTROL_PSA_CMD */
++        break;
++
++    case 0x2b0:       /* CONTROL_SEC_CTRL */
++    case 0x2b4:       /* CONTROL_SEC_TEST */
++    case 0x2d0:       /* CONTROL_SEC_EMU */
++    case 0x2d4:       /* CONTROL_SEC_TAP */
++    case 0x2d8:       /* CONTROL_OCM_RAM_PERM */
++    case 0x2dc:       /* CONTROL_OCM_PUB_RAM_ADD */
++    case 0x2e0:       /* CONTROL_EXT_SEC_RAM_START_ADD */
++    case 0x2e4:       /* CONTROL_EXT_SEC_RAM_STOP_ADD */
++    case 0x2f0:       /* CONTROL_SEC_STATUS */
++    case 0x2f4:       /* CONTROL_SEC_ERR_STATUS */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_sysctl_readfn[] = {
++    omap_badwidth_read32,     /* TODO */
++    omap_badwidth_read32,     /* TODO */
++    omap_sysctl_read,
++};
++
++static CPUWriteMemoryFunc *omap_sysctl_writefn[] = {
++    omap_badwidth_write32,    /* TODO */
++    omap_badwidth_write32,    /* TODO */
++    omap_sysctl_write,
++};
++
++static void omap_sysctl_reset(struct omap_sysctl_s *s)
++{
++    /* (power-on reset) */
++    s->sysconfig = 0;
++    s->obs = 0;
++    s->devconfig = 0x0c000000;
++    s->msuspendmux[0] = 0x00000000;
++    s->msuspendmux[1] = 0x00000000;
++    s->msuspendmux[2] = 0x00000000;
++    s->msuspendmux[3] = 0x00000000;
++    s->msuspendmux[4] = 0x00000000;
++    s->psaconfig = 1;
++
++    s->padconf[0x00] = 0x000f0f0f;
++    s->padconf[0x01] = 0x00000000;
++    s->padconf[0x02] = 0x00000000;
++    s->padconf[0x03] = 0x00000000;
++    s->padconf[0x04] = 0x00000000;
++    s->padconf[0x05] = 0x00000000;
++    s->padconf[0x06] = 0x00000000;
++    s->padconf[0x07] = 0x00000000;
++    s->padconf[0x08] = 0x08080800;
++    s->padconf[0x09] = 0x08080808;
++    s->padconf[0x0a] = 0x08080808;
++    s->padconf[0x0b] = 0x08080808;
++    s->padconf[0x0c] = 0x08080808;
++    s->padconf[0x0d] = 0x08080800;
++    s->padconf[0x0e] = 0x08080808;
++    s->padconf[0x0f] = 0x08080808;
++    s->padconf[0x10] = 0x18181808;    /* | 0x07070700 if SBoot3 */
++    s->padconf[0x11] = 0x18181818;    /* | 0x07070707 if SBoot3 */
++    s->padconf[0x12] = 0x18181818;    /* | 0x07070707 if SBoot3 */
++    s->padconf[0x13] = 0x18181818;    /* | 0x07070707 if SBoot3 */
++    s->padconf[0x14] = 0x18181818;    /* | 0x00070707 if SBoot3 */
++    s->padconf[0x15] = 0x18181818;
++    s->padconf[0x16] = 0x18181818;    /* | 0x07000000 if SBoot3 */
++    s->padconf[0x17] = 0x1f001f00;
++    s->padconf[0x18] = 0x1f1f1f1f;
++    s->padconf[0x19] = 0x00000000;
++    s->padconf[0x1a] = 0x1f180000;
++    s->padconf[0x1b] = 0x00001f1f;
++    s->padconf[0x1c] = 0x1f001f00;
++    s->padconf[0x1d] = 0x00000000;
++    s->padconf[0x1e] = 0x00000000;
++    s->padconf[0x1f] = 0x08000000;
++    s->padconf[0x20] = 0x08080808;
++    s->padconf[0x21] = 0x08080808;
++    s->padconf[0x22] = 0x0f080808;
++    s->padconf[0x23] = 0x0f0f0f0f;
++    s->padconf[0x24] = 0x000f0f0f;
++    s->padconf[0x25] = 0x1f1f1f0f;
++    s->padconf[0x26] = 0x080f0f1f;
++    s->padconf[0x27] = 0x070f1808;
++    s->padconf[0x28] = 0x0f070707;
++    s->padconf[0x29] = 0x000f0f1f;
++    s->padconf[0x2a] = 0x0f0f0f1f;
++    s->padconf[0x2b] = 0x08000000;
++    s->padconf[0x2c] = 0x0000001f;
++    s->padconf[0x2d] = 0x0f0f1f00;
++    s->padconf[0x2e] = 0x1f1f0f0f;
++    s->padconf[0x2f] = 0x0f1f1f1f;
++    s->padconf[0x30] = 0x0f0f0f0f;
++    s->padconf[0x31] = 0x0f1f0f1f;
++    s->padconf[0x32] = 0x0f0f0f0f;
++    s->padconf[0x33] = 0x0f1f0f1f;
++    s->padconf[0x34] = 0x1f1f0f0f;
++    s->padconf[0x35] = 0x0f0f1f1f;
++    s->padconf[0x36] = 0x0f0f1f0f;
++    s->padconf[0x37] = 0x0f0f0f0f;
++    s->padconf[0x38] = 0x1f18180f;
++    s->padconf[0x39] = 0x1f1f1f1f;
++    s->padconf[0x3a] = 0x00001f1f;
++    s->padconf[0x3b] = 0x00000000;
++    s->padconf[0x3c] = 0x00000000;
++    s->padconf[0x3d] = 0x0f0f0f0f;
++    s->padconf[0x3e] = 0x18000f0f;
++    s->padconf[0x3f] = 0x00070000;
++    s->padconf[0x40] = 0x00000707;
++    s->padconf[0x41] = 0x0f1f0700;
++    s->padconf[0x42] = 0x1f1f070f;
++    s->padconf[0x43] = 0x0008081f;
++    s->padconf[0x44] = 0x00000800;
++}
++
++struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
++                omap_clk iclk, struct omap_mpu_state_s *mpu)
++{
++    int iomemtype;
++    struct omap_sysctl_s *s = (struct omap_sysctl_s *)
++            qemu_mallocz(sizeof(struct omap_sysctl_s));
++
++    s->mpu = mpu;
++    omap_sysctl_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_sysctl_readfn,
++                    omap_sysctl_writefn, s);
++    s->base = omap_l4_attach(ta, 0, iomemtype);
++    omap_l4_attach(ta, 0, iomemtype);
++
++    return s;
++}
++
++/* SDRAM Controller Subsystem */
++struct omap_sdrc_s {
++    target_phys_addr_t base;
++
++    uint8_t config;
++};
++
++static void omap_sdrc_reset(struct omap_sdrc_s *s)
++{
++    s->config = 0x10;
++}
++
++static uint32_t omap_sdrc_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* SDRC_REVISION */
++        return 0x20;
++
++    case 0x10:        /* SDRC_SYSCONFIG */
++        return s->config;
++
++    case 0x14:        /* SDRC_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x40:        /* SDRC_CS_CFG */
++    case 0x44:        /* SDRC_SHARING */
++    case 0x48:        /* SDRC_ERR_ADDR */
++    case 0x4c:        /* SDRC_ERR_TYPE */
++    case 0x60:        /* SDRC_DLLA_SCTRL */
++    case 0x64:        /* SDRC_DLLA_STATUS */
++    case 0x68:        /* SDRC_DLLB_CTRL */
++    case 0x6c:        /* SDRC_DLLB_STATUS */
++    case 0x70:        /* SDRC_POWER */
++    case 0x80:        /* SDRC_MCFG_0 */
++    case 0x84:        /* SDRC_MR_0 */
++    case 0x88:        /* SDRC_EMR1_0 */
++    case 0x8c:        /* SDRC_EMR2_0 */
++    case 0x90:        /* SDRC_EMR3_0 */
++    case 0x94:        /* SDRC_DCDL1_CTRL */
++    case 0x98:        /* SDRC_DCDL2_CTRL */
++    case 0x9c:        /* SDRC_ACTIM_CTRLA_0 */
++    case 0xa0:        /* SDRC_ACTIM_CTRLB_0 */
++    case 0xa4:        /* SDRC_RFR_CTRL_0 */
++    case 0xa8:        /* SDRC_MANUAL_0 */
++    case 0xb0:        /* SDRC_MCFG_1 */
++    case 0xb4:        /* SDRC_MR_1 */
++    case 0xb8:        /* SDRC_EMR1_1 */
++    case 0xbc:        /* SDRC_EMR2_1 */
++    case 0xc0:        /* SDRC_EMR3_1 */
++    case 0xc4:        /* SDRC_ACTIM_CTRLA_1 */
++    case 0xc8:        /* SDRC_ACTIM_CTRLB_1 */
++    case 0xd4:        /* SDRC_RFR_CTRL_1 */
++    case 0xd8:        /* SDRC_MANUAL_1 */
++        return 0x00;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_sdrc_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque;
++    int offset = addr - s->base;
++
++    switch (offset) {
++    case 0x00:        /* SDRC_REVISION */
++    case 0x14:        /* SDRC_SYSSTATUS */
++    case 0x48:        /* SDRC_ERR_ADDR */
++    case 0x64:        /* SDRC_DLLA_STATUS */
++    case 0x6c:        /* SDRC_DLLB_STATUS */
++        OMAP_RO_REG(addr);
++        return;
++
++    case 0x10:        /* SDRC_SYSCONFIG */
++        if ((value >> 3) != 0x2)
++            fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
++                            __FUNCTION__, value >> 3);
++        if (value & 2)
++            omap_sdrc_reset(s);
++        s->config = value & 0x18;
++        break;
++
++    case 0x40:        /* SDRC_CS_CFG */
++    case 0x44:        /* SDRC_SHARING */
++    case 0x4c:        /* SDRC_ERR_TYPE */
++    case 0x60:        /* SDRC_DLLA_SCTRL */
++    case 0x68:        /* SDRC_DLLB_CTRL */
++    case 0x70:        /* SDRC_POWER */
++    case 0x80:        /* SDRC_MCFG_0 */
++    case 0x84:        /* SDRC_MR_0 */
++    case 0x88:        /* SDRC_EMR1_0 */
++    case 0x8c:        /* SDRC_EMR2_0 */
++    case 0x90:        /* SDRC_EMR3_0 */
++    case 0x94:        /* SDRC_DCDL1_CTRL */
++    case 0x98:        /* SDRC_DCDL2_CTRL */
++    case 0x9c:        /* SDRC_ACTIM_CTRLA_0 */
++    case 0xa0:        /* SDRC_ACTIM_CTRLB_0 */
++    case 0xa4:        /* SDRC_RFR_CTRL_0 */
++    case 0xa8:        /* SDRC_MANUAL_0 */
++    case 0xb0:        /* SDRC_MCFG_1 */
++    case 0xb4:        /* SDRC_MR_1 */
++    case 0xb8:        /* SDRC_EMR1_1 */
++    case 0xbc:        /* SDRC_EMR2_1 */
++    case 0xc0:        /* SDRC_EMR3_1 */
++    case 0xc4:        /* SDRC_ACTIM_CTRLA_1 */
++    case 0xc8:        /* SDRC_ACTIM_CTRLB_1 */
++    case 0xd4:        /* SDRC_RFR_CTRL_1 */
++    case 0xd8:        /* SDRC_MANUAL_1 */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_sdrc_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_sdrc_read,
++};
++
++static CPUWriteMemoryFunc *omap_sdrc_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_sdrc_write,
++};
++
++struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base)
++{
++    int iomemtype;
++    struct omap_sdrc_s *s = (struct omap_sdrc_s *)
++            qemu_mallocz(sizeof(struct omap_sdrc_s));
++
++    s->base = base;
++    omap_sdrc_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_sdrc_readfn,
++                    omap_sdrc_writefn, s);
++    cpu_register_physical_memory(s->base, 0x1000, iomemtype);
++
++    return s;
++}
++
++/* General-Purpose Memory Controller */
++struct omap_gpmc_s {
++    target_phys_addr_t base;
++    qemu_irq irq;
++
++    uint8_t sysconfig;
++    uint16_t irqst;
++    uint16_t irqen;
++    uint16_t timeout;
++    uint16_t config;
++    uint32_t prefconfig[2];
++    int prefcontrol;
++    int preffifo;
++    int prefcount;
++    struct omap_gpmc_cs_file_s {
++        uint32_t config[7];
++        target_phys_addr_t base;
++        size_t size;
++        int iomemtype;
++        void (*base_update)(void *opaque, target_phys_addr_t new);
++        void (*unmap)(void *opaque);
++        void *opaque;
++    } cs_file[8];
++    int ecc_cs;
++    int ecc_ptr;
++    uint32_t ecc_cfg;
++    struct ecc_state_s ecc[9];
++};
++
++static void omap_gpmc_int_update(struct omap_gpmc_s *s)
++{
++    qemu_set_irq(s->irq, s->irqen & s->irqst);
++}
++
++static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask)
++{
++    /* TODO: check for overlapping regions and report access errors */
++    if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
++                    (base < 0 || base >= 0x40) ||
++                    (base & 0x0f & ~mask)) {
++        fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
++                        __FUNCTION__);
++        return;
++    }
++
++    if (!f->opaque)
++        return;
++
++    f->base = base << 24;
++    f->size = (0x0fffffff & ~(mask << 24)) + 1;
++    /* TODO: rather than setting the size of the mapping (which should be
++     * constant), the mask should cause wrapping of the address space, so
++     * that the same memory becomes accessible at every <i>size</i> bytes
++     * starting from <i>base</i>.  */
++    if (f->iomemtype)
++        cpu_register_physical_memory(f->base, f->size, f->iomemtype);
++
++    if (f->base_update)
++        f->base_update(f->opaque, f->base);
++}
++
++static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f)
++{
++    if (f->size) {
++        if (f->unmap)
++            f->unmap(f->opaque);
++        if (f->iomemtype)
++            cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED);
++        f->base = 0;
++        f->size = 0;
++    }
++}
++
++static void omap_gpmc_reset(struct omap_gpmc_s *s)
++{
++    int i;
++
++    s->sysconfig = 0;
++    s->irqst = 0;
++    s->irqen = 0;
++    omap_gpmc_int_update(s);
++    s->timeout = 0;
++    s->config = 0xa00;
++    s->prefconfig[0] = 0x00004000;
++    s->prefconfig[1] = 0x00000000;
++    s->prefcontrol = 0;
++    s->preffifo = 0;
++    s->prefcount = 0;
++    for (i = 0; i < 8; i ++) {
++        if (s->cs_file[i].config[6] & (1 << 6))                       /* CSVALID */
++            omap_gpmc_cs_unmap(s->cs_file + i);
++        s->cs_file[i].config[0] = i ? 1 << 12 : 0;
++        s->cs_file[i].config[1] = 0x101001;
++        s->cs_file[i].config[2] = 0x020201;
++        s->cs_file[i].config[3] = 0x10031003;
++        s->cs_file[i].config[4] = 0x10f1111;
++        s->cs_file[i].config[5] = 0;
++        s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
++        if (s->cs_file[i].config[6] & (1 << 6))                       /* CSVALID */
++            omap_gpmc_cs_map(&s->cs_file[i],
++                            s->cs_file[i].config[6] & 0x1f,   /* MASKADDR */
++                        (s->cs_file[i].config[6] >> 8 & 0xf));        /* BASEADDR */
++    }
++    omap_gpmc_cs_map(s->cs_file, 0, 0xf);
++    s->ecc_cs = 0;
++    s->ecc_ptr = 0;
++    s->ecc_cfg = 0x3fcff000;
++    for (i = 0; i < 9; i ++)
++        ecc_reset(&s->ecc[i]);
++}
++
++static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
++    int offset = addr - s->base;
++    int cs;
++    struct omap_gpmc_cs_file_s *f;
++
++    switch (offset) {
++    case 0x000:       /* GPMC_REVISION */
++        return 0x20;
++
++    case 0x010:       /* GPMC_SYSCONFIG */
++        return s->sysconfig;
++
++    case 0x014:       /* GPMC_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x018:       /* GPMC_IRQSTATUS */
++        return s->irqst;
++
++    case 0x01c:       /* GPMC_IRQENABLE */
++        return s->irqen;
++
++    case 0x040:       /* GPMC_TIMEOUT_CONTROL */
++        return s->timeout;
++
++    case 0x044:       /* GPMC_ERR_ADDRESS */
++    case 0x048:       /* GPMC_ERR_TYPE */
++        return 0;
++
++    case 0x050:       /* GPMC_CONFIG */
++        return s->config;
++
++    case 0x054:       /* GPMC_STATUS */
++        return 0x001;
++
++    case 0x060 ... 0x1d4:
++        cs = (offset - 0x060) / 0x30;
++        offset -= cs * 0x30;
++        f = s->cs_file + cs;
++        switch (offset - cs * 0x30) {
++            case 0x60:        /* GPMC_CONFIG1 */
++                return f->config[0];
++            case 0x64:        /* GPMC_CONFIG2 */
++                return f->config[1];
++            case 0x68:        /* GPMC_CONFIG3 */
++                return f->config[2];
++            case 0x6c:        /* GPMC_CONFIG4 */
++                return f->config[3];
++            case 0x70:        /* GPMC_CONFIG5 */
++                return f->config[4];
++            case 0x74:        /* GPMC_CONFIG6 */
++                return f->config[5];
++            case 0x78:        /* GPMC_CONFIG7 */
++                return f->config[6];
++            case 0x84:        /* GPMC_NAND_DATA */
++                return 0;
++        }
++        break;
++
++    case 0x1e0:       /* GPMC_PREFETCH_CONFIG1 */
++        return s->prefconfig[0];
++    case 0x1e4:       /* GPMC_PREFETCH_CONFIG2 */
++        return s->prefconfig[1];
++    case 0x1ec:       /* GPMC_PREFETCH_CONTROL */
++        return s->prefcontrol;
++    case 0x1f0:       /* GPMC_PREFETCH_STATUS */
++        return (s->preffifo << 24) |
++                ((s->preffifo >
++                  ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
++                s->prefcount;
++
++    case 0x1f4:       /* GPMC_ECC_CONFIG */
++        return s->ecc_cs;
++    case 0x1f8:       /* GPMC_ECC_CONTROL */
++        return s->ecc_ptr;
++    case 0x1fc:       /* GPMC_ECC_SIZE_CONFIG */
++        return s->ecc_cfg;
++    case 0x200 ... 0x220:     /* GPMC_ECC_RESULT */
++        cs = (offset & 0x1f) >> 2;
++        /* TODO: check correctness */
++        return
++                ((s->ecc[cs].cp    &  0x07) <<  0) |
++                ((s->ecc[cs].cp    &  0x38) << 13) |
++                ((s->ecc[cs].lp[0] & 0x1ff) <<  3) |
++                ((s->ecc[cs].lp[1] & 0x1ff) << 19);
++
++    case 0x230:       /* GPMC_TESTMODE_CTRL */
++        return 0;
++    case 0x234:       /* GPMC_PSA_LSB */
++    case 0x238:       /* GPMC_PSA_MSB */
++        return 0x00000000;
++    }
++
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
++    int offset = addr - s->base;
++    int cs;
++    struct omap_gpmc_cs_file_s *f;
++
++    switch (offset) {
++    case 0x000:       /* GPMC_REVISION */
++    case 0x014:       /* GPMC_SYSSTATUS */
++    case 0x054:       /* GPMC_STATUS */
++    case 0x1f0:       /* GPMC_PREFETCH_STATUS */
++    case 0x200 ... 0x220:     /* GPMC_ECC_RESULT */
++    case 0x234:       /* GPMC_PSA_LSB */
++    case 0x238:       /* GPMC_PSA_MSB */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x010:       /* GPMC_SYSCONFIG */
++        if ((value >> 3) == 0x3)
++            fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
++                            __FUNCTION__, value >> 3);
++        if (value & 2)
++            omap_gpmc_reset(s);
++        s->sysconfig = value & 0x19;
++        break;
++
++    case 0x018:       /* GPMC_IRQSTATUS */
++        s->irqen = ~value;
++        omap_gpmc_int_update(s);
++        break;
++
++    case 0x01c:       /* GPMC_IRQENABLE */
++        s->irqen = value & 0xf03;
++        omap_gpmc_int_update(s);
++        break;
++
++    case 0x040:       /* GPMC_TIMEOUT_CONTROL */
++        s->timeout = value & 0x1ff1;
++        break;
++
++    case 0x044:       /* GPMC_ERR_ADDRESS */
++    case 0x048:       /* GPMC_ERR_TYPE */
++        break;
++
++    case 0x050:       /* GPMC_CONFIG */
++        s->config = value & 0xf13;
++        break;
++
++    case 0x060 ... 0x1d4:
++        cs = (offset - 0x060) / 0x30;
++        offset -= cs * 0x30;
++        f = s->cs_file + cs;
++        switch (offset - cs * 0x30) {
++            case 0x60:        /* GPMC_CONFIG1 */
++                f->config[0] = value & 0xffef3e13;
++                break;
++            case 0x64:        /* GPMC_CONFIG2 */
++                f->config[1] = value & 0x001f1f8f;
++                break;
++            case 0x68:        /* GPMC_CONFIG3 */
++                f->config[2] = value & 0x001f1f8f;
++                break;
++            case 0x6c:        /* GPMC_CONFIG4 */
++                f->config[3] = value & 0x1f8f1f8f;
++                break;
++            case 0x70:        /* GPMC_CONFIG5 */
++                f->config[4] = value & 0x0f1f1f1f;
++                break;
++            case 0x74:        /* GPMC_CONFIG6 */
++                f->config[5] = value & 0x00000fcf;
++                break;
++            case 0x78:        /* GPMC_CONFIG7 */
++                if ((f->config[6] ^ value) & 0xf7f) {
++                    if (f->config[6] & (1 << 6))              /* CSVALID */
++                        omap_gpmc_cs_unmap(f);
++                    if (value & (1 << 6))                     /* CSVALID */
++                        omap_gpmc_cs_map(f, value & 0x1f,     /* MASKADDR */
++                                        (value >> 8 & 0xf));  /* BASEADDR */
++                }
++                f->config[6] = value & 0x00000f7f;
++                break;
++            case 0x7c:        /* GPMC_NAND_COMMAND */
++            case 0x80:        /* GPMC_NAND_ADDRESS */
++            case 0x84:        /* GPMC_NAND_DATA */
++                break;
++
++            default:
++                goto bad_reg;
++        }
++        break;
++
++    case 0x1e0:       /* GPMC_PREFETCH_CONFIG1 */
++        s->prefconfig[0] = value & 0x7f8f7fbf;
++        /* TODO: update interrupts, fifos, dmas */
++        break;
++
++    case 0x1e4:       /* GPMC_PREFETCH_CONFIG2 */
++        s->prefconfig[1] = value & 0x3fff;
++        break;
++
++    case 0x1ec:       /* GPMC_PREFETCH_CONTROL */
++        s->prefcontrol = value & 1;
++        if (s->prefcontrol) {
++            if (s->prefconfig[0] & 1)
++                s->preffifo = 0x40;
++            else
++                s->preffifo = 0x00;
++        }
++        /* TODO: start */
++        break;
++
++    case 0x1f4:       /* GPMC_ECC_CONFIG */
++        s->ecc_cs = 0x8f;
++        break;
++    case 0x1f8:       /* GPMC_ECC_CONTROL */
++        if (value & (1 << 8))
++            for (cs = 0; cs < 9; cs ++)
++                ecc_reset(&s->ecc[cs]);
++        s->ecc_ptr = value & 0xf;
++        if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
++            s->ecc_ptr = 0;
++            s->ecc_cs &= ~1;
++        }
++        break;
++    case 0x1fc:       /* GPMC_ECC_SIZE_CONFIG */
++        s->ecc_cfg = value & 0x3fcff1ff;
++        break;
++    case 0x230:       /* GPMC_TESTMODE_CTRL */
++        if (value & 7)
++            fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
++        break;
++
++    default:
++    bad_reg:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++}
++
++static CPUReadMemoryFunc *omap_gpmc_readfn[] = {
++    omap_badwidth_read32,     /* TODO */
++    omap_badwidth_read32,     /* TODO */
++    omap_gpmc_read,
++};
++
++static CPUWriteMemoryFunc *omap_gpmc_writefn[] = {
++    omap_badwidth_write32,    /* TODO */
++    omap_badwidth_write32,    /* TODO */
++    omap_gpmc_write,
++};
++
++struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq)
++{
++    int iomemtype;
++    struct omap_gpmc_s *s = (struct omap_gpmc_s *)
++            qemu_mallocz(sizeof(struct omap_gpmc_s));
++
++    s->base = base;
++    omap_gpmc_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_gpmc_readfn,
++                    omap_gpmc_writefn, s);
++    cpu_register_physical_memory(s->base, 0x1000, iomemtype);
++
++    return s;
++}
++
++void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
++                void (*base_upd)(void *opaque, target_phys_addr_t new),
++                void (*unmap)(void *opaque), void *opaque)
++{
++    struct omap_gpmc_cs_file_s *f;
++
++    if (cs < 0 || cs >= 8) {
++        fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
++        exit(-1);
++    }
++    f = &s->cs_file[cs];
++
++    f->iomemtype = iomemtype;
++    f->base_update = base_upd;
++    f->unmap = unmap;
++    f->opaque = opaque;
++
++    if (f->config[6] & (1 << 6))                              /* CSVALID */
++        omap_gpmc_cs_map(f, f->config[6] & 0x1f,              /* MASKADDR */
++                        (f->config[6] >> 8 & 0xf));           /* BASEADDR */
++}
++
++/* General chip reset */
++static void omap2_mpu_reset(void *opaque)
++{
++    struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
++
++    omap_inth_reset(mpu->ih[0]);
++    omap_dma_reset(mpu->dma);
++    omap_prcm_reset(mpu->prcm);
++    omap_sysctl_reset(mpu->sysc);
++    omap_gp_timer_reset(mpu->gptimer[0]);
++    omap_gp_timer_reset(mpu->gptimer[1]);
++    omap_gp_timer_reset(mpu->gptimer[2]);
++    omap_gp_timer_reset(mpu->gptimer[3]);
++    omap_gp_timer_reset(mpu->gptimer[4]);
++    omap_gp_timer_reset(mpu->gptimer[5]);
++    omap_gp_timer_reset(mpu->gptimer[6]);
++    omap_gp_timer_reset(mpu->gptimer[7]);
++    omap_gp_timer_reset(mpu->gptimer[8]);
++    omap_gp_timer_reset(mpu->gptimer[9]);
++    omap_gp_timer_reset(mpu->gptimer[10]);
++    omap_gp_timer_reset(mpu->gptimer[11]);
++    omap_synctimer_reset(&mpu->synctimer);
++    omap_sdrc_reset(mpu->sdrc);
++    omap_gpmc_reset(mpu->gpmc);
++    omap_dss_reset(mpu->dss);
++#if 0
++    omap_wd_timer_reset(mpu->wdt);
++    omap_ulpd_pm_reset(mpu);
++    omap_pin_cfg_reset(mpu);
++    omap_mpui_reset(mpu);
++    omap_tipb_bridge_reset(mpu->private_tipb);
++    omap_tipb_bridge_reset(mpu->public_tipb);
++    omap_dpll_reset(&mpu->dpll[0]);
++    omap_dpll_reset(&mpu->dpll[1]);
++    omap_dpll_reset(&mpu->dpll[2]);
++#endif
++    omap_uart_reset(mpu->uart[0]);
++    omap_uart_reset(mpu->uart[1]);
++    omap_uart_reset(mpu->uart[2]);
++    omap_mmc_reset(mpu->mmc);
++    omap_gpif_reset(mpu->gpif);
++    omap_mcspi_reset(mpu->mcspi[0]);
++    omap_mcspi_reset(mpu->mcspi[1]);
++#if 0
++    omap_pwl_reset(mpu);
++    omap_pwt_reset(mpu);
++#endif
++    omap_i2c_reset(mpu->i2c[0]);
++    omap_i2c_reset(mpu->i2c[1]);
++#if 0
++    omap_rtc_reset(mpu->rtc);
++    omap_mcbsp_reset(mpu->mcbsp1);
++    omap_mcbsp_reset(mpu->mcbsp2);
++    omap_mcbsp_reset(mpu->mcbsp3);
++    omap_lpg_reset(mpu->led[0]);
++    omap_lpg_reset(mpu->led[1]);
++    omap_clkm_reset(mpu);
++#endif
++    cpu_reset(mpu->env);
++}
++
++static int omap2_validate_addr(struct omap_mpu_state_s *s,
++                target_phys_addr_t addr)
++{
++    return 1;
++}
++
++static const struct dma_irq_map omap2_dma_irq_map[] = {
++    { 0, OMAP_INT_24XX_SDMA_IRQ0 },
++    { 0, OMAP_INT_24XX_SDMA_IRQ1 },
++    { 0, OMAP_INT_24XX_SDMA_IRQ2 },
++    { 0, OMAP_INT_24XX_SDMA_IRQ3 },
++};
++
++struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
++                DisplayState *ds, const char *core)
++{
++    struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
++            qemu_mallocz(sizeof(struct omap_mpu_state_s));
++    ram_addr_t sram_base, q3_base;
++    qemu_irq *cpu_irq;
++    qemu_irq dma_irqs[4];
++    omap_clk gpio_clks[4];
++    int sdindex;
++    int i;
++
++    /* Core */
++    s->mpu_model = omap2420;
++    s->env = cpu_init(core ?: "arm1136-r2");
++    if (!s->env) {
++        fprintf(stderr, "Unable to find CPU definition\n");
++        exit(1);
++    }
++    s->sdram_size = sdram_size;
++    s->sram_size = OMAP242X_SRAM_SIZE;
++
++    s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
++
++    /* Clocks */
++    omap_clk_init(s);
++
++    /* Memory-mapped stuff */
++    cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size,
++                    (q3_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM);
++    cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size,
++                    (sram_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM);
++
++    s->l4 = omap_l4_init(OMAP2_L4_BASE, 54);
++
++    /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */
++    cpu_irq = arm_pic_init_cpu(s->env);
++    s->ih[0] = omap2_inth_init(0x480fe000, 0x1000, 3, &s->irq[0],
++                    cpu_irq[ARM_PIC_CPU_IRQ], cpu_irq[ARM_PIC_CPU_FIQ],
++                    omap_findclk(s, "mpu_intc_fclk"),
++                    omap_findclk(s, "mpu_intc_iclk"));
++
++    s->prcm = omap_prcm_init(omap_l4tao(s->l4, 3),
++                    s->irq[0][OMAP_INT_24XX_PRCM_MPU_IRQ], NULL, NULL, s);
++
++    s->sysc = omap_sysctl_init(omap_l4tao(s->l4, 1),
++                    omap_findclk(s, "omapctrl_iclk"), s);
++
++    for (i = 0; i < 4; i ++)
++        dma_irqs[i] =
++                s->irq[omap2_dma_irq_map[i].ih][omap2_dma_irq_map[i].intr];
++    s->dma = omap_dma4_init(0x48056000, dma_irqs, s, 256, 32,
++                    omap_findclk(s, "sdma_iclk"),
++                    omap_findclk(s, "sdma_fclk"));
++    s->port->addr_valid = omap2_validate_addr;
++
++    s->uart[0] = omap2_uart_init(omap_l4ta(s->l4, 19),
++                    s->irq[0][OMAP_INT_24XX_UART1_IRQ],
++                    omap_findclk(s, "uart1_fclk"),
++                    omap_findclk(s, "uart1_iclk"),
++                    s->drq[OMAP24XX_DMA_UART1_TX],
++                    s->drq[OMAP24XX_DMA_UART1_RX], serial_hds[0]);
++    s->uart[1] = omap2_uart_init(omap_l4ta(s->l4, 20),
++                    s->irq[0][OMAP_INT_24XX_UART2_IRQ],
++                    omap_findclk(s, "uart2_fclk"),
++                    omap_findclk(s, "uart2_iclk"),
++                    s->drq[OMAP24XX_DMA_UART2_TX],
++                    s->drq[OMAP24XX_DMA_UART2_RX],
++                    serial_hds[0] ? serial_hds[1] : 0);
++    s->uart[2] = omap2_uart_init(omap_l4ta(s->l4, 21),
++                    s->irq[0][OMAP_INT_24XX_UART3_IRQ],
++                    omap_findclk(s, "uart3_fclk"),
++                    omap_findclk(s, "uart3_iclk"),
++                    s->drq[OMAP24XX_DMA_UART3_TX],
++                    s->drq[OMAP24XX_DMA_UART3_RX],
++                    serial_hds[0] && serial_hds[1] ? serial_hds[2] : 0);
++
++    s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER1],
++                    omap_findclk(s, "wu_gpt1_clk"),
++                    omap_findclk(s, "wu_l4_iclk"));
++    s->gptimer[1] = omap_gp_timer_init(omap_l4ta(s->l4, 8),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER2],
++                    omap_findclk(s, "core_gpt2_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[2] = omap_gp_timer_init(omap_l4ta(s->l4, 22),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER3],
++                    omap_findclk(s, "core_gpt3_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[3] = omap_gp_timer_init(omap_l4ta(s->l4, 23),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER4],
++                    omap_findclk(s, "core_gpt4_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[4] = omap_gp_timer_init(omap_l4ta(s->l4, 24),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER5],
++                    omap_findclk(s, "core_gpt5_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[5] = omap_gp_timer_init(omap_l4ta(s->l4, 25),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER6],
++                    omap_findclk(s, "core_gpt6_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[6] = omap_gp_timer_init(omap_l4ta(s->l4, 26),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER7],
++                    omap_findclk(s, "core_gpt7_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[7] = omap_gp_timer_init(omap_l4ta(s->l4, 27),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER8],
++                    omap_findclk(s, "core_gpt8_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[8] = omap_gp_timer_init(omap_l4ta(s->l4, 28),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER9],
++                    omap_findclk(s, "core_gpt9_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[9] = omap_gp_timer_init(omap_l4ta(s->l4, 29),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER10],
++                    omap_findclk(s, "core_gpt10_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[10] = omap_gp_timer_init(omap_l4ta(s->l4, 30),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER11],
++                    omap_findclk(s, "core_gpt11_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++    s->gptimer[11] = omap_gp_timer_init(omap_l4ta(s->l4, 31),
++                    s->irq[0][OMAP_INT_24XX_GPTIMER12],
++                    omap_findclk(s, "core_gpt12_clk"),
++                    omap_findclk(s, "core_l4_iclk"));
++
++    omap_tap_init(omap_l4ta(s->l4, 2), s);
++
++    omap_synctimer_init(omap_l4tao(s->l4, 2), s,
++                    omap_findclk(s, "clk32-kHz"),
++                    omap_findclk(s, "core_l4_iclk"));
++
++    s->i2c[0] = omap2_i2c_init(omap_l4tao(s->l4, 5),
++                    s->irq[0][OMAP_INT_24XX_I2C1_IRQ],
++                    &s->drq[OMAP24XX_DMA_I2C1_TX],
++                    omap_findclk(s, "i2c1.fclk"),
++                    omap_findclk(s, "i2c1.iclk"));
++    s->i2c[1] = omap2_i2c_init(omap_l4tao(s->l4, 6),
++                    s->irq[0][OMAP_INT_24XX_I2C2_IRQ],
++                    &s->drq[OMAP24XX_DMA_I2C2_TX],
++                    omap_findclk(s, "i2c2.fclk"),
++                    omap_findclk(s, "i2c2.iclk"));
++
++    gpio_clks[0] = omap_findclk(s, "gpio1_dbclk");
++    gpio_clks[1] = omap_findclk(s, "gpio2_dbclk");
++    gpio_clks[2] = omap_findclk(s, "gpio3_dbclk");
++    gpio_clks[3] = omap_findclk(s, "gpio4_dbclk");
++    s->gpif = omap2_gpio_init(omap_l4ta(s->l4, 3),
++                    &s->irq[0][OMAP_INT_24XX_GPIO_BANK1],
++                    gpio_clks, omap_findclk(s, "gpio_iclk"), 4);
++
++    s->sdrc = omap_sdrc_init(0x68009000);
++    s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]);
++
++    sdindex = drive_get_index(IF_SD, 0, 0);
++    if (sdindex == -1) {
++        fprintf(stderr, "qemu: missing SecureDigital device\n");
++        exit(1);
++    }
++    s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), drives_table[sdindex].bdrv,
++                    s->irq[0][OMAP_INT_24XX_MMC_IRQ],
++                    &s->drq[OMAP24XX_DMA_MMC1_TX],
++                    omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk"));
++
++    s->mcspi[0] = omap_mcspi_init(omap_l4ta(s->l4, 35), 4,
++                    s->irq[0][OMAP_INT_24XX_MCSPI1_IRQ], 
++                    &s->drq[OMAP24XX_DMA_SPI1_TX0],
++                    omap_findclk(s, "spi1_fclk"),
++                    omap_findclk(s, "spi1_iclk"));
++    s->mcspi[1] = omap_mcspi_init(omap_l4ta(s->l4, 36), 2,
++                    s->irq[0][OMAP_INT_24XX_MCSPI2_IRQ], 
++                    &s->drq[OMAP24XX_DMA_SPI2_TX0],
++                    omap_findclk(s, "spi2_fclk"),
++                    omap_findclk(s, "spi2_iclk"));
++
++    s->dss = omap_dss_init(omap_l4ta(s->l4, 10), 0x68000800, ds,
++                    /* XXX wire M_IRQ_25, D_L2_IRQ_30 and I_IRQ_13 together */
++                    s->irq[0][OMAP_INT_24XX_DSS_IRQ], s->drq[OMAP24XX_DMA_DSS],
++                    omap_findclk(s, "dss_clk1"), omap_findclk(s, "dss_clk2"),
++                    omap_findclk(s, "dss_54m_clk"),
++                    omap_findclk(s, "dss_l3_iclk"),
++                    omap_findclk(s, "dss_l4_iclk"));
++
++    /* Register mappings not currenlty implemented:
++     * SystemControlMod       48000000 - 48000fff
++     * SystemControlL4        48001000 - 48001fff
++     * 32kHz Timer Mod        48004000 - 48004fff
++     * 32kHz Timer L4 48005000 - 48005fff
++     * PRCM ModA      48008000 - 480087ff
++     * PRCM ModB      48008800 - 48008fff
++     * PRCM L4                48009000 - 48009fff
++     * TEST-BCM Mod   48012000 - 48012fff
++     * TEST-BCM L4    48013000 - 48013fff
++     * TEST-TAP Mod   48014000 - 48014fff
++     * TEST-TAP L4    48015000 - 48015fff
++     * GPIO1 Mod      48018000 - 48018fff
++     * GPIO Top               48019000 - 48019fff
++     * GPIO2 Mod      4801a000 - 4801afff
++     * GPIO L4                4801b000 - 4801bfff
++     * GPIO3 Mod      4801c000 - 4801cfff
++     * GPIO4 Mod      4801e000 - 4801efff
++     * WDTIMER1 Mod   48020000 - 48010fff
++     * WDTIMER Top    48021000 - 48011fff
++     * WDTIMER2 Mod   48022000 - 48012fff
++     * WDTIMER L4     48023000 - 48013fff
++     * WDTIMER3 Mod   48024000 - 48014fff
++     * WDTIMER3 L4    48025000 - 48015fff
++     * WDTIMER4 Mod   48026000 - 48016fff
++     * WDTIMER4 L4    48027000 - 48017fff
++     * GPTIMER1 Mod   48028000 - 48018fff
++     * GPTIMER1 L4    48029000 - 48019fff
++     * GPTIMER2 Mod   4802a000 - 4801afff
++     * GPTIMER2 L4    4802b000 - 4801bfff
++     * L4-Config AP   48040000 - 480407ff
++     * L4-Config IP   48040800 - 48040fff
++     * L4-Config LA   48041000 - 48041fff
++     * ARM11ETB Mod   48048000 - 48049fff
++     * ARM11ETB L4    4804a000 - 4804afff
++     * DISPLAY Top    48050000 - 480503ff
++     * DISPLAY DISPC  48050400 - 480507ff
++     * DISPLAY RFBI   48050800 - 48050bff
++     * DISPLAY VENC   48050c00 - 48050fff
++     * DISPLAY L4     48051000 - 48051fff
++     * CAMERA Top     48052000 - 480523ff
++     * CAMERA core    48052400 - 480527ff
++     * CAMERA DMA     48052800 - 48052bff
++     * CAMERA MMU     48052c00 - 48052fff
++     * CAMERA L4      48053000 - 48053fff
++     * SDMA Mod               48056000 - 48056fff
++     * SDMA L4                48057000 - 48057fff
++     * SSI Top                48058000 - 48058fff
++     * SSI GDD                48059000 - 48059fff
++     * SSI Port1      4805a000 - 4805afff
++     * SSI Port2      4805b000 - 4805bfff
++     * SSI L4         4805c000 - 4805cfff
++     * USB Mod                4805e000 - 480fefff
++     * USB L4         4805f000 - 480fffff
++     * WIN_TRACER1 Mod        48060000 - 48060fff
++     * WIN_TRACER1 L4 48061000 - 48061fff
++     * WIN_TRACER2 Mod        48062000 - 48062fff
++     * WIN_TRACER2 L4 48063000 - 48063fff
++     * WIN_TRACER3 Mod        48064000 - 48064fff
++     * WIN_TRACER3 L4 48065000 - 48065fff
++     * WIN_TRACER4 Top        48066000 - 480660ff
++     * WIN_TRACER4 ETT        48066100 - 480661ff
++     * WIN_TRACER4 WT 48066200 - 480662ff
++     * WIN_TRACER4 L4 48067000 - 48067fff
++     * XTI Mod                48068000 - 48068fff
++     * XTI L4         48069000 - 48069fff
++     * UART1 Mod      4806a000 - 4806afff
++     * UART1 L4               4806b000 - 4806bfff
++     * UART2 Mod      4806c000 - 4806cfff
++     * UART2 L4               4806d000 - 4806dfff
++     * UART3 Mod      4806e000 - 4806efff
++     * UART3 L4               4806f000 - 4806ffff
++     * I2C1 Mod               48070000 - 48070fff
++     * I2C1 L4                48071000 - 48071fff
++     * I2C2 Mod               48072000 - 48072fff
++     * I2C2 L4                48073000 - 48073fff
++     * McBSP1 Mod     48074000 - 48074fff
++     * McBSP1 L4      48075000 - 48075fff
++     * McBSP2 Mod     48076000 - 48076fff
++     * McBSP2 L4      48077000 - 48077fff
++     * GPTIMER3 Mod   48078000 - 48078fff
++     * GPTIMER3 L4    48079000 - 48079fff
++     * GPTIMER4 Mod   4807a000 - 4807afff
++     * GPTIMER4 L4    4807b000 - 4807bfff
++     * GPTIMER5 Mod   4807c000 - 4807cfff
++     * GPTIMER5 L4    4807d000 - 4807dfff
++     * GPTIMER6 Mod   4807e000 - 4807efff
++     * GPTIMER6 L4    4807f000 - 4807ffff
++     * GPTIMER7 Mod   48080000 - 48080fff
++     * GPTIMER7 L4    48081000 - 48081fff
++     * GPTIMER8 Mod   48082000 - 48082fff
++     * GPTIMER8 L4    48083000 - 48083fff
++     * GPTIMER9 Mod   48084000 - 48084fff
++     * GPTIMER9 L4    48085000 - 48085fff
++     * GPTIMER10 Mod  48086000 - 48086fff
++     * GPTIMER10 L4   48087000 - 48087fff
++     * GPTIMER11 Mod  48088000 - 48088fff
++     * GPTIMER11 L4   48089000 - 48089fff
++     * GPTIMER12 Mod  4808a000 - 4808afff
++     * GPTIMER12 L4   4808b000 - 4808bfff
++     * EAC Mod                48090000 - 48090fff
++     * EAC L4         48091000 - 48091fff
++     * FAC Mod                48092000 - 48092fff
++     * FAC L4         48093000 - 48093fff
++     * MAILBOX Mod    48094000 - 48094fff
++     * MAILBOX L4     48095000 - 48095fff
++     * SPI1 Mod               48098000 - 48098fff
++     * SPI1 L4                48099000 - 48099fff
++     * SPI2 Mod               4809a000 - 4809afff
++     * SPI2 L4                4809b000 - 4809bfff
++     * MMC/SDIO Mod   4809c000 - 4809cfff
++     * MMC/SDIO L4    4809d000 - 4809dfff
++     * MS_PRO Mod     4809e000 - 4809efff
++     * MS_PRO L4      4809f000 - 4809ffff
++     * RNG Mod                480a0000 - 480a0fff
++     * RNG L4         480a1000 - 480a1fff
++     * DES3DES Mod    480a2000 - 480a2fff
++     * DES3DES L4     480a3000 - 480a3fff
++     * SHA1MD5 Mod    480a4000 - 480a4fff
++     * SHA1MD5 L4     480a5000 - 480a5fff
++     * AES Mod                480a6000 - 480a6fff
++     * AES L4         480a7000 - 480a7fff
++     * PKA Mod                480a8000 - 480a9fff
++     * PKA L4         480aa000 - 480aafff
++     * MG Mod         480b0000 - 480b0fff
++     * MG L4          480b1000 - 480b1fff
++     * HDQ/1-wire Mod 480b2000 - 480b2fff
++     * HDQ/1-wire L4  480b3000 - 480b3fff
++     * MPU interrupt  480fe000 - 480fefff
++     * IVA RAM                5c000000 - 5c01ffff
++     * IVA ROM                5c020000 - 5c027fff
++     * IMG_BUF_A      5c040000 - 5c040fff
++     * IMG_BUF_B      5c042000 - 5c042fff
++     * VLCDS          5c048000 - 5c0487ff
++     * IMX_COEF               5c049000 - 5c04afff
++     * IMX_CMD                5c051000 - 5c051fff
++     * VLCDQ          5c053000 - 5c0533ff
++     * VLCDH          5c054000 - 5c054fff
++     * SEQ_CMD                5c055000 - 5c055fff
++     * IMX_REG                5c056000 - 5c0560ff
++     * VLCD_REG               5c056100 - 5c0561ff
++     * SEQ_REG                5c056200 - 5c0562ff
++     * IMG_BUF_REG    5c056300 - 5c0563ff
++     * SEQIRQ_REG     5c056400 - 5c0564ff
++     * OCP_REG                5c060000 - 5c060fff
++     * SYSC_REG               5c070000 - 5c070fff
++     * MMU_REG                5d000000 - 5d000fff
++     * sDMA R         68000400 - 680005ff
++     * sDMA W         68000600 - 680007ff
++     * Display Control        68000800 - 680009ff
++     * DSP subsystem  68000a00 - 68000bff
++     * MPU subsystem  68000c00 - 68000dff
++     * IVA subsystem  68001000 - 680011ff
++     * USB            68001200 - 680013ff
++     * Camera         68001400 - 680015ff
++     * VLYNQ (firewall)       68001800 - 68001bff
++     * VLYNQ          68001e00 - 68001fff
++     * SSI            68002000 - 680021ff
++     * L4             68002400 - 680025ff
++     * DSP (firewall) 68002800 - 68002bff
++     * DSP subsystem  68002e00 - 68002fff
++     * IVA (firewall) 68003000 - 680033ff
++     * IVA            68003600 - 680037ff
++     * GFX            68003a00 - 68003bff
++     * CMDWR emulation        68003c00 - 68003dff
++     * SMS            68004000 - 680041ff
++     * OCM            68004200 - 680043ff
++     * GPMC           68004400 - 680045ff
++     * RAM (firewall) 68005000 - 680053ff
++     * RAM (err login)        68005400 - 680057ff
++     * ROM (firewall) 68005800 - 68005bff
++     * ROM (err login)        68005c00 - 68005fff
++     * GPMC (firewall)        68006000 - 680063ff
++     * GPMC (err login)       68006400 - 680067ff
++     * SMS (err login)        68006c00 - 68006fff
++     * SMS registers  68008000 - 68008fff
++     * SDRC registers 68009000 - 68009fff
++     * GPMC registers 6800a000   6800afff
++     */
++
++    qemu_register_reset(omap2_mpu_reset, s);
++
++    return s;
++}
+diff --git a/hw/omap_clk.c b/hw/omap_clk.c
+index 37daec2..da03e15 100644
+--- a/hw/omap_clk.c
++++ b/hw/omap_clk.c
+@@ -34,6 +34,9 @@ struct clk {
+ #define CLOCK_IN_OMAP730      (1 << 11)
+ #define CLOCK_IN_OMAP1510     (1 << 12)
+ #define CLOCK_IN_OMAP16XX     (1 << 13)
++#define CLOCK_IN_OMAP242X     (1 << 14)
++#define CLOCK_IN_OMAP243X     (1 << 15)
++#define CLOCK_IN_OMAP343X     (1 << 16)
+     uint32_t flags;
+     int id;
+@@ -55,7 +58,8 @@ static struct clk xtal_osc12m = {
+ static struct clk xtal_osc32k = {
+     .name     = "xtal_osc_32k",
+     .rate     = 32768,
+-    .flags    = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
++    .flags    = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
++            CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ };
+ static struct clk ck_ref = {
+@@ -502,11 +506,441 @@ static struct clk i2c_ick = {
+ static struct clk clk32k = {
+     .name     = "clk32-kHz",
+     .flags    = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+-            ALWAYS_ENABLED,
+-    .parent     = &xtal_osc32k,
++            CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .parent   = &xtal_osc32k,
++};
++
++static struct clk apll_96m = {
++    .name     = "apll_96m",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .rate     = 96000000,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk apll_54m = {
++    .name     = "apll_54m",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .rate     = 54000000,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk sys_clk = {
++    .name     = "sys_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .rate     = 32768,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk sleep_clk = {
++    .name     = "sleep_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .rate     = 32768,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk dpll_ck = {
++    .name     = "dpll",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk dpll_x2_ck = {
++    .name     = "dpll_x2",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk wdt1_sys_clk = {
++    .name     = "wdt1_sys_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
++    .rate     = 32768,
++    /*.parent = sys.xtalin */
++};
++
++static struct clk func_96m_clk = {
++    .name     = "func_96m_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .divisor  = 1,
++    .parent   = &apll_96m,
++};
++
++static struct clk func_48m_clk = {
++    .name     = "func_48m_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .divisor  = 2,
++    .parent   = &apll_96m,
++};
++
++static struct clk func_12m_clk = {
++    .name     = "func_12m_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .divisor  = 8,
++    .parent   = &apll_96m,
++};
++
++static struct clk func_54m_clk = {
++    .name     = "func_54m_clk",
++    .flags    = CLOCK_IN_OMAP242X,
++    .divisor  = 1,
++    .parent   = &apll_54m,
++};
++
++static struct clk sys_clkout = {
++    .name     = "clkout",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk sys_clkout2 = {
++    .name     = "clkout2",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_clk = {
++    .name     = "core_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &dpll_ck,
++};
++
++static struct clk l3_clk = {
++    .name     = "l3_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk core_l4_iclk = {
++    .name     = "core_l4_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &l3_clk,
++};
++
++static struct clk wu_l4_iclk = {
++    .name     = "wu_l4_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &l3_clk,
++};
++
++static struct clk core_l3_iclk = {
++    .name     = "core_l3_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk core_l4_usb_clk = {
++    .name     = "core_l4_usb_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &l3_clk,
++};
++
++static struct clk wu_gpt1_clk = {
++    .name     = "wu_gpt1_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk wu_32k_clk = {
++    .name     = "wu_32k_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk uart1_fclk = {
++    .name     = "uart1_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_48m_clk,
++};
++
++static struct clk uart1_iclk = {
++    .name     = "uart1_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk uart2_fclk = {
++    .name     = "uart2_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_48m_clk,
++};
++
++static struct clk uart2_iclk = {
++    .name     = "uart2_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk uart3_fclk = {
++    .name     = "uart3_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_48m_clk,
++};
++
++static struct clk uart3_iclk = {
++    .name     = "uart3_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk mpu_fclk = {
++    .name     = "mpu_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk mpu_iclk = {
++    .name     = "mpu_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk int_m_fclk = {
++    .name     = "int_m_fclk",
++    .alias    = "mpu_intc_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk int_m_iclk = {
++    .name     = "int_m_iclk",
++    .alias    = "mpu_intc_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_clk,
++};
++
++static struct clk core_gpt2_clk = {
++    .name     = "core_gpt2_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt3_clk = {
++    .name     = "core_gpt3_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt4_clk = {
++    .name     = "core_gpt4_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt5_clk = {
++    .name     = "core_gpt5_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt6_clk = {
++    .name     = "core_gpt6_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt7_clk = {
++    .name     = "core_gpt7_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt8_clk = {
++    .name     = "core_gpt8_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt9_clk = {
++    .name     = "core_gpt9_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt10_clk = {
++    .name     = "core_gpt10_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt11_clk = {
++    .name     = "core_gpt11_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk core_gpt12_clk = {
++    .name     = "core_gpt12_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &sys_clk,
++};
++
++static struct clk mcbsp1_clk = {
++    .name     = "mcbsp1_cg",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .divisor  = 2,
++    .parent   = &func_96m_clk,
++};
++
++static struct clk mcbsp2_clk = {
++    .name     = "mcbsp2_cg",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .divisor  = 2,
++    .parent   = &func_96m_clk,
++};
++
++static struct clk emul_clk = {
++    .name     = "emul_ck",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_54m_clk,
++};
++
++static struct clk sdma_fclk = {
++    .name     = "sdma_fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &l3_clk,
++};
++
++static struct clk sdma_iclk = {
++    .name     = "sdma_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l3_iclk, /* core_l4_iclk for the configuration port */
++};
++
++static struct clk i2c1_fclk = {
++    .name     = "i2c1.fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_12m_clk,
++    .divisor  = 1,
++};
++
++static struct clk i2c1_iclk = {
++    .name     = "i2c1.iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk i2c2_fclk = {
++    .name     = "i2c2.fclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_12m_clk,
++    .divisor  = 1,
++};
++
++static struct clk i2c2_iclk = {
++    .name     = "i2c2.iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk gpio_dbclk[4] = {
++    {
++        .name = "gpio1_dbclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &wu_32k_clk,
++    }, {
++        .name = "gpio2_dbclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &wu_32k_clk,
++    }, {
++        .name = "gpio3_dbclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &wu_32k_clk,
++    }, {
++        .name = "gpio4_dbclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &wu_32k_clk,
++    },
++};
++
++static struct clk gpio_iclk = {
++    .name     = "gpio_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &wu_l4_iclk,
++};
++
++static struct clk mmc_fck = {
++    .name     = "mmc_fclk",
++    .flags    = CLOCK_IN_OMAP242X,
++    .parent   = &func_96m_clk,
++};
++
++static struct clk mmc_ick = {
++    .name     = "mmc_iclk",
++    .flags    = CLOCK_IN_OMAP242X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk spi_fclk[3] = {
++    {
++        .name = "spi1_fclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &func_48m_clk,
++    }, {
++        .name = "spi2_fclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &func_48m_clk,
++    }, {
++        .name = "spi3_fclk",
++        .flags        = CLOCK_IN_OMAP243X,
++        .parent       = &func_48m_clk,
++    },
++};
++
++static struct clk dss_clk[2] = {
++    {
++        .name = "dss_clk1",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &core_clk,
++    }, {
++        .name = "dss_clk2",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &sys_clk,
++    },
++};
++
++static struct clk dss_54m_clk = {
++    .name     = "dss_54m_clk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &func_54m_clk,
++};
++
++static struct clk dss_l3_iclk = {
++    .name     = "dss_l3_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l3_iclk,
++};
++
++static struct clk dss_l4_iclk = {
++    .name     = "dss_l4_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    .parent   = &core_l4_iclk,
++};
++
++static struct clk spi_iclk[3] = {
++    {
++        .name = "spi1_iclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &core_l4_iclk,
++    }, {
++        .name = "spi2_iclk",
++        .flags        = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++        .parent       = &core_l4_iclk,
++    }, {
++        .name = "spi3_iclk",
++        .flags        = CLOCK_IN_OMAP243X,
++        .parent       = &core_l4_iclk,
++    },
++};
++
++static struct clk omapctrl_clk = {
++    .name     = "omapctrl_iclk",
++    .flags    = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
++    /* XXX Should be in WKUP domain */
++    .parent   = &core_l4_iclk,
+ };
+ static struct clk *onchip_clks[] = {
++    /* OMAP 1 */
++
+     /* non-ULPD clocks */
+     &xtal_osc12m,
+     &xtal_osc32k,
+@@ -572,6 +1006,80 @@ static struct clk *onchip_clks[] = {
+     /* Virtual clocks */
+     &i2c_fck,
+     &i2c_ick,
++
++    /* OMAP 2 */
++
++    &apll_96m,
++    &apll_54m,
++    &sys_clk,
++    &sleep_clk,
++    &dpll_ck,
++    &dpll_x2_ck,
++    &wdt1_sys_clk,
++    &func_96m_clk,
++    &func_48m_clk,
++    &func_12m_clk,
++    &func_54m_clk,
++    &sys_clkout,
++    &sys_clkout2,
++    &core_clk,
++    &l3_clk,
++    &core_l4_iclk,
++    &wu_l4_iclk,
++    &core_l3_iclk,
++    &core_l4_usb_clk,
++    &wu_gpt1_clk,
++    &wu_32k_clk,
++    &uart1_fclk,
++    &uart1_iclk,
++    &uart2_fclk,
++    &uart2_iclk,
++    &uart3_fclk,
++    &uart3_iclk,
++    &mpu_fclk,
++    &mpu_iclk,
++    &int_m_fclk,
++    &int_m_iclk,
++    &core_gpt2_clk,
++    &core_gpt3_clk,
++    &core_gpt4_clk,
++    &core_gpt5_clk,
++    &core_gpt6_clk,
++    &core_gpt7_clk,
++    &core_gpt8_clk,
++    &core_gpt9_clk,
++    &core_gpt10_clk,
++    &core_gpt11_clk,
++    &core_gpt12_clk,
++    &mcbsp1_clk,
++    &mcbsp2_clk,
++    &emul_clk,
++    &sdma_fclk,
++    &sdma_iclk,
++    &i2c1_fclk,
++    &i2c1_iclk,
++    &i2c2_fclk,
++    &i2c2_iclk,
++    &gpio_dbclk[0],
++    &gpio_dbclk[1],
++    &gpio_dbclk[2],
++    &gpio_dbclk[3],
++    &gpio_iclk,
++    &mmc_fck,
++    &mmc_ick,
++    &spi_fclk[0],
++    &spi_iclk[0],
++    &spi_fclk[1],
++    &spi_iclk[1],
++    &spi_fclk[2],
++    &spi_iclk[2],
++    &dss_clk[0],
++    &dss_clk[1],
++    &dss_54m_clk,
++    &dss_l3_iclk,
++    &dss_l4_iclk,
++    &omapctrl_clk,
++
+     0
+ };
+@@ -727,6 +1235,12 @@ void omap_clk_init(struct omap_mpu_state_s *mpu)
+         flag = CLOCK_IN_OMAP310;
+     else if (cpu_is_omap1510(mpu))
+         flag = CLOCK_IN_OMAP1510;
++    else if (cpu_is_omap2410(mpu) || cpu_is_omap2420(mpu))
++        flag = CLOCK_IN_OMAP242X;
++    else if (cpu_is_omap2430(mpu))
++        flag = CLOCK_IN_OMAP243X;
++    else if (cpu_is_omap3430(mpu))
++        flag = CLOCK_IN_OMAP243X;
+     else
+         return;
+diff --git a/hw/omap_dma.c b/hw/omap_dma.c
+index 1835826..6c0bd82 100644
+--- a/hw/omap_dma.c
++++ b/hw/omap_dma.c
+@@ -28,12 +28,15 @@ struct omap_dma_channel_s {
+     /* transfer data */
+     int burst[2];
+     int pack[2];
++    int endian[2];
++    int endian_lock[2];
++    int translate[2];
+     enum omap_dma_port port[2];
+     target_phys_addr_t addr[2];
+     omap_dma_addressing_t mode[2];
+-    uint16_t elements;
++    uint32_t elements;
+     uint16_t frames;
+-    int16_t frame_index[2];
++    int32_t frame_index[2];
+     int16_t element_index[2];
+     int data_type;
+@@ -41,6 +44,7 @@ struct omap_dma_channel_s {
+     int transparent_copy;
+     int constant_fill;
+     uint32_t color;
++    int prefetch;
+     /* auto init and linked channel data */
+     int end_prog;
+@@ -52,11 +56,13 @@ struct omap_dma_channel_s {
+     /* interruption data */
+     int interrupts;
+     int status;
++    int cstatus;
+     /* state data */
+     int active;
+     int enable;
+     int sync;
++    int src_sync;
+     int pending_request;
+     int waiting_end_prog;
+     uint16_t cpc;
+@@ -75,16 +81,21 @@ struct omap_dma_channel_s {
+         target_phys_addr_t src, dest;
+         int frame;
+         int element;
++        int pck_element;
+         int frame_delta[2];
+         int elem_delta[2];
+         int frames;
+         int elements;
++        int pck_elements;
+     } active_set;
+     /* unused parameters */
++    int write_mode;
+     int priority;
+     int interleave_disabled;
+     int type;
++    int suspend;
++    int buf_disable;
+ };
+ struct omap_dma_s {
+@@ -93,15 +104,21 @@ struct omap_dma_s {
+     target_phys_addr_t base;
+     omap_clk clk;
+     int64_t delay;
+-    uint32_t drq;
++    uint64_t drq;
++    qemu_irq irq[4];
++    void (*intr_update)(struct omap_dma_s *s);
+     enum omap_dma_model model;
+     int omap_3_1_mapping_disabled;
+-    uint16_t gcr;
++    uint32_t gcr;
++    uint32_t ocp;
++    uint32_t caps[5];
++    uint32_t irqen[4];
++    uint32_t irqstat[4];
+     int run_count;
+     int chans;
+-    struct omap_dma_channel_s ch[16];
++    struct omap_dma_channel_s ch[32];
+     struct omap_dma_lcd_channel_s lcd_ch;
+ };
+@@ -113,23 +130,13 @@ struct omap_dma_s {
+ #define LAST_FRAME_INTR (1 << 4)
+ #define END_BLOCK_INTR  (1 << 5)
+ #define SYNC            (1 << 6)
++#define END_PKT_INTR  (1 << 7)
++#define TRANS_ERR_INTR        (1 << 8)
++#define MISALIGN_INTR (1 << 11)
+-static void omap_dma_interrupts_update(struct omap_dma_s *s)
++static inline void omap_dma_interrupts_update(struct omap_dma_s *s)
+ {
+-    struct omap_dma_channel_s *ch = s->ch;
+-    int i;
+-
+-    if (s->omap_3_1_mapping_disabled) {
+-        for (i = 0; i < s->chans; i ++, ch ++)
+-            if (ch->status)
+-                qemu_irq_raise(ch->irq);
+-    } else {
+-        /* First three interrupts are shared between two channels each. */
+-        for (i = 0; i < 6; i ++, ch ++) {
+-            if (ch->status || (ch->sibling && ch->sibling->status))
+-                qemu_irq_raise(ch->irq);
+-        }
+-    }
++    return s->intr_update(s);
+ }
+ static void omap_dma_channel_load(struct omap_dma_s *s,
+@@ -148,8 +155,10 @@ static void omap_dma_channel_load(struct omap_dma_s *s,
+     a->dest = ch->addr[1];
+     a->frames = ch->frames;
+     a->elements = ch->elements;
++    a->pck_elements = ch->frame_index[!ch->src_sync];
+     a->frame = 0;
+     a->element = 0;
++    a->pck_element = 0;
+     if (unlikely(!ch->elements || !ch->frames)) {
+         printf("%s: bad DMA request\n", __FUNCTION__);
+@@ -202,16 +211,15 @@ static void omap_dma_deactivate_channel(struct omap_dma_s *s,
+     /* Update cpc */
+     ch->cpc = ch->active_set.dest & 0xffff;
+-    if (ch->pending_request && !ch->waiting_end_prog) {
++    if (ch->pending_request && !ch->waiting_end_prog && ch->enable) {
+         /* Don't deactivate the channel */
+         ch->pending_request = 0;
+-        if (ch->enable)
+-            return;
++        return;
+     }
+     /* Don't deactive the channel if it is synchronized and the DMA request is
+        active */
+-    if (ch->sync && (s->drq & (1 << ch->sync)) && ch->enable)
++    if (ch->sync && ch->enable && (s->drq & (1 << ch->sync)))
+         return;
+     if (ch->active) {
+@@ -231,6 +239,9 @@ static void omap_dma_enable_channel(struct omap_dma_s *s,
+         ch->enable = 1;
+         ch->waiting_end_prog = 0;
+         omap_dma_channel_load(s, ch);
++        /* TODO: theoretically if ch->sync && ch->prefetch &&
++         * !s->drq[ch->sync], we should also activate and fetch from source
++         * and then stall until signalled.  */
+         if ((!ch->sync) || (s->drq & (1 << ch->sync)))
+             omap_dma_activate_channel(s, ch);
+     }
+@@ -259,16 +270,47 @@ static void omap_dma_channel_end_prog(struct omap_dma_s *s,
+     }
+ }
++static void omap_dma_interrupts_3_1_update(struct omap_dma_s *s)
++{
++    struct omap_dma_channel_s *ch = s->ch;
++
++    /* First three interrupts are shared between two channels each. */
++    if (ch[0].status | ch[6].status)
++        qemu_irq_raise(ch[0].irq);
++    if (ch[1].status | ch[7].status)
++        qemu_irq_raise(ch[1].irq);
++    if (ch[2].status | ch[8].status)
++        qemu_irq_raise(ch[2].irq);
++    if (ch[3].status)
++        qemu_irq_raise(ch[3].irq);
++    if (ch[4].status)
++        qemu_irq_raise(ch[4].irq);
++    if (ch[5].status)
++        qemu_irq_raise(ch[5].irq);
++}
++
++static void omap_dma_interrupts_3_2_update(struct omap_dma_s *s)
++{
++    struct omap_dma_channel_s *ch = s->ch;
++    int i;
++
++    for (i = s->chans; i; ch ++, i --)
++        if (ch->status)
++            qemu_irq_raise(ch->irq);
++}
++
+ static void omap_dma_enable_3_1_mapping(struct omap_dma_s *s)
+ {
+     s->omap_3_1_mapping_disabled = 0;
+     s->chans = 9;
++    s->intr_update = omap_dma_interrupts_3_1_update;
+ }
+ static void omap_dma_disable_3_1_mapping(struct omap_dma_s *s)
+ {
+     s->omap_3_1_mapping_disabled = 1;
+     s->chans = 16;
++    s->intr_update = omap_dma_interrupts_3_2_update;
+ }
+ static void omap_dma_process_request(struct omap_dma_s *s, int request)
+@@ -358,6 +400,22 @@ static void omap_dma_channel_run(struct omap_dma_s *s)
+                 if (ch->interrupts & HALF_FRAME_INTR)
+                     ch->status |= HALF_FRAME_INTR;
++            if (ch->fs && ch->bs) {
++                a->pck_element ++;
++                /* Check if a full packet has beed transferred.  */
++                if (a->pck_element == a->pck_elements) {
++                    a->pck_element = 0;
++
++                    /* Set the END_PKT interrupt */
++                    if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync)
++                        ch->status |= END_PKT_INTR;
++
++                    /* If the channel is packet-synchronized, deactivate it */
++                    if (ch->sync)
++                        omap_dma_deactivate_channel(s, ch);
++                }
++            }
++
+             if (a->element == a->elements) {
+                 /* End of Frame */
+                 a->element = 0;
+@@ -366,7 +424,7 @@ static void omap_dma_channel_run(struct omap_dma_s *s)
+                 a->frame ++;
+                 /* If the channel is frame synchronized, deactivate it */
+-                if (ch->sync && ch->fs)
++                if (ch->sync && ch->fs && !ch->bs)
+                     omap_dma_deactivate_channel(s, ch);
+                 /* If the channel is async, update cpc */
+@@ -414,50 +472,62 @@ void omap_dma_reset(struct omap_dma_s *s)
+     int i;
+     qemu_del_timer(s->tm);
+-    s->gcr = 0x0004;
++    if (s->model < omap_dma_4)
++        s->gcr = 0x0004;
++    else
++        s->gcr = 0x00010010;
++    s->ocp = 0x00000000;
++    memset(&s->irqstat, 0, sizeof(s->irqstat));
++    memset(&s->irqen, 0, sizeof(s->irqen));
+     s->drq = 0x00000000;
+     s->run_count = 0;
+     s->lcd_ch.src = emiff;
+     s->lcd_ch.condition = 0;
+     s->lcd_ch.interrupts = 0;
+     s->lcd_ch.dual = 0;
+-    omap_dma_enable_3_1_mapping(s);
++    if (s->model < omap_dma_4)
++        omap_dma_enable_3_1_mapping(s);
+     for (i = 0; i < s->chans; i ++) {
++        s->ch[i].suspend = 0;
++        s->ch[i].prefetch = 0;
++        s->ch[i].buf_disable = 0;
++        s->ch[i].src_sync = 0;
+         memset(&s->ch[i].burst, 0, sizeof(s->ch[i].burst));
+         memset(&s->ch[i].port, 0, sizeof(s->ch[i].port));
+         memset(&s->ch[i].mode, 0, sizeof(s->ch[i].mode));
+-        memset(&s->ch[i].elements, 0, sizeof(s->ch[i].elements));
+-        memset(&s->ch[i].frames, 0, sizeof(s->ch[i].frames));
+         memset(&s->ch[i].frame_index, 0, sizeof(s->ch[i].frame_index));
+         memset(&s->ch[i].element_index, 0, sizeof(s->ch[i].element_index));
+-        memset(&s->ch[i].data_type, 0, sizeof(s->ch[i].data_type));
+-        memset(&s->ch[i].transparent_copy, 0,
+-                        sizeof(s->ch[i].transparent_copy));
+-        memset(&s->ch[i].constant_fill, 0, sizeof(s->ch[i].constant_fill));
+-        memset(&s->ch[i].color, 0, sizeof(s->ch[i].color));
+-        memset(&s->ch[i].end_prog, 0, sizeof(s->ch[i].end_prog));
+-        memset(&s->ch[i].repeat, 0, sizeof(s->ch[i].repeat));
+-        memset(&s->ch[i].auto_init, 0, sizeof(s->ch[i].auto_init));
+-        memset(&s->ch[i].link_enabled, 0, sizeof(s->ch[i].link_enabled));
+-        memset(&s->ch[i].link_next_ch, 0, sizeof(s->ch[i].link_next_ch));
+-        s->ch[i].interrupts = 0x0003;
+-        memset(&s->ch[i].status, 0, sizeof(s->ch[i].status));
+-        memset(&s->ch[i].active, 0, sizeof(s->ch[i].active));
+-        memset(&s->ch[i].enable, 0, sizeof(s->ch[i].enable));
+-        memset(&s->ch[i].sync, 0, sizeof(s->ch[i].sync));
+-        memset(&s->ch[i].pending_request, 0, sizeof(s->ch[i].pending_request));
+-        memset(&s->ch[i].waiting_end_prog, 0,
+-                        sizeof(s->ch[i].waiting_end_prog));
+-        memset(&s->ch[i].cpc, 0, sizeof(s->ch[i].cpc));
+-        memset(&s->ch[i].fs, 0, sizeof(s->ch[i].fs));
+-        memset(&s->ch[i].bs, 0, sizeof(s->ch[i].bs));
+-        memset(&s->ch[i].omap_3_1_compatible_disable, 0,
+-                        sizeof(s->ch[i].omap_3_1_compatible_disable));
++        memset(&s->ch[i].endian, 0, sizeof(s->ch[i].endian));
++        memset(&s->ch[i].endian_lock, 0, sizeof(s->ch[i].endian_lock));
++        memset(&s->ch[i].translate, 0, sizeof(s->ch[i].translate));
++        s->ch[i].write_mode = 0;
++        s->ch[i].data_type = 0;
++        s->ch[i].transparent_copy = 0;
++        s->ch[i].constant_fill = 0;
++        s->ch[i].color = 0x00000000;
++        s->ch[i].end_prog = 0;
++        s->ch[i].repeat = 0;
++        s->ch[i].auto_init = 0;
++        s->ch[i].link_enabled = 0;
++        if (s->model < omap_dma_4)
++            s->ch[i].interrupts = 0x0003;
++        else
++            s->ch[i].interrupts = 0x0000;
++        s->ch[i].status = 0;
++        s->ch[i].cstatus = 0;
++        s->ch[i].active = 0;
++        s->ch[i].enable = 0;
++        s->ch[i].sync = 0;
++        s->ch[i].pending_request = 0;
++        s->ch[i].waiting_end_prog = 0;
++        s->ch[i].cpc = 0x0000;
++        s->ch[i].fs = 0;
++        s->ch[i].bs = 0;
++        s->ch[i].omap_3_1_compatible_disable = 0;
+         memset(&s->ch[i].active_set, 0, sizeof(s->ch[i].active_set));
+-        memset(&s->ch[i].priority, 0, sizeof(s->ch[i].priority));
+-        memset(&s->ch[i].interleave_disabled, 0,
+-                        sizeof(s->ch[i].interleave_disabled));
+-        memset(&s->ch[i].type, 0, sizeof(s->ch[i].type));
++        s->ch[i].priority = 0;
++        s->ch[i].interleave_disabled = 0;
++        s->ch[i].type = 0;
+     }
+ }
+@@ -476,7 +546,7 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s,
+         break;
+     case 0x02:        /* SYS_DMA_CCR_CH0 */
+-        if (s->model == omap_dma_3_1)
++        if (s->model <= omap_dma_3_1)
+             *value = 0 << 10;                 /* FIFO_FLUSH reads as 0 */
+         else
+             *value = ch->omap_3_1_compatible_disable << 10;
+@@ -596,11 +666,11 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+         ch->burst[0] = (value & 0x0180) >> 7;
+         ch->pack[0] = (value & 0x0040) >> 6;
+         ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2);
+-        ch->data_type = (1 << (value & 3));
+-        if (ch->port[0] >= omap_dma_port_last)
++        ch->data_type = 1 << (value & 3);
++        if (ch->port[0] >= __omap_dma_port_last)
+             printf("%s: invalid DMA port %i\n", __FUNCTION__,
+                             ch->port[0]);
+-        if (ch->port[1] >= omap_dma_port_last)
++        if (ch->port[1] >= __omap_dma_port_last)
+             printf("%s: invalid DMA port %i\n", __FUNCTION__,
+                             ch->port[1]);
+         if ((value & 3) == 3)
+@@ -611,7 +681,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+         ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14);
+         ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12);
+         ch->end_prog = (value & 0x0800) >> 11;
+-        if (s->model > omap_dma_3_1)
++        if (s->model >= omap_dma_3_2)
+             ch->omap_3_1_compatible_disable  = (value >> 10) & 0x1;
+         ch->repeat = (value & 0x0200) >> 9;
+         ch->auto_init = (value & 0x0100) >> 8;
+@@ -630,7 +700,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+         break;
+     case 0x04:        /* SYS_DMA_CICR_CH0 */
+-        ch->interrupts = value;
++        ch->interrupts = value & 0x3f;
+         break;
+     case 0x06:        /* SYS_DMA_CSR_CH0 */
+@@ -696,7 +766,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+         break;
+     case 0x24:        /* DMA_CCR2 */
+-        ch->bs  = (value >> 2) & 0x1;
++        ch->bs = (value >> 2) & 0x1;
+         ch->transparent_copy = (value >> 1) & 0x1;
+         ch->constant_fill = value & 0x1;
+         break;
+@@ -1126,48 +1196,29 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset,
+         break;
+     case 0x44e:       /* DMA_CAPS_0_U */
+-        *ret = (1 << 3) | /* Constant Fill Capacity */
+-            (1 << 2);     /* Transparent BLT Capacity */
++        *ret = (s->caps[0] >> 16) & 0xffff;
+         break;
+-
+     case 0x450:       /* DMA_CAPS_0_L */
+-    case 0x452:       /* DMA_CAPS_1_U */
+-        *ret = 0;
++        *ret = (s->caps[0] >>  0) & 0xffff;
+         break;
++    case 0x452:       /* DMA_CAPS_1_U */
++        *ret = (s->caps[1] >> 16) & 0xffff;
++        break;
+     case 0x454:       /* DMA_CAPS_1_L */
+-        *ret = (1 << 1); /* 1-bit palletized capability */
++        *ret = (s->caps[1] >>  0) & 0xffff;
+         break;
+     case 0x456:       /* DMA_CAPS_2 */
+-        *ret = (1 << 8) | /* SSDIC */
+-            (1 << 7) |    /* DDIAC */
+-            (1 << 6) |    /* DSIAC */
+-            (1 << 5) |    /* DPIAC */
+-            (1 << 4) |    /* DCAC  */
+-            (1 << 3) |    /* SDIAC */
+-            (1 << 2) |    /* SSIAC */
+-            (1 << 1) |    /* SPIAC */
+-            1;            /* SCAC  */
++        *ret = s->caps[2];
+         break;
+     case 0x458:       /* DMA_CAPS_3 */
+-        *ret = (1 << 5) | /* CCC */
+-            (1 << 4) |    /* IC  */
+-            (1 << 3) |    /* ARC */
+-            (1 << 2) |    /* AEC */
+-            (1 << 1) |    /* FSC */
+-            1;            /* ESC */
++        *ret = s->caps[3];
+         break;
+     case 0x45a:       /* DMA_CAPS_4 */
+-        *ret = (1 << 6) | /* SSC  */
+-            (1 << 5) |    /* BIC  */
+-            (1 << 4) |    /* LFIC */
+-            (1 << 3) |    /* FIC  */
+-            (1 << 2) |    /* HFIC */
+-            (1 << 1) |    /* EDIC */
+-            1;            /* TOIC */
++        *ret = s->caps[4];
+         break;
+     case 0x460:       /* DMA_PCh2_SR */
+@@ -1193,7 +1244,7 @@ static uint32_t omap_dma_read(void *opaque, target_phys_addr_t addr)
+     switch (offset) {
+     case 0x300 ... 0x3fe:
+-        if (s->model == omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
++        if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
+             if (omap_dma_3_1_lcd_read(&s->lcd_ch, offset, &ret))
+                 break;
+             return ret;
+@@ -1207,7 +1258,7 @@ static uint32_t omap_dma_read(void *opaque, target_phys_addr_t addr)
+         return ret;
+     case 0x404 ... 0x4fe:
+-        if (s->model == omap_dma_3_1)
++        if (s->model <= omap_dma_3_1)
+             break;
+         /* Fall through. */
+     case 0x400:
+@@ -1236,7 +1287,7 @@ static void omap_dma_write(void *opaque, target_phys_addr_t addr,
+     switch (offset) {
+     case 0x300 ... 0x3fe:
+-        if (s->model == omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
++        if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
+             if (omap_dma_3_1_lcd_write(&s->lcd_ch, offset, value))
+                 break;
+             return;
+@@ -1250,7 +1301,7 @@ static void omap_dma_write(void *opaque, target_phys_addr_t addr,
+         return;
+     case 0x404 ... 0x4fe:
+-        if (s->model == omap_dma_3_1)
++        if (s->model <= omap_dma_3_1)
+             break;
+     case 0x400:
+         /* Fall through. */
+@@ -1285,7 +1336,7 @@ static CPUWriteMemoryFunc *omap_dma_writefn[] = {
+ static void omap_dma_request(void *opaque, int drq, int req)
+ {
+     struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+-    /* The request pins are level triggered.  */
++    /* The request pins are level triggered in QEMU.  */
+     if (req) {
+         if (~s->drq & (1 << drq)) {
+             s->drq |= 1 << drq;
+@@ -1310,6 +1361,52 @@ static void omap_dma_clk_update(void *opaque, int line, int on)
+     }
+ }
++static void omap_dma_setcaps(struct omap_dma_s *s)
++{
++    switch (s->model) {
++    default:
++    case omap_dma_3_1:
++        break;
++    case omap_dma_3_2:
++    case omap_dma_4:
++        /* XXX Only available for sDMA */
++        s->caps[0] =
++                (1 << 19) |   /* Constant Fill Capability */
++                (1 << 18);    /* Transparent BLT Capability */
++        s->caps[1] =
++                (1 << 1);     /* 1-bit palettized capability (DMA 3.2 only) */
++        s->caps[2] =
++                (1 << 8) |    /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */
++                (1 << 7) |    /* DST_DOUBLE_INDEX_ADRS_CPBLTY */
++                (1 << 6) |    /* DST_SINGLE_INDEX_ADRS_CPBLTY */
++                (1 << 5) |    /* DST_POST_INCRMNT_ADRS_CPBLTY */
++                (1 << 4) |    /* DST_CONST_ADRS_CPBLTY */
++                (1 << 3) |    /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */
++                (1 << 2) |    /* SRC_SINGLE_INDEX_ADRS_CPBLTY */
++                (1 << 1) |    /* SRC_POST_INCRMNT_ADRS_CPBLTY */
++                (1 << 0);     /* SRC_CONST_ADRS_CPBLTY */
++        s->caps[3] =
++                (1 << 6) |    /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */
++                (1 << 7) |    /* PKT_SYNCHR_CPBLTY (DMA 4 only) */
++                (1 << 5) |    /* CHANNEL_CHAINING_CPBLTY */
++                (1 << 4) |    /* LCh_INTERLEAVE_CPBLTY */
++                (1 << 3) |    /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */
++                (1 << 2) |    /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */
++                (1 << 1) |    /* FRAME_SYNCHR_CPBLTY */
++                (1 << 0);     /* ELMNT_SYNCHR_CPBLTY */
++        s->caps[4] =
++                (1 << 7) |    /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */
++                (1 << 6) |    /* SYNC_STATUS_CPBLTY */
++                (1 << 5) |    /* BLOCK_INTERRUPT_CPBLTY */
++                (1 << 4) |    /* LAST_FRAME_INTERRUPT_CPBLTY */
++                (1 << 3) |    /* FRAME_INTERRUPT_CPBLTY */
++                (1 << 2) |    /* HALF_FRAME_INTERRUPT_CPBLTY */
++                (1 << 1) |    /* EVENT_DROP_INTERRUPT_CPBLTY */
++                (1 << 0);     /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */
++        break;
++    }
++}
++
+ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+                 qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk,
+                 enum omap_dma_model model)
+@@ -1318,7 +1415,7 @@ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+     struct omap_dma_s *s = (struct omap_dma_s *)
+             qemu_mallocz(sizeof(struct omap_dma_s));
+-    if (model == omap_dma_3_1) {
++    if (model <= omap_dma_3_1) {
+         num_irqs = 6;
+         memsize = 0x800;
+     } else {
+@@ -1331,6 +1428,7 @@ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+     s->clk = clk;
+     s->lcd_ch.irq = lcd_irq;
+     s->lcd_ch.mpu = mpu;
++    omap_dma_setcaps(s);
+     while (num_irqs --)
+         s->ch[num_irqs].irq = irqs[num_irqs];
+     for (i = 0; i < 3; i ++) {
+@@ -1350,6 +1448,393 @@ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+     return s;
+ }
++static void omap_dma_interrupts_4_update(struct omap_dma_s *s)
++{
++    struct omap_dma_channel_s *ch = s->ch;
++    uint32_t bmp, bit;
++
++    for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1)
++        if (ch->status) {
++            bmp |= bit;
++            ch->cstatus |= ch->status;
++            ch->status = 0;
++        }
++    if ((s->irqstat[0] |= s->irqen[0] & bmp))
++        qemu_irq_raise(s->irq[0]);
++    if ((s->irqstat[1] |= s->irqen[1] & bmp))
++        qemu_irq_raise(s->irq[1]);
++    if ((s->irqstat[2] |= s->irqen[2] & bmp))
++        qemu_irq_raise(s->irq[2]);
++    if ((s->irqstat[3] |= s->irqen[3] & bmp))
++        qemu_irq_raise(s->irq[3]);
++}
++
++static uint32_t omap_dma4_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
++    int irqn = 0, chnum, offset = addr - s->base;
++    struct omap_dma_channel_s *ch;
++
++    switch (offset) {
++    case 0x00:        /* DMA4_REVISION */
++        return 0x40;
++
++    case 0x14:        /* DMA4_IRQSTATUS_L3 */
++        irqn ++;
++    case 0x10:        /* DMA4_IRQSTATUS_L2 */
++        irqn ++;
++    case 0x0c:        /* DMA4_IRQSTATUS_L1 */
++        irqn ++;
++    case 0x08:        /* DMA4_IRQSTATUS_L0 */
++        return s->irqstat[irqn];
++
++    case 0x24:        /* DMA4_IRQENABLE_L3 */
++        irqn ++;
++    case 0x20:        /* DMA4_IRQENABLE_L2 */
++        irqn ++;
++    case 0x1c:        /* DMA4_IRQENABLE_L1 */
++        irqn ++;
++    case 0x18:        /* DMA4_IRQENABLE_L0 */
++        return s->irqen[irqn];
++
++    case 0x28:        /* DMA4_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x2c:        /* DMA4_OCP_SYSCONFIG */
++        return s->ocp;
++
++    case 0x64:        /* DMA4_CAPS_0 */
++        return s->caps[0];
++    case 0x6c:        /* DMA4_CAPS_2 */
++        return s->caps[2];
++    case 0x70:        /* DMA4_CAPS_3 */
++        return s->caps[3];
++    case 0x74:        /* DMA4_CAPS_4 */
++        return s->caps[4];
++
++    case 0x78:        /* DMA4_GCR */
++        return s->gcr;
++
++    case 0x80 ... 0xfff:
++        offset -= 0x80;
++        chnum = offset / 0x60;
++        ch = s->ch + chnum;
++        offset -= chnum * 0x60;
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return 0;
++    }
++
++    /* Per-channel registers */
++    switch (offset) {
++    case 0x00:        /* DMA4_CCR */
++        return (ch->buf_disable << 25) |
++                (ch->src_sync << 24) |
++                (ch->prefetch << 23) |
++                ((ch->sync & 0x60) << 14) |
++                (ch->bs << 18) |
++                (ch->transparent_copy << 17) |
++                (ch->constant_fill << 16) |
++                (ch->mode[1] << 14) |
++                (ch->mode[0] << 12) |
++                (0 << 10) | (0 << 9) |
++                (ch->suspend << 8) |
++                (ch->enable << 7) |
++                (ch->priority << 6) |
++                (ch->fs << 5) | (ch->sync & 0x1f);
++
++    case 0x04:        /* DMA4_CLNK_CTRL */
++        return (ch->link_enabled << 15) | ch->link_next_ch;
++
++    case 0x08:        /* DMA4_CICR */
++        return ch->interrupts;
++
++    case 0x0c:        /* DMA4_CSR */
++        return ch->cstatus;
++
++    case 0x10:        /* DMA4_CSDP */
++        return (ch->endian[0] << 21) |
++                (ch->endian_lock[0] << 20) |
++                (ch->endian[1] << 19) |
++                (ch->endian_lock[1] << 18) |
++                (ch->write_mode << 16) |
++                (ch->burst[1] << 14) |
++                (ch->pack[1] << 13) |
++                (ch->translate[1] << 9) |
++                (ch->burst[0] << 7) |
++                (ch->pack[0] << 6) |
++                (ch->translate[0] << 2) |
++                (ch->data_type >> 1);
++
++    case 0x14:        /* DMA4_CEN */
++        return ch->elements;
++
++    case 0x18:        /* DMA4_CFN */
++        return ch->frames;
++
++    case 0x1c:        /* DMA4_CSSA */
++        return ch->addr[0];
++
++    case 0x20:        /* DMA4_CDSA */
++        return ch->addr[1];
++
++    case 0x24:        /* DMA4_CSEI */
++        return ch->element_index[0];
++
++    case 0x28:        /* DMA4_CSFI */
++        return ch->frame_index[0];
++
++    case 0x2c:        /* DMA4_CDEI */
++        return ch->element_index[1];
++
++    case 0x30:        /* DMA4_CDFI */
++        return ch->frame_index[1];
++
++    case 0x34:        /* DMA4_CSAC */
++        return ch->active_set.src & 0xffff;
++
++    case 0x38:        /* DMA4_CDAC */
++        return ch->active_set.dest & 0xffff;
++
++    case 0x3c:        /* DMA4_CCEN */
++        return ch->active_set.element;
++
++    case 0x40:        /* DMA4_CCFN */
++        return ch->active_set.frame;
++
++    case 0x44:        /* DMA4_COLOR */
++        /* XXX only in sDMA */
++        return ch->color;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return 0;
++    }
++}
++
++static void omap_dma4_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dma_s *s = (struct omap_dma_s *) opaque;
++    int chnum, irqn = 0, offset = addr - s->base;
++    struct omap_dma_channel_s *ch;
++
++    switch (offset) {
++    case 0x14:        /* DMA4_IRQSTATUS_L3 */
++        irqn ++;
++    case 0x10:        /* DMA4_IRQSTATUS_L2 */
++        irqn ++;
++    case 0x0c:        /* DMA4_IRQSTATUS_L1 */
++        irqn ++;
++    case 0x08:        /* DMA4_IRQSTATUS_L0 */
++        s->irqstat[irqn] &= ~value;
++        if (!s->irqstat[irqn])
++            qemu_irq_lower(s->irq[irqn]);
++        return;
++
++    case 0x24:        /* DMA4_IRQENABLE_L3 */
++        irqn ++;
++    case 0x20:        /* DMA4_IRQENABLE_L2 */
++        irqn ++;
++    case 0x1c:        /* DMA4_IRQENABLE_L1 */
++        irqn ++;
++    case 0x18:        /* DMA4_IRQENABLE_L0 */
++        s->irqen[irqn] = value;
++        return;
++
++    case 0x2c:        /* DMA4_OCP_SYSCONFIG */
++        if (value & 2)                                                /* SOFTRESET */
++            omap_dma_reset(s);
++        s->ocp = value & 0x3321;
++        if (((s->ocp >> 12) & 3) == 3)                                /* MIDLEMODE */
++            fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__);
++        return;
++
++    case 0x78:        /* DMA4_GCR */
++        s->gcr = value & 0x00ff00ff;
++      if ((value & 0xff) == 0x00)             /* MAX_CHANNEL_FIFO_DEPTH */
++            fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __FUNCTION__);
++        return;
++
++    case 0x80 ... 0xfff:
++        offset -= 0x80;
++        chnum = offset / 0x60;
++        ch = s->ch + chnum;
++        offset -= chnum * 0x60;
++        break;
++
++    case 0x00:        /* DMA4_REVISION */
++    case 0x28:        /* DMA4_SYSSTATUS */
++    case 0x64:        /* DMA4_CAPS_0 */
++    case 0x6c:        /* DMA4_CAPS_2 */
++    case 0x70:        /* DMA4_CAPS_3 */
++    case 0x74:        /* DMA4_CAPS_4 */
++        OMAP_RO_REG(addr);
++        return;
++
++    default:
++        OMAP_BAD_REG(addr);
++        return;
++    }
++
++    /* Per-channel registers */
++    switch (offset) {
++    case 0x00:        /* DMA4_CCR */
++        ch->buf_disable = (value >> 25) & 1;
++        ch->src_sync = (value >> 24) & 1;     /* XXX For CamDMA must be 1 */
++        if (ch->buf_disable && !ch->src_sync)
++            fprintf(stderr, "%s: Buffering disable is not allowed in "
++                            "destination synchronised mode\n", __FUNCTION__);
++        ch->prefetch = (value >> 23) & 1;
++        ch->bs = (value >> 18) & 1;
++        ch->transparent_copy = (value >> 17) & 1;
++        ch->constant_fill = (value >> 16) & 1;
++        ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14);
++        ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12);
++        ch->suspend = (value & 0x0100) >> 8;
++        ch->priority = (value & 0x0040) >> 6;
++        ch->fs = (value & 0x0020) >> 5;
++        if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1])
++            fprintf(stderr, "%s: For a packet transfer at least one port "
++                            "must be constant-addressed\n", __FUNCTION__);
++        ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060);
++        /* XXX must be 0x01 for CamDMA */
++
++        if (value & 0x0080)
++            omap_dma_enable_channel(s, ch);
++        else
++            omap_dma_disable_channel(s, ch);
++
++        break;
++
++    case 0x04:        /* DMA4_CLNK_CTRL */
++        ch->link_enabled = (value >> 15) & 0x1;
++        ch->link_next_ch = value & 0x1f;
++        break;
++
++    case 0x08:        /* DMA4_CICR */
++        ch->interrupts = value & 0x09be;
++        break;
++
++    case 0x0c:        /* DMA4_CSR */
++        ch->cstatus &= ~value;
++        break;
++
++    case 0x10:        /* DMA4_CSDP */
++        ch->endian[0] =(value >> 21) & 1;
++        ch->endian_lock[0] =(value >> 20) & 1;
++        ch->endian[1] =(value >> 19) & 1;
++        ch->endian_lock[1] =(value >> 18) & 1;
++        if (ch->endian[0] != ch->endian[1])
++            fprintf(stderr, "%s: DMA endianned conversion enable attempt\n",
++                            __FUNCTION__);
++        ch->write_mode = (value >> 16) & 3;
++        ch->burst[1] = (value & 0xc000) >> 14;
++        ch->pack[1] = (value & 0x2000) >> 13;
++        ch->translate[1] = (value & 0x1e00) >> 9;
++        ch->burst[0] = (value & 0x0180) >> 7;
++        ch->pack[0] = (value & 0x0040) >> 6;
++        ch->translate[0] = (value & 0x003c) >> 2;
++        if (ch->translate[0] | ch->translate[1])
++            fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n",
++                            __FUNCTION__);
++        ch->data_type = 1 << (value & 3);
++        if ((value & 3) == 3)
++            printf("%s: bad data_type for DMA channel\n", __FUNCTION__);
++        break;
++
++    case 0x14:        /* DMA4_CEN */
++        ch->elements = value & 0xffffff;
++        break;
++
++    case 0x18:        /* DMA4_CFN */
++        ch->frames = value & 0xffff;
++        break;
++
++    case 0x1c:        /* DMA4_CSSA */
++        ch->addr[0] = (target_phys_addr_t) (uint32_t) value;
++        break;
++
++    case 0x20:        /* DMA4_CDSA */
++        ch->addr[1] = (target_phys_addr_t) (uint32_t) value;
++        break;
++
++    case 0x24:        /* DMA4_CSEI */
++        ch->element_index[0] = (int16_t) value;
++        break;
++
++    case 0x28:        /* DMA4_CSFI */
++        ch->frame_index[0] = (int32_t) value;
++        break;
++
++    case 0x2c:        /* DMA4_CDEI */
++        ch->element_index[1] = (int16_t) value;
++        break;
++
++    case 0x30:        /* DMA4_CDFI */
++        ch->frame_index[1] = (int32_t) value;
++        break;
++
++    case 0x44:        /* DMA4_COLOR */
++        /* XXX only in sDMA */
++        ch->color = value;
++        break;
++
++    case 0x34:        /* DMA4_CSAC */
++    case 0x38:        /* DMA4_CDAC */
++    case 0x3c:        /* DMA4_CCEN */
++    case 0x40:        /* DMA4_CCFN */
++        OMAP_RO_REG(addr);
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_dma4_readfn[] = {
++    omap_badwidth_read16,
++    omap_dma4_read,
++    omap_dma4_read,
++};
++
++static CPUWriteMemoryFunc *omap_dma4_writefn[] = {
++    omap_badwidth_write16,
++    omap_dma4_write,
++    omap_dma4_write,
++};
++
++struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs,
++                struct omap_mpu_state_s *mpu, int fifo,
++                int chans, omap_clk iclk, omap_clk fclk)
++{
++    int iomemtype;
++    struct omap_dma_s *s = (struct omap_dma_s *)
++            qemu_mallocz(sizeof(struct omap_dma_s));
++
++    s->base = base;
++    s->model = omap_dma_4;
++    s->chans = chans;
++    s->mpu = mpu;
++    s->clk = fclk;
++    memcpy(&s->irq, irqs, sizeof(s->irq));
++    s->intr_update = omap_dma_interrupts_4_update;
++    omap_dma_setcaps(s);
++    s->tm = qemu_new_timer(vm_clock, (QEMUTimerCB *) omap_dma_channel_run, s);
++    omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]);
++    mpu->drq = qemu_allocate_irqs(omap_dma_request, s, 64);
++    omap_dma_reset(s);
++    omap_dma_clk_update(s, 0, 1);
++
++    iomemtype = cpu_register_io_memory(0, omap_dma4_readfn,
++                    omap_dma4_writefn, s);
++    cpu_register_physical_memory(s->base, 0x1000, iomemtype);
++
++    return s;
++}
++
+ struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct omap_dma_s *s)
+ {
+     return &s->lcd_ch;
+diff --git a/hw/omap_dss.c b/hw/omap_dss.c
+new file mode 100644
+index 0000000..1c16802
+--- /dev/null
++++ b/hw/omap_dss.c
+@@ -0,0 +1,1088 @@
++/*
++ * OMAP2 Display Subsystem.
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++#include "hw.h"
++#include "console.h"
++#include "omap.h"
++
++struct omap_dss_s {
++    target_phys_addr_t diss_base;
++    target_phys_addr_t disc_base;
++    target_phys_addr_t rfbi_base;
++    target_phys_addr_t venc_base;
++    target_phys_addr_t im3_base;
++    qemu_irq irq;
++    qemu_irq drq;
++    DisplayState *state;
++
++    int autoidle;
++    int control;
++    int enable;
++
++    struct omap_dss_panel_s {
++        int enable;
++        int nx;
++        int ny;
++
++        int x;
++        int y;
++    } dig, lcd;
++
++    struct {
++        uint32_t idlemode;
++        uint32_t irqst;
++        uint32_t irqen;
++        uint32_t control;
++        uint32_t config;
++        uint32_t capable;
++        uint32_t timing[3];
++        int line;
++        uint32_t bg[2];
++        uint32_t trans[2];
++
++        struct omap_dss_plane_s {
++            int enable;
++            int bpp;
++            int posx;
++            int posy;
++            int nx;
++            int ny;
++
++            target_phys_addr_t addr[3];
++
++            uint32_t attr;
++            uint32_t tresh;
++            int rowinc;
++            int colinc;
++            int wininc;
++        } l[3];
++
++        int invalidate;
++        uint16_t palette[256];
++    } dispc;
++
++    struct {
++        int idlemode;
++        uint32_t control;
++        int enable;
++        int pixels;
++        int busy;
++        int skiplines;
++        uint16_t rxbuf;
++        uint32_t config[2];
++        uint32_t time[4];
++        uint32_t data[6];
++        uint16_t vsync;
++        uint16_t hsync;
++        struct rfbi_chip_s *chip[2];
++    } rfbi;
++};
++
++static void omap_dispc_interrupt_update(struct omap_dss_s *s)
++{
++    qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen);
++}
++
++static void omap_rfbi_reset(struct omap_dss_s *s)
++{
++    s->rfbi.idlemode = 0;
++    s->rfbi.control = 2;
++    s->rfbi.enable = 0;
++    s->rfbi.pixels = 0;
++    s->rfbi.skiplines = 0;
++    s->rfbi.busy = 0;
++    s->rfbi.config[0] = 0x00310000;
++    s->rfbi.config[1] = 0x00310000;
++    s->rfbi.time[0] = 0;
++    s->rfbi.time[1] = 0;
++    s->rfbi.time[2] = 0;
++    s->rfbi.time[3] = 0;
++    s->rfbi.data[0] = 0;
++    s->rfbi.data[1] = 0;
++    s->rfbi.data[2] = 0;
++    s->rfbi.data[3] = 0;
++    s->rfbi.data[4] = 0;
++    s->rfbi.data[5] = 0;
++    s->rfbi.vsync = 0;
++    s->rfbi.hsync = 0;
++}
++
++void omap_dss_reset(struct omap_dss_s *s)
++{
++    s->autoidle = 0;
++    s->control = 0;
++    s->enable = 0;
++
++    s->dig.enable = 0;
++    s->dig.nx = 1;
++    s->dig.ny = 1;
++
++    s->lcd.enable = 0;
++    s->lcd.nx = 1;
++    s->lcd.ny = 1;
++
++    s->dispc.idlemode = 0;
++    s->dispc.irqst = 0;
++    s->dispc.irqen = 0;
++    s->dispc.control = 0;
++    s->dispc.config = 0;
++    s->dispc.capable = 0x161;
++    s->dispc.timing[0] = 0;
++    s->dispc.timing[1] = 0;
++    s->dispc.timing[2] = 0;
++    s->dispc.line = 0;
++    s->dispc.bg[0] = 0;
++    s->dispc.bg[1] = 0;
++    s->dispc.trans[0] = 0;
++    s->dispc.trans[1] = 0;
++
++    s->dispc.l[0].enable = 0;
++    s->dispc.l[0].bpp = 0;
++    s->dispc.l[0].addr[0] = 0;
++    s->dispc.l[0].addr[1] = 0;
++    s->dispc.l[0].addr[2] = 0;
++    s->dispc.l[0].posx = 0;
++    s->dispc.l[0].posy = 0;
++    s->dispc.l[0].nx = 1;
++    s->dispc.l[0].ny = 1;
++    s->dispc.l[0].attr = 0;
++    s->dispc.l[0].tresh = 0;
++    s->dispc.l[0].rowinc = 1;
++    s->dispc.l[0].colinc = 1;
++    s->dispc.l[0].wininc = 0;
++
++    omap_rfbi_reset(s);
++    omap_dispc_interrupt_update(s);
++}
++
++static uint32_t omap_diss_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->diss_base;
++
++    switch (offset) {
++    case 0x00:        /* DSS_REVISIONNUMBER */
++        return 0x20;
++
++    case 0x10:        /* DSS_SYSCONFIG */
++        return s->autoidle;
++
++    case 0x14:        /* DSS_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x40:        /* DSS_CONTROL */
++        return s->control;
++
++    case 0x50:        /* DSS_PSA_LCD_REG_1 */
++    case 0x54:        /* DSS_PSA_LCD_REG_2 */
++    case 0x58:        /* DSS_PSA_VIDEO_REG */
++        /* TODO: fake some values when appropriate s->control bits are set */
++        return 0;
++
++    case 0x5c:        /* DSS_STATUS */
++        return 1 + (s->control & 1);
++
++    default:
++        break;
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_diss_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->diss_base;
++
++    switch (offset) {
++    case 0x00:        /* DSS_REVISIONNUMBER */
++    case 0x14:        /* DSS_SYSSTATUS */
++    case 0x50:        /* DSS_PSA_LCD_REG_1 */
++    case 0x54:        /* DSS_PSA_LCD_REG_2 */
++    case 0x58:        /* DSS_PSA_VIDEO_REG */
++    case 0x5c:        /* DSS_STATUS */
++        OMAP_RO_REG(addr);
++        break;
++
++    case 0x10:        /* DSS_SYSCONFIG */
++        if (value & 2)                                                /* SOFTRESET */
++            omap_dss_reset(s);
++        s->autoidle = value & 1;
++        break;
++
++    case 0x40:        /* DSS_CONTROL */
++        s->control = value & 0x3dd;
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_diss1_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_diss_read,
++};
++
++static CPUWriteMemoryFunc *omap_diss1_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_diss_write,
++};
++
++static uint32_t omap_disc_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->disc_base;
++
++    switch (offset) {
++    case 0x000:       /* DISPC_REVISION */
++        return 0x20;
++
++    case 0x010:       /* DISPC_SYSCONFIG */
++        return s->dispc.idlemode;
++
++    case 0x014:       /* DISPC_SYSSTATUS */
++        return 1;                                             /* RESETDONE */
++
++    case 0x018:       /* DISPC_IRQSTATUS */
++        return s->dispc.irqst;
++
++    case 0x01c:       /* DISPC_IRQENABLE */
++        return s->dispc.irqen;
++
++    case 0x040:       /* DISPC_CONTROL */
++        return s->dispc.control;
++
++    case 0x044:       /* DISPC_CONFIG */
++        return s->dispc.config;
++
++    case 0x048:       /* DISPC_CAPABLE */
++        return s->dispc.capable;
++
++    case 0x04c:       /* DISPC_DEFAULT_COLOR0 */
++        return s->dispc.bg[0];
++    case 0x050:       /* DISPC_DEFAULT_COLOR1 */
++        return s->dispc.bg[1];
++    case 0x054:       /* DISPC_TRANS_COLOR0 */
++        return s->dispc.trans[0];
++    case 0x058:       /* DISPC_TRANS_COLOR1 */
++        return s->dispc.trans[1];
++
++    case 0x05c:       /* DISPC_LINE_STATUS */
++        return 0x7ff;
++    case 0x060:       /* DISPC_LINE_NUMBER */
++        return s->dispc.line;
++
++    case 0x064:       /* DISPC_TIMING_H */
++        return s->dispc.timing[0];
++    case 0x068:       /* DISPC_TIMING_V */
++        return s->dispc.timing[1];
++    case 0x06c:       /* DISPC_POL_FREQ */
++        return s->dispc.timing[2];
++    case 0x070:       /* DISPC_DIVISOR */
++        return s->dispc.timing[3];
++
++    case 0x078:       /* DISPC_SIZE_DIG */
++        return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1);
++    case 0x07c:       /* DISPC_SIZE_LCD */
++        return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1);
++
++    case 0x080:       /* DISPC_GFX_BA0 */
++        return s->dispc.l[0].addr[0];
++    case 0x084:       /* DISPC_GFX_BA1 */
++        return s->dispc.l[0].addr[1];
++    case 0x088:       /* DISPC_GFX_POSITION */
++        return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx;
++    case 0x08c:       /* DISPC_GFX_SIZE */
++        return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1);
++    case 0x0a0:       /* DISPC_GFX_ATTRIBUTES */
++        return s->dispc.l[0].attr;
++    case 0x0a4:       /* DISPC_GFX_FIFO_TRESHOLD */
++        return s->dispc.l[0].tresh;
++    case 0x0a8:       /* DISPC_GFX_FIFO_SIZE_STATUS */
++        return 256;
++    case 0x0ac:       /* DISPC_GFX_ROW_INC */
++        return s->dispc.l[0].rowinc;
++    case 0x0b0:       /* DISPC_GFX_PIXEL_INC */
++        return s->dispc.l[0].colinc;
++    case 0x0b4:       /* DISPC_GFX_WINDOW_SKIP */
++        return s->dispc.l[0].wininc;
++    case 0x0b8:       /* DISPC_GFX_TABLE_BA */
++        return s->dispc.l[0].addr[2];
++
++    case 0x0bc:       /* DISPC_VID1_BA0 */
++    case 0x0c0:       /* DISPC_VID1_BA1 */
++    case 0x0c4:       /* DISPC_VID1_POSITION */
++    case 0x0c8:       /* DISPC_VID1_SIZE */
++    case 0x0cc:       /* DISPC_VID1_ATTRIBUTES */
++    case 0x0d0:       /* DISPC_VID1_FIFO_TRESHOLD */
++    case 0x0d4:       /* DISPC_VID1_FIFO_SIZE_STATUS */
++    case 0x0d8:       /* DISPC_VID1_ROW_INC */
++    case 0x0dc:       /* DISPC_VID1_PIXEL_INC */
++    case 0x0e0:       /* DISPC_VID1_FIR */
++    case 0x0e4:       /* DISPC_VID1_PICTURE_SIZE */
++    case 0x0e8:       /* DISPC_VID1_ACCU0 */
++    case 0x0ec:       /* DISPC_VID1_ACCU1 */
++    case 0x0f0 ... 0x140:     /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */
++    case 0x14c:       /* DISPC_VID2_BA0 */
++    case 0x150:       /* DISPC_VID2_BA1 */
++    case 0x154:       /* DISPC_VID2_POSITION */
++    case 0x158:       /* DISPC_VID2_SIZE */
++    case 0x15c:       /* DISPC_VID2_ATTRIBUTES */
++    case 0x160:       /* DISPC_VID2_FIFO_TRESHOLD */
++    case 0x164:       /* DISPC_VID2_FIFO_SIZE_STATUS */
++    case 0x168:       /* DISPC_VID2_ROW_INC */
++    case 0x16c:       /* DISPC_VID2_PIXEL_INC */
++    case 0x170:       /* DISPC_VID2_FIR */
++    case 0x174:       /* DISPC_VID2_PICTURE_SIZE */
++    case 0x178:       /* DISPC_VID2_ACCU0 */
++    case 0x17c:       /* DISPC_VID2_ACCU1 */
++    case 0x180 ... 0x1d0:     /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */
++    case 0x1d4:       /* DISPC_DATA_CYCLE1 */
++    case 0x1d8:       /* DISPC_DATA_CYCLE2 */
++    case 0x1dc:       /* DISPC_DATA_CYCLE3 */
++        return 0;
++
++    default:
++        break;
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_disc_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->disc_base;
++
++    switch (offset) {
++    case 0x010:       /* DISPC_SYSCONFIG */
++        if (value & 2)                                                /* SOFTRESET */
++            omap_dss_reset(s);
++        s->dispc.idlemode = value & 0x301b;
++        break;
++
++    case 0x018:       /* DISPC_IRQSTATUS */
++        s->dispc.irqst &= ~value;
++        omap_dispc_interrupt_update(s);
++        break;
++
++    case 0x01c:       /* DISPC_IRQENABLE */
++        s->dispc.irqen = value & 0xffff;
++        omap_dispc_interrupt_update(s);
++        break;
++
++    case 0x040:       /* DISPC_CONTROL */
++        s->dispc.control = value & 0x07ff9fff;
++        s->dig.enable = (value >> 1) & 1;
++        s->lcd.enable = (value >> 0) & 1;
++        if (value & (1 << 12))                        /* OVERLAY_OPTIMIZATION */
++            if (~((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1))
++                 fprintf(stderr, "%s: Overlay Optimization when no overlay "
++                                 "region effectively exists leads to "
++                                 "unpredictable behaviour!\n", __FUNCTION__);
++        if (value & (1 << 6)) {                               /* GODIGITAL */
++            //// Shadows:
++            //// s->dispc.config
++            //// s->dispc.capable
++            //// s->dispc.bg[0]
++            //// s->dispc.bg[1]
++            //// s->dispc.trans[0]
++            //// s->dispc.trans[1]
++            //// s->dispc.line
++            //// s->dispc.timing[0]
++            //// s->dispc.timing[1]
++            //// s->dispc.timing[2]
++            //// s->dispc.timing[3]
++            //// s->lcd.nx
++            //// s->lcd.ny
++            //// s->dig.nx
++            //// s->dig.ny
++            //// s->dispc.l[0].addr[0]
++            //// s->dispc.l[0].addr[1]
++            //// s->dispc.l[0].addr[2]
++            //// s->dispc.l[0].posx
++            //// s->dispc.l[0].posy
++            //// s->dispc.l[0].nx
++            //// s->dispc.l[0].ny
++            //// s->dispc.l[0].tresh
++            //// s->dispc.l[0].rowinc
++            //// s->dispc.l[0].colinc
++            //// s->dispc.l[0].wininc
++        }
++        if (value & (1 << 5)) {                               /* GOLCD */
++        }
++        s->dispc.invalidate = 1;
++        break;
++
++    case 0x044:       /* DISPC_CONFIG */
++        s->dispc.config = value & 0x3fff;
++        //// bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded
++        //// bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded
++        s->dispc.invalidate = 1;
++        break;
++
++    case 0x048:       /* DISPC_CAPABLE */
++        s->dispc.capable = value & 0x3ff;
++        break;
++
++    case 0x04c:       /* DISPC_DEFAULT_COLOR0 */
++        s->dispc.bg[0] = value & 0xffffff;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x050:       /* DISPC_DEFAULT_COLOR1 */
++        s->dispc.bg[1] = value & 0xffffff;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x054:       /* DISPC_TRANS_COLOR0 */
++        s->dispc.trans[0] = value & 0xffffff;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x058:       /* DISPC_TRANS_COLOR1 */
++        s->dispc.trans[1] = value & 0xffffff;
++        s->dispc.invalidate = 1;
++        break;
++
++    case 0x060:       /* DISPC_LINE_NUMBER */
++        s->dispc.line = value & 0x7ff;
++        break;
++
++    case 0x064:       /* DISPC_TIMING_H */
++        s->dispc.timing[0] = value & 0x0ff0ff3f;
++        break;
++    case 0x068:       /* DISPC_TIMING_V */
++        s->dispc.timing[1] = value & 0x0ff0ff3f;
++        break;
++    case 0x06c:       /* DISPC_POL_FREQ */
++        s->dispc.timing[2] = value & 0x0003ffff;
++        break;
++    case 0x070:       /* DISPC_DIVISOR */
++        s->dispc.timing[3] = value & 0x00ff00ff;
++        break;
++
++    case 0x078:       /* DISPC_SIZE_DIG */
++        s->dig.nx = ((value >>  0) & 0x7ff) + 1;              /* PPL */
++        s->dig.ny = ((value >> 16) & 0x7ff) + 1;              /* LPP */
++        s->dispc.invalidate = 1;
++        break;
++    case 0x07c:       /* DISPC_SIZE_LCD */
++        s->lcd.nx = ((value >>  0) & 0x7ff) + 1;              /* PPL */
++        s->lcd.ny = ((value >> 16) & 0x7ff) + 1;              /* LPP */
++        s->dispc.invalidate = 1;
++        break;
++    case 0x080:       /* DISPC_GFX_BA0 */
++        s->dispc.l[0].addr[0] = (target_phys_addr_t) value;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x084:       /* DISPC_GFX_BA1 */
++        s->dispc.l[0].addr[1] = (target_phys_addr_t) value;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x088:       /* DISPC_GFX_POSITION */
++        s->dispc.l[0].posx = ((value >>  0) & 0x7ff);         /* GFXPOSX */
++        s->dispc.l[0].posy = ((value >> 16) & 0x7ff);         /* GFXPOSY */
++        s->dispc.invalidate = 1;
++        break;
++    case 0x08c:       /* DISPC_GFX_SIZE */
++        s->dispc.l[0].nx = ((value >>  0) & 0x7ff) + 1;               /* GFXSIZEX */
++        s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1;               /* GFXSIZEY */
++        s->dispc.invalidate = 1;
++        break;
++    case 0x0a0:       /* DISPC_GFX_ATTRIBUTES */
++        s->dispc.l[0].attr = value & 0x7ff;
++        if (value & (3 << 9))
++            fprintf(stderr, "%s: Big-endian pixel format not supported\n",
++                            __FUNCTION__);
++        s->dispc.l[0].enable = value & 1;
++        s->dispc.l[0].bpp = (value >> 1) & 0xf;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x0a4:       /* DISPC_GFX_FIFO_TRESHOLD */
++        s->dispc.l[0].tresh = value & 0x01ff01ff;
++        break;
++    case 0x0ac:       /* DISPC_GFX_ROW_INC */
++        s->dispc.l[0].rowinc = value;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x0b0:       /* DISPC_GFX_PIXEL_INC */
++        s->dispc.l[0].colinc = value;
++        s->dispc.invalidate = 1;
++        break;
++    case 0x0b4:       /* DISPC_GFX_WINDOW_SKIP */
++        s->dispc.l[0].wininc = value;
++        break;
++    case 0x0b8:       /* DISPC_GFX_TABLE_BA */
++        s->dispc.l[0].addr[2] = (target_phys_addr_t) value;
++        s->dispc.invalidate = 1;
++        break;
++
++    case 0x0bc:       /* DISPC_VID1_BA0 */
++    case 0x0c0:       /* DISPC_VID1_BA1 */
++    case 0x0c4:       /* DISPC_VID1_POSITION */
++    case 0x0c8:       /* DISPC_VID1_SIZE */
++    case 0x0cc:       /* DISPC_VID1_ATTRIBUTES */
++    case 0x0d0:       /* DISPC_VID1_FIFO_TRESHOLD */
++    case 0x0d8:       /* DISPC_VID1_ROW_INC */
++    case 0x0dc:       /* DISPC_VID1_PIXEL_INC */
++    case 0x0e0:       /* DISPC_VID1_FIR */
++    case 0x0e4:       /* DISPC_VID1_PICTURE_SIZE */
++    case 0x0e8:       /* DISPC_VID1_ACCU0 */
++    case 0x0ec:       /* DISPC_VID1_ACCU1 */
++    case 0x0f0 ... 0x140:     /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */
++    case 0x14c:       /* DISPC_VID2_BA0 */
++    case 0x150:       /* DISPC_VID2_BA1 */
++    case 0x154:       /* DISPC_VID2_POSITION */
++    case 0x158:       /* DISPC_VID2_SIZE */
++    case 0x15c:       /* DISPC_VID2_ATTRIBUTES */
++    case 0x160:       /* DISPC_VID2_FIFO_TRESHOLD */
++    case 0x168:       /* DISPC_VID2_ROW_INC */
++    case 0x16c:       /* DISPC_VID2_PIXEL_INC */
++    case 0x170:       /* DISPC_VID2_FIR */
++    case 0x174:       /* DISPC_VID2_PICTURE_SIZE */
++    case 0x178:       /* DISPC_VID2_ACCU0 */
++    case 0x17c:       /* DISPC_VID2_ACCU1 */
++    case 0x180 ... 0x1d0:     /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */
++    case 0x1d4:       /* DISPC_DATA_CYCLE1 */
++    case 0x1d8:       /* DISPC_DATA_CYCLE2 */
++    case 0x1dc:       /* DISPC_DATA_CYCLE3 */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_disc1_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_disc_read,
++};
++
++static CPUWriteMemoryFunc *omap_disc1_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_disc_write,
++};
++
++static void *omap_rfbi_get_buffer(struct omap_dss_s *s)
++{
++    target_phys_addr_t fb;
++    uint32_t pd;
++
++    /* TODO */
++    fb = s->dispc.l[0].addr[0];
++
++    pd = cpu_get_physical_page_desc(fb);
++    if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM)
++        /* TODO */
++        cpu_abort(cpu_single_env, "%s: framebuffer outside RAM!\n",
++                        __FUNCTION__);
++    else
++        return phys_ram_base +
++                (pd & TARGET_PAGE_MASK) +
++                (fb & ~TARGET_PAGE_MASK);
++}
++
++static void omap_rfbi_transfer_stop(struct omap_dss_s *s)
++{
++    if (!s->rfbi.busy)
++        return;
++
++    /* TODO: in non-Bypass mode we probably need to just deassert the DRQ.  */
++
++    s->rfbi.busy = 0;
++}
++
++static void omap_rfbi_transfer_start(struct omap_dss_s *s)
++{
++    void *data;
++    size_t len;
++    int pitch;
++
++    if (!s->rfbi.enable || s->rfbi.busy)
++        return;
++
++    if (s->rfbi.control & (1 << 1)) {                         /* BYPASS */
++        /* TODO: in non-Bypass mode we probably need to just assert the
++         * DRQ and wait for DMA to write the pixels.  */
++        fprintf(stderr, "%s: Bypass mode unimplemented\n", __FUNCTION__);
++        return;
++    }
++
++    if (!(s->dispc.control & (1 << 11)))                      /* RFBIMODE */
++        return;
++    /* TODO: check that LCD output is enabled in DISPC.  */
++
++    s->rfbi.busy = 1;
++
++    data = omap_rfbi_get_buffer(s);
++
++    /* TODO bpp */
++    len = s->rfbi.pixels * 2;
++    s->rfbi.pixels = 0;
++
++    /* TODO: negative values */
++    pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2;
++
++    if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
++        s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch);
++    if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
++        s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch);
++
++    omap_rfbi_transfer_stop(s);
++
++    /* TODO */
++    s->dispc.irqst |= 1;                                      /* FRAMEDONE */
++    omap_dispc_interrupt_update(s);
++}
++
++static uint32_t omap_rfbi_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->rfbi_base;
++
++    switch (offset) {
++    case 0x00:        /* RFBI_REVISION */
++        return 0x10;
++
++    case 0x10:        /* RFBI_SYSCONFIG */
++        return s->rfbi.idlemode;
++
++    case 0x14:        /* RFBI_SYSSTATUS */
++        return 1 | (s->rfbi.busy << 8);                               /* RESETDONE */
++
++    case 0x40:        /* RFBI_CONTROL */
++        return s->rfbi.control;
++
++    case 0x44:        /* RFBI_PIXELCNT */
++        return s->rfbi.pixels;
++
++    case 0x48:        /* RFBI_LINE_NUMBER */
++        return s->rfbi.skiplines;
++
++    case 0x58:        /* RFBI_READ */
++    case 0x5c:        /* RFBI_STATUS */
++        return s->rfbi.rxbuf;
++
++    case 0x60:        /* RFBI_CONFIG0 */
++        return s->rfbi.config[0];
++    case 0x64:        /* RFBI_ONOFF_TIME0 */
++        return s->rfbi.time[0];
++    case 0x68:        /* RFBI_CYCLE_TIME0 */
++        return s->rfbi.time[1];
++    case 0x6c:        /* RFBI_DATA_CYCLE1_0 */
++        return s->rfbi.data[0];
++    case 0x70:        /* RFBI_DATA_CYCLE2_0 */
++        return s->rfbi.data[1];
++    case 0x74:        /* RFBI_DATA_CYCLE3_0 */
++        return s->rfbi.data[2];
++
++    case 0x78:        /* RFBI_CONFIG1 */
++        return s->rfbi.config[1];
++    case 0x7c:        /* RFBI_ONOFF_TIME1 */
++        return s->rfbi.time[2];
++    case 0x80:        /* RFBI_CYCLE_TIME1 */
++        return s->rfbi.time[3];
++    case 0x84:        /* RFBI_DATA_CYCLE1_1 */
++        return s->rfbi.data[3];
++    case 0x88:        /* RFBI_DATA_CYCLE2_1 */
++        return s->rfbi.data[4];
++    case 0x8c:        /* RFBI_DATA_CYCLE3_1 */
++        return s->rfbi.data[5];
++
++    case 0x90:        /* RFBI_VSYNC_WIDTH */
++        return s->rfbi.vsync;
++    case 0x94:        /* RFBI_HSYNC_WIDTH */
++        return s->rfbi.hsync;
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_rfbi_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->rfbi_base;
++
++    switch (offset) {
++    case 0x10:        /* RFBI_SYSCONFIG */
++        if (value & 2)                                                /* SOFTRESET */
++            omap_rfbi_reset(s);
++        s->rfbi.idlemode = value & 0x19;
++        break;
++
++    case 0x40:        /* RFBI_CONTROL */
++        s->rfbi.control = value & 0xf;
++        s->rfbi.enable = value & 1;
++        if (value & (1 << 4) &&                                       /* ITE */
++                        !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc))
++            omap_rfbi_transfer_start(s);
++        break;
++
++    case 0x44:        /* RFBI_PIXELCNT */
++        s->rfbi.pixels = value;
++        break;
++
++    case 0x48:        /* RFBI_LINE_NUMBER */
++        s->rfbi.skiplines = value & 0x7ff;
++        break;
++
++    case 0x4c:        /* RFBI_CMD */
++        if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
++            s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff);
++        if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
++            s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff);
++        break;
++    case 0x50:        /* RFBI_PARAM */
++        if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
++            s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff);
++        if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
++            s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff);
++        break;
++    case 0x54:        /* RFBI_DATA */
++        /* TODO: take into account the format set up in s->rfbi.config[?] and
++         * s->rfbi.data[?], but special-case the most usual scenario so that
++         * speed doesn't suffer.  */
++        if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) {
++            s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff);
++            s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16);
++        }
++        if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) {
++            s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff);
++            s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16);
++        }
++        if (!-- s->rfbi.pixels)
++            omap_rfbi_transfer_stop(s);
++        break;
++    case 0x58:        /* RFBI_READ */
++        if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
++            s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1);
++        else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
++            s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1);
++        if (!-- s->rfbi.pixels)
++            omap_rfbi_transfer_stop(s);
++        break;
++
++    case 0x5c:        /* RFBI_STATUS */
++        if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
++            s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0);
++        else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
++            s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0);
++        if (!-- s->rfbi.pixels)
++            omap_rfbi_transfer_stop(s);
++        break;
++
++    case 0x60:        /* RFBI_CONFIG0 */
++        s->rfbi.config[0] = value & 0x003f1fff;
++        break;
++
++    case 0x64:        /* RFBI_ONOFF_TIME0 */
++        s->rfbi.time[0] = value & 0x3fffffff;
++        break;
++    case 0x68:        /* RFBI_CYCLE_TIME0 */
++        s->rfbi.time[1] = value & 0x0fffffff;
++        break;
++    case 0x6c:        /* RFBI_DATA_CYCLE1_0 */
++        s->rfbi.data[0] = value & 0x0f1f0f1f;
++        break;
++    case 0x70:        /* RFBI_DATA_CYCLE2_0 */
++        s->rfbi.data[1] = value & 0x0f1f0f1f;
++        break;
++    case 0x74:        /* RFBI_DATA_CYCLE3_0 */
++        s->rfbi.data[2] = value & 0x0f1f0f1f;
++        break;
++    case 0x78:        /* RFBI_CONFIG1 */
++        s->rfbi.config[1] = value & 0x003f1fff;
++        break;
++
++    case 0x7c:        /* RFBI_ONOFF_TIME1 */
++        s->rfbi.time[2] = value & 0x3fffffff;
++        break;
++    case 0x80:        /* RFBI_CYCLE_TIME1 */
++        s->rfbi.time[3] = value & 0x0fffffff;
++        break;
++    case 0x84:        /* RFBI_DATA_CYCLE1_1 */
++        s->rfbi.data[3] = value & 0x0f1f0f1f;
++        break;
++    case 0x88:        /* RFBI_DATA_CYCLE2_1 */
++        s->rfbi.data[4] = value & 0x0f1f0f1f;
++        break;
++    case 0x8c:        /* RFBI_DATA_CYCLE3_1 */
++        s->rfbi.data[5] = value & 0x0f1f0f1f;
++        break;
++
++    case 0x90:        /* RFBI_VSYNC_WIDTH */
++        s->rfbi.vsync = value & 0xffff;
++        break;
++    case 0x94:        /* RFBI_HSYNC_WIDTH */
++        s->rfbi.hsync = value & 0xffff;
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_rfbi1_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_rfbi_read,
++};
++
++static CPUWriteMemoryFunc *omap_rfbi1_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_rfbi_write,
++};
++
++static uint32_t omap_venc_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->venc_base;
++
++    switch (offset) {
++    case 0x00:        /* REV_ID */
++    case 0x04:        /* STATUS */
++    case 0x08:        /* F_CONTROL */
++    case 0x10:        /* VIDOUT_CTRL */
++    case 0x14:        /* SYNC_CTRL */
++    case 0x1c:        /* LLEN */
++    case 0x20:        /* FLENS */
++    case 0x24:        /* HFLTR_CTRL */
++    case 0x28:        /* CC_CARR_WSS_CARR */
++    case 0x2c:        /* C_PHASE */
++    case 0x30:        /* GAIN_U */
++    case 0x34:        /* GAIN_V */
++    case 0x38:        /* GAIN_Y */
++    case 0x3c:        /* BLACK_LEVEL */
++    case 0x40:        /* BLANK_LEVEL */
++    case 0x44:        /* X_COLOR */
++    case 0x48:        /* M_CONTROL */
++    case 0x4c:        /* BSTAMP_WSS_DATA */
++    case 0x50:        /* S_CARR */
++    case 0x54:        /* LINE21 */
++    case 0x58:        /* LN_SEL */
++    case 0x5c:        /* L21__WC_CTL */
++    case 0x60:        /* HTRIGGER_VTRIGGER */
++    case 0x64:        /* SAVID__EAVID */
++    case 0x68:        /* FLEN__FAL */
++    case 0x6c:        /* LAL__PHASE_RESET */
++    case 0x70:        /* HS_INT_START_STOP_X */
++    case 0x74:        /* HS_EXT_START_STOP_X */
++    case 0x78:        /* VS_INT_START_X */
++    case 0x7c:        /* VS_INT_STOP_X__VS_INT_START_Y */
++    case 0x80:        /* VS_INT_STOP_Y__VS_INT_START_X */
++    case 0x84:        /* VS_EXT_STOP_X__VS_EXT_START_Y */
++    case 0x88:        /* VS_EXT_STOP_Y */
++    case 0x90:        /* AVID_START_STOP_X */
++    case 0x94:        /* AVID_START_STOP_Y */
++    case 0xa0:        /* FID_INT_START_X__FID_INT_START_Y */
++    case 0xa4:        /* FID_INT_OFFSET_Y__FID_EXT_START_X */
++    case 0xa8:        /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */
++    case 0xb0:        /* TVDETGP_INT_START_STOP_X */
++    case 0xb4:        /* TVDETGP_INT_START_STOP_Y */
++    case 0xb8:        /* GEN_CTRL */
++    case 0xc4:        /* DAC_TST__DAC_A */
++    case 0xc8:        /* DAC_B__DAC_C */
++        return 0;
++
++    default:
++        break;
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_venc_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->venc_base;
++
++    switch (offset) {
++    case 0x08:        /* F_CONTROL */
++    case 0x10:        /* VIDOUT_CTRL */
++    case 0x14:        /* SYNC_CTRL */
++    case 0x1c:        /* LLEN */
++    case 0x20:        /* FLENS */
++    case 0x24:        /* HFLTR_CTRL */
++    case 0x28:        /* CC_CARR_WSS_CARR */
++    case 0x2c:        /* C_PHASE */
++    case 0x30:        /* GAIN_U */
++    case 0x34:        /* GAIN_V */
++    case 0x38:        /* GAIN_Y */
++    case 0x3c:        /* BLACK_LEVEL */
++    case 0x40:        /* BLANK_LEVEL */
++    case 0x44:        /* X_COLOR */
++    case 0x48:        /* M_CONTROL */
++    case 0x4c:        /* BSTAMP_WSS_DATA */
++    case 0x50:        /* S_CARR */
++    case 0x54:        /* LINE21 */
++    case 0x58:        /* LN_SEL */
++    case 0x5c:        /* L21__WC_CTL */
++    case 0x60:        /* HTRIGGER_VTRIGGER */
++    case 0x64:        /* SAVID__EAVID */
++    case 0x68:        /* FLEN__FAL */
++    case 0x6c:        /* LAL__PHASE_RESET */
++    case 0x70:        /* HS_INT_START_STOP_X */
++    case 0x74:        /* HS_EXT_START_STOP_X */
++    case 0x78:        /* VS_INT_START_X */
++    case 0x7c:        /* VS_INT_STOP_X__VS_INT_START_Y */
++    case 0x80:        /* VS_INT_STOP_Y__VS_INT_START_X */
++    case 0x84:        /* VS_EXT_STOP_X__VS_EXT_START_Y */
++    case 0x88:        /* VS_EXT_STOP_Y */
++    case 0x90:        /* AVID_START_STOP_X */
++    case 0x94:        /* AVID_START_STOP_Y */
++    case 0xa0:        /* FID_INT_START_X__FID_INT_START_Y */
++    case 0xa4:        /* FID_INT_OFFSET_Y__FID_EXT_START_X */
++    case 0xa8:        /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */
++    case 0xb0:        /* TVDETGP_INT_START_STOP_X */
++    case 0xb4:        /* TVDETGP_INT_START_STOP_Y */
++    case 0xb8:        /* GEN_CTRL */
++    case 0xc4:        /* DAC_TST__DAC_A */
++    case 0xc8:        /* DAC_B__DAC_C */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_venc1_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_venc_read,
++};
++
++static CPUWriteMemoryFunc *omap_venc1_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_venc_write,
++};
++
++static uint32_t omap_im3_read(void *opaque, target_phys_addr_t addr)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->im3_base;
++
++    switch (offset) {
++    case 0x0a8:       /* SBIMERRLOGA */
++    case 0x0b0:       /* SBIMERRLOG */
++    case 0x190:       /* SBIMSTATE */
++    case 0x198:       /* SBTMSTATE_L */
++    case 0x19c:       /* SBTMSTATE_H */
++    case 0x1a8:       /* SBIMCONFIG_L */
++    case 0x1ac:       /* SBIMCONFIG_H */
++    case 0x1f8:       /* SBID_L */
++    case 0x1fc:       /* SBID_H */
++        return 0;
++
++    default:
++        break;
++    }
++    OMAP_BAD_REG(addr);
++    return 0;
++}
++
++static void omap_im3_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct omap_dss_s *s = (struct omap_dss_s *) opaque;
++    int offset = addr - s->im3_base;
++
++    switch (offset) {
++    case 0x0b0:       /* SBIMERRLOG */
++    case 0x190:       /* SBIMSTATE */
++    case 0x198:       /* SBTMSTATE_L */
++    case 0x19c:       /* SBTMSTATE_H */
++    case 0x1a8:       /* SBIMCONFIG_L */
++    case 0x1ac:       /* SBIMCONFIG_H */
++        break;
++
++    default:
++        OMAP_BAD_REG(addr);
++    }
++}
++
++static CPUReadMemoryFunc *omap_im3_readfn[] = {
++    omap_badwidth_read32,
++    omap_badwidth_read32,
++    omap_im3_read,
++};
++
++static CPUWriteMemoryFunc *omap_im3_writefn[] = {
++    omap_badwidth_write32,
++    omap_badwidth_write32,
++    omap_im3_write,
++};
++
++struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
++                target_phys_addr_t l3_base, DisplayState *ds,
++                qemu_irq irq, qemu_irq drq,
++                omap_clk fck1, omap_clk fck2, omap_clk ck54m,
++                omap_clk ick1, omap_clk ick2)
++{
++    int iomemtype[5];
++    struct omap_dss_s *s = (struct omap_dss_s *)
++            qemu_mallocz(sizeof(struct omap_dss_s));
++
++    s->irq = irq;
++    s->drq = drq;
++    s->state = ds;
++    omap_dss_reset(s);
++
++    iomemtype[0] = cpu_register_io_memory(0, omap_diss1_readfn,
++                    omap_diss1_writefn, s);
++    iomemtype[1] = cpu_register_io_memory(0, omap_disc1_readfn,
++                    omap_disc1_writefn, s);
++    iomemtype[2] = cpu_register_io_memory(0, omap_rfbi1_readfn,
++                    omap_rfbi1_writefn, s);
++    iomemtype[3] = cpu_register_io_memory(0, omap_venc1_readfn,
++                    omap_venc1_writefn, s);
++    iomemtype[4] = cpu_register_io_memory(0, omap_im3_readfn,
++                    omap_im3_writefn, s);
++    s->diss_base = omap_l4_attach(ta, 0, iomemtype[0]);
++    s->disc_base = omap_l4_attach(ta, 1, iomemtype[1]);
++    s->rfbi_base = omap_l4_attach(ta, 2, iomemtype[2]);
++    s->venc_base = omap_l4_attach(ta, 3, iomemtype[3]);
++    s->im3_base = l3_base;
++    cpu_register_physical_memory(s->im3_base, 0x1000, iomemtype[4]);
++
++#if 0
++    if (ds)
++        graphic_console_init(ds, omap_update_display,
++                        omap_invalidate_display, omap_screen_dump, s);
++#endif
++
++    return s;
++}
++
++void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip)
++{
++    if (cs < 0 || cs > 1)
++        cpu_abort(cpu_single_env, "%s: wrong CS %i\n", __FUNCTION__, cs);
++    s->rfbi.chip[cs] = chip;
++}
+diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c
+index de63309..9915676 100644
+--- a/hw/omap_i2c.c
++++ b/hw/omap_i2c.c
+@@ -150,6 +150,8 @@ static void omap_i2c_fifo_run(struct omap_i2c_s *s)
+             }
+             if (ack && s->count_cur)
+                 s->stat |= 1 << 4;                            /* XRDY */
++            else
++                s->stat &= ~(1 << 4);                         /* XRDY */
+             if (!s->count_cur) {
+                 s->stat |= 1 << 2;                            /* ARDY */
+                 s->control &= ~(1 << 10);                     /* MST */
+@@ -161,6 +163,8 @@ static void omap_i2c_fifo_run(struct omap_i2c_s *s)
+             }
+             if (s->rxlen)
+                 s->stat |= 1 << 3;                            /* RRDY */
++            else
++                s->stat &= ~(1 << 3);                         /* RRDY */
+         }
+         if (!s->count_cur) {
+             if ((s->control >> 1) & 1) {                      /* STP */
+@@ -321,7 +325,8 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
+             return;
+         }
+-        s->stat &= ~(value & 0x3f);
++        /* RRDY and XRDY are reset by hardware. (in all versions???) */
++        s->stat &= ~(value & 0x27);
+         omap_i2c_interrupts_update(s);
+         break;
+@@ -376,11 +381,13 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
+             break;
+         }
+         if ((value & (1 << 15)) && !(value & (1 << 10))) {    /* MST */
+-            printf("%s: I^2C slave mode not supported\n", __FUNCTION__);
++            fprintf(stderr, "%s: I^2C slave mode not supported\n",
++                            __FUNCTION__);
+             break;
+         }
+         if ((value & (1 << 15)) && value & (1 << 8)) {                /* XA */
+-            printf("%s: 10-bit addressing mode not supported\n", __FUNCTION__);
++            fprintf(stderr, "%s: 10-bit addressing mode not supported\n",
++                            __FUNCTION__);
+             break;
+         }
+         if ((value & (1 << 15)) && value & (1 << 0)) {                /* STT */
+@@ -427,7 +434,7 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
+                 omap_i2c_interrupts_update(s);
+             }
+         if (value & (1 << 15))                                        /* ST_EN */
+-            printf("%s: System Test not supported\n", __FUNCTION__);
++            fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__);
+         break;
+     default:
+diff --git a/hw/omap_mmc.c b/hw/omap_mmc.c
+index 6fbbb84..e46289a 100644
+--- a/hw/omap_mmc.c
++++ b/hw/omap_mmc.c
+@@ -26,19 +26,24 @@ struct omap_mmc_s {
+     target_phys_addr_t base;
+     qemu_irq irq;
+     qemu_irq *dma;
++    qemu_irq coverswitch;
+     omap_clk clk;
+     SDState *card;
+     uint16_t last_cmd;
+     uint16_t sdio;
+     uint16_t rsp[8];
+     uint32_t arg;
++    int lines;
+     int dw;
+     int mode;
+     int enable;
++    int be;
++    int rev;
+     uint16_t status;
+     uint16_t mask;
+     uint8_t cto;
+     uint16_t dto;
++    int clkdiv;
+     uint16_t fifo[32];
+     int fifo_start;
+     int fifo_len;
+@@ -53,6 +58,11 @@ struct omap_mmc_s {
+     int ddir;
+     int transfer;
++
++    int cdet_wakeup;
++    int cdet_enable;
++    int cdet_state;
++    qemu_irq cdet;
+ };
+ static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
+@@ -107,6 +117,11 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
+     struct sd_request_s request;
+     uint8_t response[16];
++    if (init && cmd == 0) {
++        host->status |= 0x0001;
++        return;
++    }
++
+     if (resptype == sd_r1 && busy)
+         resptype = sd_r1b;
+@@ -265,6 +280,34 @@ static void omap_mmc_update(void *opaque)
+     omap_mmc_interrupts_update(s);
+ }
++void omap_mmc_reset(struct omap_mmc_s *host)
++{
++    host->last_cmd = 0;
++    memset(host->rsp, 0, sizeof(host->rsp));
++    host->arg = 0;
++    host->dw = 0;
++    host->mode = 0;
++    host->enable = 0;
++    host->status = 0;
++    host->mask = 0;
++    host->cto = 0;
++    host->dto = 0;
++    host->fifo_len = 0;
++    host->blen = 0;
++    host->blen_counter = 0;
++    host->nblk = 0;
++    host->nblk_counter = 0;
++    host->tx_dma = 0;
++    host->rx_dma = 0;
++    host->ae_level = 0x00;
++    host->af_level = 0x1f;
++    host->transfer = 0;
++    host->cdet_wakeup = 0;
++    host->cdet_enable = 0;
++    qemu_set_irq(host->coverswitch, host->cdet_state);
++    host->clkdiv = 0;
++}
++
+ static uint32_t omap_mmc_read(void *opaque, target_phys_addr_t offset)
+ {
+     uint16_t i;
+@@ -282,7 +325,8 @@ static uint32_t omap_mmc_read(void *opaque, target_phys_addr_t offset)
+         return s->arg >> 16;
+     case 0x0c:        /* MMC_CON */
+-        return (s->dw << 15) | (s->mode << 12) | (s->enable << 11);
++        return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | 
++                (s->be << 10) | s->clkdiv;
+     case 0x10:        /* MMC_STAT */
+         return s->status;
+@@ -324,12 +368,12 @@ static uint32_t omap_mmc_read(void *opaque, target_phys_addr_t offset)
+     case 0x30:        /* MMC_SPI */
+         return 0x0000;
+     case 0x34:        /* MMC_SDIO */
+-        return s->sdio;
++        return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
+     case 0x38:        /* MMC_SYST */
+         return 0x0000;
+     case 0x3c:        /* MMC_REV */
+-        return 0x0001;
++        return s->rev;
+     case 0x40:        /* MMC_RSP0 */
+     case 0x44:        /* MMC_RSP1 */
+@@ -340,6 +384,13 @@ static uint32_t omap_mmc_read(void *opaque, target_phys_addr_t offset)
+     case 0x58:        /* MMC_RSP6 */
+     case 0x5c:        /* MMC_RSP7 */
+         return s->rsp[(offset - 0x40) >> 2];
++
++    /* OMAP2-specific */
++    case 0x60:        /* MMC_IOSR */
++    case 0x64:        /* MMC_SYSC */
++        return 0;
++    case 0x68:        /* MMC_SYSS */
++        return 1;                                             /* RSTD */
+     }
+     OMAP_BAD_REG(offset);
+@@ -383,10 +434,16 @@ static void omap_mmc_write(void *opaque, target_phys_addr_t offset,
+         s->dw = (value >> 15) & 1;
+         s->mode = (value >> 12) & 3;
+         s->enable = (value >> 11) & 1;
++        s->be = (value >> 10) & 1;
++        s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff);
+         if (s->mode != 0)
+             printf("SD mode %i unimplemented!\n", s->mode);
+-        if (s->dw != 0)
++        if (s->be != 0)
++            printf("SD FIFO byte sex unimplemented!\n");
++        if (s->dw != 0 && s->lines < 4)
+             printf("4-bit SD bus enabled\n");
++        if (!s->enable)
++            omap_mmc_reset(s);
+         break;
+     case 0x10:        /* MMC_STAT */
+@@ -395,13 +452,13 @@ static void omap_mmc_write(void *opaque, target_phys_addr_t offset,
+         break;
+     case 0x14:        /* MMC_IE */
+-        s->mask = value;
++        s->mask = value & 0x7fff;
+         omap_mmc_interrupts_update(s);
+         break;
+     case 0x18:        /* MMC_CTO */
+         s->cto = value & 0xff;
+-        if (s->cto > 0xfd)
++        if (s->cto > 0xfd && s->rev <= 1)
+             printf("MMC: CTO of 0xff and 0xfe cannot be used!\n");
+         break;
+@@ -446,10 +503,12 @@ static void omap_mmc_write(void *opaque, target_phys_addr_t offset,
+         break;
+     /* SPI, SDIO and TEST modes unimplemented */
+-    case 0x30:        /* MMC_SPI */
++    case 0x30:        /* MMC_SPI (OMAP1 only) */
+         break;
+     case 0x34:        /* MMC_SDIO */
+-        s->sdio = value & 0x2020;
++        s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020);
++        s->cdet_wakeup = (value >> 9) & 1;
++        s->cdet_enable = (value >> 2) & 1;
+         break;
+     case 0x38:        /* MMC_SYST */
+         break;
+@@ -466,6 +525,19 @@ static void omap_mmc_write(void *opaque, target_phys_addr_t offset,
+         OMAP_RO_REG(offset);
+         break;
++    /* OMAP2-specific */
++    case 0x60:        /* MMC_IOSR */
++        if (value & 0xf)
++            printf("MMC: SDIO bits used!\n");
++        break;
++    case 0x64:        /* MMC_SYSC */
++        if (value & (1 << 2))                                 /* SRTS */
++            omap_mmc_reset(s);
++        break;
++    case 0x68:        /* MMC_SYSS */
++        OMAP_RO_REG(offset);
++        break;
++
+     default:
+         OMAP_BAD_REG(offset);
+     }
+@@ -483,28 +555,21 @@ static CPUWriteMemoryFunc *omap_mmc_writefn[] = {
+     omap_badwidth_write16,
+ };
+-void omap_mmc_reset(struct omap_mmc_s *host)
++static void omap_mmc_cover_cb(void *opaque, int line, int level)
+ {
+-    host->last_cmd = 0;
+-    memset(host->rsp, 0, sizeof(host->rsp));
+-    host->arg = 0;
+-    host->dw = 0;
+-    host->mode = 0;
+-    host->enable = 0;
+-    host->status = 0;
+-    host->mask = 0;
+-    host->cto = 0;
+-    host->dto = 0;
+-    host->fifo_len = 0;
+-    host->blen = 0;
+-    host->blen_counter = 0;
+-    host->nblk = 0;
+-    host->nblk_counter = 0;
+-    host->tx_dma = 0;
+-    host->rx_dma = 0;
+-    host->ae_level = 0x00;
+-    host->af_level = 0x1f;
+-    host->transfer = 0;
++    struct omap_mmc_s *host = (struct omap_mmc_s *) opaque;
++
++    if (!host->cdet_state && level) {
++        host->status |= 0x0002;
++        omap_mmc_interrupts_update(host);
++        if (host->cdet_wakeup)
++            /* TODO: Assert wake-up */;
++    }
++
++    if (host->cdet_state != level) {
++        qemu_set_irq(host->coverswitch, level);
++        host->cdet_state = level;
++    }
+ }
+ struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+@@ -519,6 +584,10 @@ struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+     s->base = base;
+     s->dma = dma;
+     s->clk = clk;
++    s->lines = 1;     /* TODO: needs to be settable per-board */
++    s->rev = 1;
++
++    omap_mmc_reset(s);
+     iomemtype = cpu_register_io_memory(0, omap_mmc_readfn,
+                     omap_mmc_writefn, s);
+@@ -530,7 +599,46 @@ struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+     return s;
+ }
++struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
++                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
++                omap_clk fclk, omap_clk iclk)
++{
++    int iomemtype;
++    struct omap_mmc_s *s = (struct omap_mmc_s *)
++            qemu_mallocz(sizeof(struct omap_mmc_s));
++
++    s->irq = irq;
++    s->dma = dma;
++    s->clk = fclk;
++    s->lines = 4;
++    s->rev = 2;
++
++    omap_mmc_reset(s);
++
++    iomemtype = cpu_register_io_memory(0, omap_mmc_readfn,
++                    omap_mmc_writefn, s);
++    s->base = omap_l4_attach(ta, 0, iomemtype);
++
++    /* Instantiate the storage */
++    s->card = sd_init(bd, 0);
++
++    s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
++    sd_set_cb(s->card, 0, s->cdet);
++
++    return s;
++}
++
+ void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover)
+ {
+-    sd_set_cb(s->card, ro, cover);
++    if (s->cdet) {
++        sd_set_cb(s->card, ro, s->cdet);
++        s->coverswitch = cover;
++        qemu_set_irq(cover, s->cdet_state);
++    } else
++        sd_set_cb(s->card, ro, cover);
++}
++
++void omap_mmc_enable(struct omap_mmc_s *s, int enable)
++{
++    sd_enable(s->card, enable);
+ }
+diff --git a/hw/onenand.c b/hw/onenand.c
+new file mode 100644
+index 0000000..549d392
+--- /dev/null
++++ b/hw/onenand.c
+@@ -0,0 +1,642 @@
++/*
++ * OneNAND flash memories emulation.
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "qemu-common.h"
++#include "flash.h"
++#include "irq.h"
++#include "sysemu.h"
++#include "block.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)
++
++struct onenand_s {
++    uint32_t id;
++    int shift;
++    target_phys_addr_t base;
++    qemu_irq intr;
++    qemu_irq rdy;
++    BlockDriverState *bdrv;
++    BlockDriverState *bdrv_cur;
++    uint8_t *image;
++    uint8_t *otp;
++    uint8_t *current;
++    ram_addr_t ram;
++    uint8_t *boot[2];
++    uint8_t *data[2][2];
++    int iomemtype;
++    int cycle;
++    int otpmode;
++
++    uint16_t addr[8];
++    uint16_t unladdr[8];
++    int bufaddr;
++    int count;
++    uint16_t command;
++    uint16_t config[2];
++    uint16_t status;
++    uint16_t intstatus;
++    uint16_t wpstatus;
++
++    struct ecc_state_s ecc;
++
++    int density_mask;
++    int secs;
++    int secs_cur;
++    int blocks;
++    uint8_t *blockwp;
++};
++
++enum {
++    ONEN_BUF_BLOCK = 0,
++    ONEN_BUF_BLOCK2 = 1,
++    ONEN_BUF_DEST_BLOCK = 2,
++    ONEN_BUF_DEST_PAGE = 3,
++    ONEN_BUF_PAGE = 7,
++};
++
++enum {
++    ONEN_ERR_CMD = 1 << 10,
++    ONEN_ERR_ERASE = 1 << 11,
++    ONEN_ERR_PROG = 1 << 12,
++    ONEN_ERR_LOAD = 1 << 13,
++};
++
++enum {
++    ONEN_INT_RESET = 1 << 4,
++    ONEN_INT_ERASE = 1 << 5,
++    ONEN_INT_PROG = 1 << 6,
++    ONEN_INT_LOAD = 1 << 7,
++    ONEN_INT = 1 << 15,
++};
++
++enum {
++    ONEN_LOCK_LOCKTIGHTEN = 1 << 0,
++    ONEN_LOCK_LOCKED = 1 << 1,
++    ONEN_LOCK_UNLOCKED = 1 << 2,
++};
++
++void onenand_base_update(void *opaque, target_phys_addr_t new)
++{
++    struct onenand_s *s = (struct onenand_s *) opaque;
++
++    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 + (0xc000 << s->shift),
++                        0x4000 << s->shift, s->iomemtype);
++}
++
++void onenand_base_unmap(void *opaque)
++{
++    struct onenand_s *s = (struct onenand_s *) opaque;
++
++    cpu_register_physical_memory(s->base,
++                    0x10000 << s->shift, IO_MEM_UNASSIGNED);
++}
++
++static void onenand_intr_update(struct onenand_s *s)
++{
++    qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1);
++}
++
++/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */
++static void onenand_reset(struct onenand_s *s, int cold)
++{
++    memset(&s->addr, 0, sizeof(s->addr));
++    s->command = 0;
++    s->count = 1;
++    s->bufaddr = 0;
++    s->config[0] = 0x40c0;
++    s->config[1] = 0x0000;
++    onenand_intr_update(s);
++    qemu_irq_raise(s->rdy);
++    s->status = 0x0000;
++    s->intstatus = cold ? 0x8080 : 0x8010;
++    s->unladdr[0] = 0;
++    s->unladdr[1] = 0;
++    s->wpstatus = 0x0002;
++    s->cycle = 0;
++    s->otpmode = 0;
++    s->bdrv_cur = s->bdrv;
++    s->current = s->image;
++    s->secs_cur = s->secs;
++
++    if (cold) {
++        /* Lock the whole flash */
++        memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);
++
++        if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0)
++            cpu_abort(cpu_single_env, "%s: Loading the BootRAM failed.\n",
++                            __FUNCTION__);
++    }
++}
++
++static inline int onenand_load_main(struct onenand_s *s, int sec, int secn,
++                void *dest)
++{
++    if (s->bdrv_cur)
++        return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0;
++    else if (sec + secn > s->secs_cur)
++        return 1;
++
++    memcpy(dest, s->current + (sec << 9), secn << 9);
++
++    return 0;
++}
++
++static inline int onenand_prog_main(struct onenand_s *s, int sec, int secn,
++                void *src)
++{
++    if (s->bdrv_cur)
++        return bdrv_write(s->bdrv_cur, sec, src, secn) < 0;
++    else if (sec + secn > s->secs_cur)
++        return 1;
++
++    memcpy(s->current + (sec << 9), src, secn << 9);
++
++    return 0;
++}
++
++static inline int onenand_load_spare(struct onenand_s *s, int sec, int secn,
++                void *dest)
++{
++    uint8_t buf[512];
++
++    if (s->bdrv_cur) {
++        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)
++        return 1;
++    else
++        memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
++ 
++    return 0;
++}
++
++static inline int onenand_prog_spare(struct onenand_s *s, int sec, int secn,
++                void *src)
++{
++    uint8_t buf[512];
++
++    if (s->bdrv_cur) {
++        if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 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)
++        return 1;
++
++    memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
++ 
++    return 0;
++}
++
++static inline int onenand_erase(struct onenand_s *s, int sec, int num)
++{
++    /* TODO: optimise */
++    uint8_t buf[512];
++
++    memset(buf, 0xff, sizeof(buf));
++    for (; num > 0; num --, sec ++) {
++        if (onenand_prog_main(s, sec, 1, buf))
++            return 1;
++        if (onenand_prog_spare(s, sec, 1, buf))
++            return 1;
++    }
++
++    return 0;
++}
++
++static void onenand_command(struct onenand_s *s, int cmd)
++{
++    int b;
++    int sec;
++    void *buf;
++#define SETADDR(block, page)                  \
++    sec = (s->addr[page] & 3) +                       \
++            ((((s->addr[page] >> 2) & 0x3f) + \
++              (((s->addr[block] & 0xfff) |    \
++                (s->addr[block] >> 15 ?               \
++                 s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
++#define SETBUF_M()                            \
++    buf = (s->bufaddr & 8) ?                  \
++            s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0];   \
++    buf += (s->bufaddr & 3) << 9;
++#define SETBUF_S()                            \
++    buf = (s->bufaddr & 8) ?                  \
++            s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1];   \
++    buf += (s->bufaddr & 3) << 4;
++
++    switch (cmd) {
++    case 0x00:        /* Load single/multiple sector data unit into buffer */
++        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++
++        SETBUF_M()
++        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
++
++        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
++         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
++         * then we need two split the read/write into two chunks.
++         */
++        s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
++        break;
++    case 0x13:        /* Load single/multiple spare sector into buffer */
++        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++
++        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)
++         * then we need two split the read/write into two chunks.
++         */
++        s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
++        break;
++    case 0x80:        /* Program single/multiple sector data unit from buffer */
++        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++
++        SETBUF_M()
++        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
++
++        /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
++         * or    if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
++         * then we need two split the read/write into two chunks.
++         */
++        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
++        break;
++    case 0x1a:        /* Program single/multiple spare area sector from buffer */
++        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++
++        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)
++         * then we need two split the read/write into two chunks.
++         */
++        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
++        break;
++    case 0x1b:        /* Copy-back program */
++        SETBUF_S()
++
++        SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++        if (onenand_load_main(s, sec, s->count, buf))
++            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
++
++        SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE)
++        if (onenand_prog_main(s, sec, s->count, buf))
++            s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
++
++        /* TODO: spare areas */
++
++        s->intstatus |= ONEN_INT | ONEN_INT_PROG;
++        break;
++
++    case 0x23:        /* Unlock NAND array block(s) */
++        s->intstatus |= ONEN_INT;
++
++        /* XXX the previous (?) area should be locked automatically */
++        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
++            if (b >= s->blocks) {
++                s->status |= ONEN_ERR_CMD;
++                break;
++            }
++            if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
++                break;
++
++            s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
++        }
++        break;
++    case 0x2a:        /* Lock NAND array block(s) */
++        s->intstatus |= ONEN_INT;
++
++        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
++            if (b >= s->blocks) {
++                s->status |= ONEN_ERR_CMD;
++                break;
++            }
++            if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
++                break;
++
++            s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED;
++        }
++        break;
++    case 0x2c:        /* Lock-tight NAND array block(s) */
++        s->intstatus |= ONEN_INT;
++
++        for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
++            if (b >= s->blocks) {
++                s->status |= ONEN_ERR_CMD;
++                break;
++            }
++            if (s->blockwp[b] == ONEN_LOCK_UNLOCKED)
++                continue;
++
++            s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN;
++        }
++        break;
++
++    case 0x71:        /* Erase-Verify-Read */
++        s->intstatus |= ONEN_INT;
++        break;
++    case 0x95:        /* Multi-block erase */
++        qemu_irq_pulse(s->intr);
++        /* Fall through.  */
++    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->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;
++
++        s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
++        break;
++    case 0xb0:        /* Erase suspend */
++        break;
++    case 0x30:        /* Erase resume */
++        s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
++        break;
++
++    case 0xf0:        /* Reset NAND Flash core */
++        onenand_reset(s, 0);
++        break;
++    case 0xf3:        /* Reset OneNAND */
++        onenand_reset(s, 0);
++        break;
++
++    case 0x65:        /* OTP Access */
++        s->intstatus |= ONEN_INT;
++        s->bdrv_cur = 0;
++        s->current = s->otp;
++        s->secs_cur = 1 << (BLOCK_SHIFT - 9);
++        s->addr[ONEN_BUF_BLOCK] = 0;
++        s->otpmode = 1;
++        break;
++
++    default:
++        s->status |= ONEN_ERR_CMD;
++        s->intstatus |= ONEN_INT;
++        fprintf(stderr, "%s: unknown OneNAND command %x\n",
++                        __FUNCTION__, cmd);
++    }
++
++    onenand_intr_update(s);
++}
++
++static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
++{
++    struct onenand_s *s = (struct onenand_s *) opaque;
++    int offset = (addr - s->base) >> s->shift;
++
++    switch (offset) {
++    case 0x0000 ... 0xc000:
++        return lduw_le_p(s->boot[0] + (addr - s->base));
++
++    case 0xf000:      /* Manufacturer ID */
++        return (s->id >> 16) & 0xff;
++    case 0xf001:      /* Device ID */
++        return (s->id >>  8) & 0xff;
++    /* TODO: get the following values from a real chip!  */
++    case 0xf002:      /* Version ID */
++        return (s->id >>  0) & 0xff;
++    case 0xf003:      /* Data Buffer size */
++        return 1 << PAGE_SHIFT;
++    case 0xf004:      /* Boot Buffer size */
++        return 0x200;
++    case 0xf005:      /* Amount of buffers */
++        return 1 | (2 << 8);
++    case 0xf006:      /* Technology */
++        return 0;
++
++    case 0xf100 ... 0xf107:   /* Start addresses */
++        return s->addr[offset - 0xf100];
++
++    case 0xf200:      /* Start buffer */
++        return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10)));
++
++    case 0xf220:      /* Command */
++        return s->command;
++    case 0xf221:      /* System Configuration 1 */
++        return s->config[0] & 0xffe0;
++    case 0xf222:      /* System Configuration 2 */
++        return s->config[1];
++
++    case 0xf240:      /* Controller Status */
++        return s->status;
++    case 0xf241:      /* Interrupt */
++        return s->intstatus;
++    case 0xf24c:      /* Unlock Start Block Address */
++        return s->unladdr[0];
++    case 0xf24d:      /* Unlock End Block Address */
++        return s->unladdr[1];
++    case 0xf24e:      /* Write Protection Status */
++        return s->wpstatus;
++
++    case 0xff00:      /* ECC Status */
++        return 0x00;
++    case 0xff01:      /* ECC Result of main area data */
++    case 0xff02:      /* ECC Result of spare area data */
++    case 0xff03:      /* ECC Result of main area data */
++    case 0xff04:      /* ECC Result of spare area data */
++        cpu_abort(cpu_single_env, "%s: imeplement ECC\n", __FUNCTION__);
++        return 0x0000;
++    }
++
++    fprintf(stderr, "%s: unknown OneNAND register %x\n",
++                    __FUNCTION__, offset);
++    return 0;
++}
++
++static void onenand_write(void *opaque, target_phys_addr_t addr,
++                uint32_t value)
++{
++    struct onenand_s *s = (struct onenand_s *) opaque;
++    int offset = (addr - s->base) >> s->shift;
++    int sec;
++
++    switch (offset) {
++    case 0x0000 ... 0x01ff:
++    case 0x8000 ... 0x800f:
++        if (s->cycle) {
++            s->cycle = 0;
++
++            if (value == 0x0000) {
++                SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
++                onenand_load_main(s, sec,
++                                1 << (PAGE_SHIFT - 9), s->data[0][0]);
++                s->addr[ONEN_BUF_PAGE] += 4;
++                s->addr[ONEN_BUF_PAGE] &= 0xff;
++            }
++            break;
++        }
++
++        switch (value) {
++        case 0x00f0:  /* Reset OneNAND */
++            onenand_reset(s, 0);
++            break;
++
++        case 0x00e0:  /* Load Data into Buffer */
++            s->cycle = 1;
++            break;
++
++        case 0x0090:  /* Read Identification Data */
++            memset(s->boot[0], 0, 3 << s->shift);
++            s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff;
++            s->boot[0][1 << s->shift] = (s->id >>  8) & 0xff;
++            s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
++            break;
++
++        default:
++            fprintf(stderr, "%s: unknown OneNAND boot command %x\n",
++                            __FUNCTION__, 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)
++            s->count = (value & 3) ?: 4;
++        else if (PAGE_SHIFT == 10)
++            s->count = (value & 1) ?: 2;
++        break;
++
++    case 0xf220:      /* Command */
++        if (s->intstatus & (1 << 15))
++            break;
++        s->command = value;
++        onenand_command(s, s->command);
++        break;
++    case 0xf221:      /* System Configuration 1 */
++        s->config[0] = value;
++        onenand_intr_update(s);
++        qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1);
++        break;
++    case 0xf222:      /* System Configuration 2 */
++        s->config[1] = value;
++        break;
++
++    case 0xf241:      /* Interrupt */
++        s->intstatus &= value;
++        if ((1 << 15) & ~s->intstatus)
++            s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE |
++                            ONEN_ERR_PROG | ONEN_ERR_LOAD);
++        onenand_intr_update(s);
++        break;
++    case 0xf24c:      /* Unlock Start Block Address */
++        s->unladdr[0] = value & (s->blocks - 1);
++        /* For some reason we have to set the end address to by default
++         * be same as start because the software forgets to write anything
++         * in there.  */
++        s->unladdr[1] = value & (s->blocks - 1);
++        break;
++    case 0xf24d:      /* Unlock End Block Address */
++        s->unladdr[1] = value & (s->blocks - 1);
++        break;
++
++    default:
++        fprintf(stderr, "%s: unknown OneNAND register %x\n",
++                        __FUNCTION__, offset);
++    }
++}
++
++static CPUReadMemoryFunc *onenand_readfn[] = {
++    onenand_read,     /* TODO */
++    onenand_read,
++    onenand_read,
++};
++
++static CPUWriteMemoryFunc *onenand_writefn[] = {
++    onenand_write,    /* TODO */
++    onenand_write,
++    onenand_write,
++};
++
++void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
++{
++    struct onenand_s *s = (struct onenand_s *) qemu_mallocz(sizeof(*s));
++    int bdrv_index = drive_get_index(IF_MTD, 0, 0);
++    uint32_t size = 1 << (24 + ((id >> 12) & 7));
++    void *ram;
++
++    s->shift = regshift;
++    s->intr = irq;
++    s->rdy = 0;
++    s->id = id;
++    s->blocks = size >> BLOCK_SHIFT;
++    s->secs = size >> 9;
++    s->blockwp = qemu_malloc(s->blocks);
++    s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0;
++    s->iomemtype = cpu_register_io_memory(0, onenand_readfn,
++                    onenand_writefn, s);
++    if (bdrv_index == -1)
++        s->image = memset(qemu_malloc(size + (size >> 5)),
++                        0xff, size + (size >> 5));
++    else
++        s->bdrv = drives_table[bdrv_index].bdrv;
++    s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
++                    0xff, (64 + 2) << PAGE_SHIFT);
++    s->ram = qemu_ram_alloc(0xc000 << s->shift);
++    ram = phys_ram_base + 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);
++
++    onenand_reset(s, 1);
++
++    return s;
++}
+diff --git a/hw/palm.c b/hw/palm.c
+index 9400ea7..8b767e2 100644
+--- a/hw/palm.c
++++ b/hw/palm.c
+@@ -25,6 +25,7 @@
+ #include "omap.h"
+ #include "boards.h"
+ #include "arm-misc.h"
++#include "devices.h"
+ static uint32_t static_readb(void *opaque, target_phys_addr_t offset)
+ {
+@@ -32,12 +33,14 @@ static uint32_t static_readb(void *opaque, target_phys_addr_t offset)
+     return *val >> ((offset & 3) << 3);
+ }
+-static uint32_t static_readh(void *opaque, target_phys_addr_t offset) {
++static uint32_t static_readh(void *opaque, target_phys_addr_t offset)
++{
+     uint32_t *val = (uint32_t *) opaque;
+     return *val >> ((offset & 1) << 3);
+ }
+-static uint32_t static_readw(void *opaque, target_phys_addr_t offset) {
++static uint32_t static_readw(void *opaque, target_phys_addr_t offset)
++{
+     uint32_t *val = (uint32_t *) opaque;
+     return *val >> ((offset & 0) << 3);
+ }
+@@ -183,6 +186,12 @@ static void palmte_gpio_setup(struct omap_mpu_state_s *cpu)
+     qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[11]);
+ }
++static struct arm_boot_info palmte_binfo = {
++    .loader_start = OMAP_EMIFF_BASE,
++    .ram_size = 0x02000000,
++    .board_id = 0x331,
++};
++
+ static void palmte_init(int ram_size, int vga_ram_size,
+                 const char *boot_device, DisplayState *ds,
+                 const char *kernel_filename, const char *kernel_cmdline,
+@@ -190,7 +199,7 @@ static void palmte_init(int ram_size, int vga_ram_size,
+ {
+     struct omap_mpu_state_s *cpu;
+     int flash_size = 0x00800000;
+-    int sdram_size = 0x02000000;
++    int sdram_size = palmte_binfo.ram_size;
+     int io;
+     static uint32_t cs0val = 0xffffffff;
+     static uint32_t cs1val = 0x0000e1a0;
+@@ -250,10 +259,12 @@ static void palmte_init(int ram_size, int vga_ram_size,
+     /* Load the kernel.  */
+     if (kernel_filename) {
+         /* Start at bootloader.  */
+-        cpu->env->regs[15] = OMAP_EMIFF_BASE;
++        cpu->env->regs[15] = palmte_binfo.loader_start;
+-        arm_load_kernel(cpu->env, sdram_size, kernel_filename, kernel_cmdline,
+-                        initrd_filename, 0x331, OMAP_EMIFF_BASE);
++        palmte_binfo.kernel_filename = kernel_filename;
++        palmte_binfo.kernel_cmdline = kernel_cmdline;
++        palmte_binfo.initrd_filename = initrd_filename;
++        arm_load_kernel(cpu->env, &palmte_binfo);
+     }
+     dpy_resize(ds, 320, 320);
+diff --git a/hw/realview.c b/hw/realview.c
+index 29579d8..acf3b9e 100644
+--- a/hw/realview.c
++++ b/hw/realview.c
+@@ -18,6 +18,11 @@
+ /* Board init.  */
++static struct arm_boot_info realview_binfo = {
++    .loader_start = 0x0,
++    .board_id = 0x33b,
++};
++
+ static void realview_init(int ram_size, int vga_ram_size,
+                      const char *boot_device, DisplayState *ds,
+                      const char *kernel_filename, const char *kernel_cmdline,
+@@ -177,8 +182,12 @@ static void realview_init(int ram_size, int vga_ram_size,
+     /* 0x68000000 PCI mem 1.  */
+     /* 0x6c000000 PCI mem 2.  */
+-    arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline,
+-                    initrd_filename, 0x33b, 0x0);
++    realview_binfo.ram_size = ram_size;
++    realview_binfo.kernel_filename = kernel_filename;
++    realview_binfo.kernel_cmdline = kernel_cmdline;
++    realview_binfo.initrd_filename = initrd_filename;
++    realview_binfo.nb_cpus = ncpu;
++    arm_load_kernel(first_cpu, &realview_binfo);
+     /* ??? Hack to map an additional page of ram for the secondary CPU
+        startup code.  I guess this works on real hardware because the
+diff --git a/hw/sd.c b/hw/sd.c
+index 1f71d85..de7dd89 100644
+--- a/hw/sd.c
++++ b/hw/sd.c
+@@ -37,7 +37,7 @@
+ #ifdef DEBUG_SD
+ #define DPRINTF(fmt, args...) \
+-do { printf("SD: " fmt , ##args); } while (0)
++do { fprintf(stderr, "SD: " fmt , ##args); } while (0)
+ #else
+ #define DPRINTF(fmt, args...) do {} while(0)
+ #endif
+@@ -99,6 +99,8 @@ struct SDState {
+     qemu_irq inserted_cb;
+     BlockDriverState *bdrv;
+     uint8_t *buf;
++
++    int enable;
+ };
+ static void sd_set_status(SDState *sd)
+@@ -530,7 +532,7 @@ static void sd_lock_command(SDState *sd)
+         sd->card_status &= ~CARD_IS_LOCKED;
+         sd->pwd_len = 0;
+         /* Erasing the entire card here! */
+-        printf("SD: Card force-erased by CMD42\n");
++        fprintf(stderr, "SD: Card force-erased by CMD42\n");
+         return;
+     }
+@@ -1076,7 +1078,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
+         return sd_r1;
+     case 56:  /* CMD56:  GEN_CMD */
+-        printf("SD: GEN_CMD 0x%08x\n", req.arg);
++        fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
+         switch (sd->state) {
+         case sd_transfer_state:
+@@ -1096,18 +1098,18 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
+     bad_cmd:
+         sd->card_status |= ILLEGAL_COMMAND;
+-        printf("SD: Unknown CMD%i\n", req.cmd);
++        fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
+         return sd_r0;
+     unimplemented_cmd:
+         /* Commands that are recognised but not yet implemented in SPI mode.  */
+         sd->card_status |= ILLEGAL_COMMAND;
+-        printf ("SD: CMD%i not implemented in SPI mode\n", req.cmd);
++        fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
+         return sd_r0;
+     }
+     sd->card_status |= ILLEGAL_COMMAND;
+-    printf("SD: CMD%i in a wrong state\n", req.cmd);
++    fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
+     return sd_r0;
+ }
+@@ -1217,7 +1219,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
+         return sd_normal_command(sd, req);
+     }
+-    printf("SD: ACMD%i in a wrong state\n", req.cmd);
++    fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
+     return sd_r0;
+ }
+@@ -1227,7 +1229,7 @@ int sd_do_command(SDState *sd, struct sd_request_s *req,
+     sd_rsp_type_t rtype;
+     int rsplen;
+-    if (!bdrv_is_inserted(sd->bdrv)) {
++    if (!bdrv_is_inserted(sd->bdrv) || !sd->enable) {
+         return 0;
+     }
+@@ -1247,7 +1249,7 @@ int sd_do_command(SDState *sd, struct sd_request_s *req,
+                           sd_cmd_class[req->cmd] == 7 ||
+                           req->cmd == 16 || req->cmd == 55))) {
+             sd->card_status |= ILLEGAL_COMMAND;
+-            printf("SD: Card is locked\n");
++            fprintf(stderr, "SD: Card is locked\n");
+             return 0;
+         }
+@@ -1321,7 +1323,7 @@ static void sd_blk_read(SDState *sd, uint32_t addr, uint32_t len)
+     uint32_t end = addr + len;
+     if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+-        printf("sd_blk_read: read error on host side\n");
++        fprintf(stderr, "sd_blk_read: read error on host side\n");
+         return;
+     }
+@@ -1329,7 +1331,7 @@ static void sd_blk_read(SDState *sd, uint32_t addr, uint32_t len)
+         memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
+         if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+-            printf("sd_blk_read: read error on host side\n");
++            fprintf(stderr, "sd_blk_read: read error on host side\n");
+             return;
+         }
+         memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511);
+@@ -1343,28 +1345,28 @@ static void sd_blk_write(SDState *sd, uint32_t addr, uint32_t len)
+     if ((addr & 511) || len < 512)
+         if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+-            printf("sd_blk_write: read error on host side\n");
++            fprintf(stderr, "sd_blk_write: read error on host side\n");
+             return;
+         }
+     if (end > (addr & ~511) + 512) {
+         memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
+         if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+-            printf("sd_blk_write: write error on host side\n");
++            fprintf(stderr, "sd_blk_write: write error on host side\n");
+             return;
+         }
+         if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+-            printf("sd_blk_write: read error on host side\n");
++            fprintf(stderr, "sd_blk_write: read error on host side\n");
+             return;
+         }
+         memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
+         if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) == -1)
+-            printf("sd_blk_write: write error on host side\n");
++            fprintf(stderr, "sd_blk_write: write error on host side\n");
+     } else {
+         memcpy(sd->buf + (addr & 511), sd->data, len);
+         if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1)
+-            printf("sd_blk_write: write error on host side\n");
++            fprintf(stderr, "sd_blk_write: write error on host side\n");
+     }
+ }
+@@ -1377,11 +1379,11 @@ void sd_write_data(SDState *sd, uint8_t value)
+ {
+     int i;
+-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
++    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+         return;
+     if (sd->state != sd_receivingdata_state) {
+-        printf("sd_write_data: not in Receiving-Data state\n");
++        fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
+         return;
+     }
+@@ -1489,7 +1491,7 @@ void sd_write_data(SDState *sd, uint8_t value)
+         break;
+     default:
+-        printf("sd_write_data: unknown command\n");
++        fprintf(stderr, "sd_write_data: unknown command\n");
+         break;
+     }
+ }
+@@ -1499,11 +1501,11 @@ uint8_t sd_read_data(SDState *sd)
+     /* TODO: Append CRCs */
+     uint8_t ret;
+-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
++    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+         return 0x00;
+     if (sd->state != sd_sendingdata_state) {
+-        printf("sd_read_data: not in Sending-Data state\n");
++        fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
+         return 0x00;
+     }
+@@ -1603,7 +1605,7 @@ uint8_t sd_read_data(SDState *sd)
+         break;
+     default:
+-        printf("sd_read_data: unknown command\n");
++        fprintf(stderr, "sd_read_data: unknown command\n");
+         return 0x00;
+     }
+@@ -1614,3 +1616,8 @@ int sd_data_ready(SDState *sd)
+ {
+     return sd->state == sd_sendingdata_state;
+ }
++
++void sd_enable(SDState *sd, int enable)
++{
++    sd->enable = enable;
++}
+diff --git a/hw/sd.h b/hw/sd.h
+index 85f110f..cb7bc9c 100644
+--- a/hw/sd.h
++++ b/hw/sd.h
+@@ -74,6 +74,7 @@ void sd_write_data(SDState *sd, uint8_t value);
+ uint8_t sd_read_data(SDState *sd);
+ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert);
+ int sd_data_ready(SDState *sd);
++void sd_enable(SDState *sd, int enable);
+ /* ssi-sd.c */
+ int ssi_sd_xfer(void *opaque, int val);
+diff --git a/hw/spitz.c b/hw/spitz.c
+index 159c633..b059f9a 100644
+--- a/hw/spitz.c
++++ b/hw/spitz.c
+@@ -1180,12 +1180,17 @@ static void sl_bootparam_write(uint32_t ptr)
+ /* Board init.  */
+ enum spitz_model_e { spitz, akita, borzoi, terrier };
++static struct arm_boot_info spitz_binfo = {
++    .loader_start = PXA2XX_SDRAM_BASE,
++    .ram_size = 0x04000000,
++};
++
+ static void spitz_common_init(int ram_size, int vga_ram_size,
+                 DisplayState *ds, const char *kernel_filename,
+                 const char *kernel_cmdline, const char *initrd_filename,
+                 const char *cpu_model, enum spitz_model_e model, int arm_id)
+ {
+-    uint32_t spitz_ram = 0x04000000;
++    uint32_t spitz_ram = spitz_binfo.ram_size;
+     uint32_t spitz_rom = 0x00800000;
+     struct pxa2xx_state_s *cpu;
+     struct scoop_info_s *scp;
+@@ -1230,10 +1235,13 @@ static void spitz_common_init(int ram_size, int vga_ram_size,
+         spitz_microdrive_attach(cpu);
+     /* Setup initial (reset) machine state */
+-    cpu->env->regs[15] = PXA2XX_SDRAM_BASE;
++    cpu->env->regs[15] = spitz_binfo.loader_start;
+-    arm_load_kernel(cpu->env, spitz_ram, kernel_filename, kernel_cmdline,
+-                    initrd_filename, arm_id, PXA2XX_SDRAM_BASE);
++    spitz_binfo.kernel_filename = kernel_filename;
++    spitz_binfo.kernel_cmdline = kernel_cmdline;
++    spitz_binfo.initrd_filename = initrd_filename;
++    spitz_binfo.board_id = arm_id;
++    arm_load_kernel(cpu->env, &spitz_binfo);
+     sl_bootparam_write(SL_PXA_PARAM_BASE - PXA2XX_SDRAM_BASE);
+ }
+diff --git a/hw/tmp105.c b/hw/tmp105.c
+new file mode 100644
+index 0000000..d9a3900
+--- /dev/null
++++ b/hw/tmp105.c
+@@ -0,0 +1,249 @@
++/*
++ * Texas Instruments TMP105 temperature sensor.
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "hw.h"
++#include "i2c.h"
++
++struct tmp105_s {
++    i2c_slave i2c;
++    int len;
++    uint8_t buf[2];
++    qemu_irq pin;
++
++    uint8_t pointer;
++    uint8_t config;
++    int16_t temperature;
++    int16_t limit[2];
++    int faults;
++    int alarm;
++};
++
++static void tmp105_interrupt_update(struct tmp105_s *s)
++{
++    qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */
++}
++
++static void tmp105_alarm_update(struct tmp105_s *s)
++{
++    if ((s->config >> 0) & 1) {                                       /* SD */
++        if ((s->config >> 7) & 1)                             /* OS */
++            s->config &= ~(1 << 7);                           /* OS */
++        else
++            return;
++    }
++
++    if ((s->config >> 1) & 1) {                                       /* TM */
++        if (s->temperature >= s->limit[1])
++            s->alarm = 1;
++        else if (s->temperature < s->limit[0])
++            s->alarm = 1;
++    } else {
++        if (s->temperature >= s->limit[1])
++            s->alarm = 1;
++        else if (s->temperature < s->limit[0])
++            s->alarm = 0;
++    }
++
++    tmp105_interrupt_update(s);
++}
++
++/* Units are 0.001 centigrades relative to 0 C.  */
++void tmp105_set(i2c_slave *i2c, int temp)
++{
++    struct tmp105_s *s = (struct tmp105_s *) i2c;
++
++    if (temp >= 128000 || temp < -128000) {
++        fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
++                        __FUNCTION__, temp / 1000, temp % 1000);
++        exit(-1);
++    }
++
++    s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
++
++    tmp105_alarm_update(s);
++}
++
++static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
++
++static void tmp105_read(struct tmp105_s *s)
++{
++    s->len = 0;
++
++    if ((s->config >> 1) & 1) {                                       /* TM */
++        s->alarm = 0;
++        tmp105_interrupt_update(s);
++    }
++
++    switch (s->pointer & 3) {
++    case 0:   /* Temperature */
++        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
++        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
++                (0xf0 << ((~s->config >> 5) & 3));            /* R */
++        break;
++
++    case 1:   /* Configuration */
++        s->buf[s->len ++] = s->config;
++        break;
++
++    case 2:   /* T_LOW */
++        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
++        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
++        break;
++
++    case 3:   /* T_HIGH */
++        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
++        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
++        break;
++    }
++}
++
++static void tmp105_write(struct tmp105_s *s)
++{
++    switch (s->pointer & 3) {
++    case 0:   /* Temperature */
++        break;
++
++    case 1:   /* Configuration */
++        if (s->buf[0] & ~s->config & (1 << 0))                        /* SD */
++            printf("%s: TMP105 shutdown\n", __FUNCTION__);
++        s->config = s->buf[0];
++        s->faults = tmp105_faultq[(s->config >> 3) & 3];      /* F */
++        tmp105_alarm_update(s);
++        break;
++
++    case 2:   /* T_LOW */
++    case 3:   /* T_HIGH */
++        if (s->len >= 3)
++            s->limit[s->pointer & 1] = (int16_t)
++                    ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
++        tmp105_alarm_update(s);
++        break;
++    }
++}
++
++static int tmp105_rx(i2c_slave *i2c)
++{
++    struct tmp105_s *s = (struct tmp105_s *) i2c;
++
++    if (s->len < 2)
++        return s->buf[s->len ++];
++    else
++        return 0xff;
++}
++
++static int tmp105_tx(i2c_slave *i2c, uint8_t data)
++{
++    struct tmp105_s *s = (struct tmp105_s *) i2c;
++
++    if (!s->len ++)
++        s->pointer = data;
++    else {
++        if (s->len <= 2)
++            s->buf[s->len - 1] = data;
++        tmp105_write(s);
++    }
++
++    return 0;
++}
++
++static void tmp105_event(i2c_slave *i2c, enum i2c_event event)
++{
++    struct tmp105_s *s = (struct tmp105_s *) i2c;
++
++    if (event == I2C_START_RECV)
++        tmp105_read(s);
++
++    s->len = 0;
++}
++
++static void tmp105_save(QEMUFile *f, void *opaque)
++{
++    struct tmp105_s *s = (struct tmp105_s *) opaque;
++
++    qemu_put_byte(f, s->len);
++    qemu_put_8s(f, &s->buf[0]);
++    qemu_put_8s(f, &s->buf[1]);
++
++    qemu_put_8s(f, &s->pointer);
++    qemu_put_8s(f, &s->config);
++    qemu_put_be16s(f, &s->temperature);
++    qemu_put_be16s(f, &s->limit[0]);
++    qemu_put_be16s(f, &s->limit[1]);
++    qemu_put_byte(f, s->alarm);
++    s->faults = tmp105_faultq[(s->config >> 3) & 3];          /* F */
++
++    i2c_slave_save(f, &s->i2c);
++}
++
++static int tmp105_load(QEMUFile *f, void *opaque, int version_id)
++{
++    struct tmp105_s *s = (struct tmp105_s *) opaque;
++
++    s->len = qemu_get_byte(f);
++    qemu_get_8s(f, &s->buf[0]);
++    qemu_get_8s(f, &s->buf[1]);
++
++    qemu_get_8s(f, &s->pointer);
++    qemu_get_8s(f, &s->config);
++    qemu_get_be16s(f, &s->temperature);
++    qemu_get_be16s(f, &s->limit[0]);
++    qemu_get_be16s(f, &s->limit[1]);
++    s->alarm = qemu_get_byte(f);
++
++    tmp105_interrupt_update(s);
++
++    i2c_slave_load(f, &s->i2c);
++    return 0;
++}
++
++void tmp105_reset(i2c_slave *i2c)
++{
++    struct tmp105_s *s = (struct tmp105_s *) i2c;
++
++    s->temperature = 0;
++    s->pointer = 0;
++    s->config = 0;
++    s->faults = tmp105_faultq[(s->config >> 3) & 3];
++    s->alarm = 0;
++
++    tmp105_interrupt_update(s);
++}
++
++static int tmp105_iid = 0;
++
++struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
++{
++    struct tmp105_s *s = (struct tmp105_s *)
++            i2c_slave_init(bus, 0, sizeof(struct tmp105_s));
++
++    s->i2c.event = tmp105_event;
++    s->i2c.recv = tmp105_rx;
++    s->i2c.send = tmp105_tx;
++    s->pin = alarm;
++
++    tmp105_reset(&s->i2c);
++
++    register_savevm("TMP105", tmp105_iid ++, 0,
++                    tmp105_save, tmp105_load, s);
++
++    return &s->i2c;
++}
+diff --git a/hw/tsc210x.c b/hw/tsc210x.c
+index 96956a4..1654b8b 100644
+--- a/hw/tsc210x.c
++++ b/hw/tsc210x.c
+@@ -2,6 +2,7 @@
+  * TI TSC2102 (touchscreen/sensors/audio controller) emulator.
+  *
+  * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
++ * Copyright (C) 2008 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
+@@ -35,12 +36,15 @@
+ struct tsc210x_state_s {
+     qemu_irq pint;
++    qemu_irq kbint;
++    qemu_irq davint;
+     QEMUTimer *timer;
+     QEMUSoundCard card;
+     struct uwire_slave_s chip;
+     struct i2s_codec_s codec;
+     uint8_t in_fifo[16384];
+     uint8_t out_fifo[16384];
++    uint16_t model;
+     int x, y;
+     int pressure;
+@@ -64,7 +68,7 @@ struct tsc210x_state_s {
+     uint16_t audio_ctrl1;
+     uint16_t audio_ctrl2;
+     uint16_t audio_ctrl3;
+-    uint16_t pll[2];
++    uint16_t pll[3];
+     uint16_t volume;
+     int64_t volume_change;
+     int softstep;
+@@ -78,6 +82,17 @@ struct tsc210x_state_s {
+     int i2s_rx_rate;
+     int i2s_tx_rate;
+     AudioState *audio;
++
++    int tr[8];
++
++    struct {
++        uint16_t down;
++        uint16_t mask;
++        int scan;
++        int debounce;
++        int mode;
++        int intr;
++    } kb;
+ };
+ static const int resolution[4] = { 12, 8, 10, 12 };
+@@ -118,17 +133,10 @@ static const uint16_t mode_regs[16] = {
+     0x0000,   /* Y+, X- drivers */
+ };
+-/*
+- * Convert screen coordinates to arbitrary values that the
+- * touchscreen in my Palm Tungsten E device returns.
+- * This shouldn't really matter (because the guest system
+- * should calibrate the touchscreen anyway), but let's
+- * imitate some real hardware.
+- */
+-#define X_TRANSFORM(value)            \
+-    ((3850 - ((int) (value) * (3850 - 250) / 32768)) << 4)
+-#define Y_TRANSFORM(value)            \
+-    ((150 + ((int) (value) * (3037 - 150) / 32768)) << 4)
++#define X_TRANSFORM(s)                        \
++    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
++#define Y_TRANSFORM(s)                        \
++    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+ #define Z1_TRANSFORM(s)                       \
+     ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+ #define Z2_TRANSFORM(s)                       \
+@@ -161,6 +169,7 @@ static void tsc210x_reset(struct tsc210x_state_s *s)
+     s->audio_ctrl3 = 0x0000;
+     s->pll[0] = 0x1004;
+     s->pll[1] = 0x0000;
++    s->pll[2] = 0x1fff;
+     s->volume = 0xffff;
+     s->dac_power = 0x8540;
+     s->softstep = 1;
+@@ -190,7 +199,15 @@ static void tsc210x_reset(struct tsc210x_state_s *s)
+     s->i2s_tx_rate = 0;
+     s->i2s_rx_rate = 0;
++    s->kb.scan = 1;
++    s->kb.debounce = 0;
++    s->kb.mask = 0x0000;
++    s->kb.mode = 3;
++    s->kb.intr = 0;
++
+     qemu_set_irq(s->pint, !s->irq);
++    qemu_set_irq(s->davint, !s->dav);
++    qemu_irq_raise(s->kbint);
+ }
+ struct tsc210x_rate_info_s {
+@@ -344,13 +361,13 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
+     switch (reg) {
+     case 0x00:        /* X */
+         s->dav &= 0xfbff;
+-        return TSC_CUT_RESOLUTION(X_TRANSFORM(s->x), s->precision) +
++        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+                 (s->noise & 3);
+     case 0x01:        /* Y */
+         s->noise ++;
+         s->dav &= 0xfdff;
+-        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s->y), s->precision) ^
++        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+                 (s->noise & 3);
+     case 0x02:        /* Z1 */
+@@ -364,6 +381,14 @@ static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg)
+                 (s->noise & 3);
+     case 0x04:        /* KPData */
++        if ((s->model & 0xff00) == 0x2300) {
++            if (s->kb.intr && (s->kb.mode & 2)) {
++                s->kb.intr = 0;
++                qemu_irq_raise(s->kbint);
++            }
++            return s->kb.down;
++        }
++
+         return 0xffff;
+     case 0x05:        /* BAT1 */
+@@ -414,9 +439,19 @@ static uint16_t tsc2102_control_register_read(
+         return (s->pressure << 15) | ((!s->busy) << 14) |
+                 (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; 
+-    case 0x01:        /* Status */
+-        return (s->pin_func << 14) | ((!s->enabled) << 13) |
+-                (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
++    case 0x01:        /* Status / Keypad Control */
++        if ((s->model & 0xff00) == 0x2100)
++            return (s->pin_func << 14) | ((!s->enabled) << 13) |
++                    (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
++        else
++            return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) |
++                    (s->kb.debounce << 11);
++
++    case 0x02:        /* DAC Control */
++        if ((s->model & 0xff00) == 0x2300)
++            return s->dac_power & 0x8000;
++        else
++            goto bad_reg;
+     case 0x03:        /* Reference */
+         return s->ref;
+@@ -427,7 +462,18 @@ static uint16_t tsc2102_control_register_read(
+     case 0x05:        /* Configuration */
+         return s->timing;
++    case 0x06:        /* Secondary configuration */
++        if ((s->model & 0xff00) == 0x2100)
++            goto bad_reg;
++        return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2];
++
++    case 0x10:        /* Keypad Mask */
++        if ((s->model & 0xff00) == 0x2100)
++            goto bad_reg;
++        return s->kb.mask;
++
+     default:
++    bad_reg:
+ #ifdef TSC_VERBOSE
+         fprintf(stderr, "tsc2102_control_register_read: "
+                         "no such register: 0x%02x\n", reg);
+@@ -556,10 +602,27 @@ static void tsc2102_control_register_write(
+         s->filter = value & 0xff;
+         return;
+-    case 0x01:        /* Status */
+-        s->pin_func = value >> 14;
++    case 0x01:        /* Status / Keypad Control */
++        if ((s->model & 0xff00) == 0x2100)
++            s->pin_func = value >> 14;
++      else {
++            s->kb.scan = (value >> 14) & 1;
++            s->kb.debounce = (value >> 11) & 7;
++            if (s->kb.intr && s->kb.scan) {
++                s->kb.intr = 0;
++                qemu_irq_raise(s->kbint);
++            }
++        }
+         return;
++    case 0x02:        /* DAC Control */
++        if ((s->model & 0xff00) == 0x2300) {
++            s->dac_power &= 0x7fff;
++            s->dac_power |= 0x8000 & value;
++        } else
++            goto bad_reg;
++        break;
++
+     case 0x03:        /* Reference */
+         s->ref = value & 0x1f;
+         return;
+@@ -586,7 +649,21 @@ static void tsc2102_control_register_write(
+ #endif
+         return;
++    case 0x06:        /* Secondary configuration */
++        if ((s->model & 0xff00) == 0x2100)
++            goto bad_reg;
++        s->kb.mode = value >> 14;
++        s->pll[2] = value & 0x3ffff;
++        return;
++
++    case 0x10:        /* Keypad Mask */
++        if ((s->model & 0xff00) == 0x2100)
++            goto bad_reg;
++        s->kb.mask = value;
++        return;
++
+     default:
++    bad_reg:
+ #ifdef TSC_VERBOSE
+         fprintf(stderr, "tsc2102_control_register_write: "
+                         "no such register: 0x%02x\n", reg);
+@@ -785,7 +862,7 @@ static void tsc210x_pin_update(struct tsc210x_state_s *s)
+         return;
+     }
+-    if (!s->enabled || s->busy)
++    if (!s->enabled || s->busy || s->dav)
+         return;
+     s->busy = 1;
+@@ -805,6 +882,8 @@ static uint16_t tsc210x_read(struct tsc210x_state_s *s)
+     switch (s->page) {
+     case TSC_DATA_REGISTERS_PAGE:
+         ret = tsc2102_data_register_read(s, s->offset);
++        if (!s->dav)
++            qemu_irq_raise(s->davint);
+         break;
+     case TSC_CONTROL_REGISTERS_PAGE:
+         ret = tsc2102_control_register_read(s, s->offset);
+@@ -859,6 +938,22 @@ static void tsc210x_write(struct tsc210x_state_s *s, uint16_t value)
+     }
+ }
++uint32_t tsc210x_txrx(void *opaque, uint32_t value)
++{
++    struct tsc210x_state_s *s = opaque;
++    uint32_t ret = 0;
++
++    /* TODO: sequential reads etc - how do we make sure the host doesn't
++     * unintentionally read out a conversion result from a register while
++     * transmitting the command word of the next command?  */
++    if (!value || (s->state && s->command))
++        ret = tsc210x_read(s);
++    if (value || (s->state && !s->command))
++        tsc210x_write(s, value);
++
++    return ret;
++}
++
+ static void tsc210x_timer_tick(void *opaque)
+ {
+     struct tsc210x_state_s *s = opaque;
+@@ -871,6 +966,7 @@ static void tsc210x_timer_tick(void *opaque)
+     s->busy = 0;
+     s->dav |= mode_regs[s->function];
+     tsc210x_pin_update(s);
++    qemu_irq_lower(s->davint);
+ }
+ static void tsc210x_touchscreen_event(void *opaque,
+@@ -1001,6 +1097,7 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
+     s->busy = qemu_timer_pending(s->timer);
+     qemu_set_irq(s->pint, !s->irq);
++    qemu_set_irq(s->davint, !s->dav);
+     return 0;
+ }
+@@ -1020,9 +1117,19 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio)
+     s->precision = s->nextprecision = 0;
+     s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s);
+     s->pint = pint;
++    s->model = 0x2102;
+     s->name = "tsc2102";
+     s->audio = audio;
++    s->tr[0] = 0;
++    s->tr[1] = 1;
++    s->tr[2] = 0;
++    s->tr[3] = 1;
++    s->tr[4] = 1;
++    s->tr[5] = 0;
++    s->tr[6] = 0;
++    s->tr[7] = 1;
++
+     s->chip.opaque = s;
+     s->chip.send = (void *) tsc210x_write;
+     s->chip.receive = (void *) tsc210x_read;
+@@ -1048,9 +1155,147 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio)
+     return &s->chip;
+ }
++struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq,
++                qemu_irq dav, AudioState *audio)
++{
++    struct tsc210x_state_s *s;
++
++    s = (struct tsc210x_state_s *)
++            qemu_mallocz(sizeof(struct tsc210x_state_s));
++    memset(s, 0, sizeof(struct tsc210x_state_s));
++    s->x = 400;
++    s->y = 240;
++    s->pressure = 0;
++    s->precision = s->nextprecision = 0;
++    s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s);
++    s->pint = penirq;
++    s->kbint = kbirq;
++    s->davint = dav;
++    s->model = 0x2301;
++    s->name = "tsc2301";
++    s->audio = audio;
++
++    s->tr[0] = 0;
++    s->tr[1] = 1;
++    s->tr[2] = 0;
++    s->tr[3] = 1;
++    s->tr[4] = 1;
++    s->tr[5] = 0;
++    s->tr[6] = 0;
++    s->tr[7] = 1;
++
++    s->chip.opaque = s;
++    s->chip.send = (void *) tsc210x_write;
++    s->chip.receive = (void *) tsc210x_read;
++
++    s->codec.opaque = s;
++    s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
++    s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
++    s->codec.in.fifo = s->in_fifo;
++    s->codec.out.fifo = s->out_fifo;
++
++    tsc210x_reset(s);
++
++    qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
++                    "QEMU TSC2301-driven Touchscreen");
++
++    if (s->audio)
++        AUD_register_card(s->audio, s->name, &s->card);
++
++    qemu_register_reset((void *) tsc210x_reset, s);
++    register_savevm(s->name, tsc2102_iid ++, 0,
++                    tsc210x_save, tsc210x_load, s);
++
++    return &s->chip;
++}
++
+ struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip)
+ {
+     struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque;
+     return &s->codec;
+ }
++
++/*
++ * Use tslib generated calibration data to generate ADC input values
++ * from the touchscreen.  Assuming 12-bit precision was used during
++ * tslib calibration.
++ */
++void tsc210x_set_transform(struct uwire_slave_s *chip,
++                struct mouse_transform_info_s *info)
++{
++    struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque;
++#if 0
++    int64_t ltr[8];
++
++    ltr[0] = (int64_t) info->a[1] * info->y;
++    ltr[1] = (int64_t) info->a[4] * info->x;
++    ltr[2] = (int64_t) info->a[1] * info->a[3] -
++            (int64_t) info->a[4] * info->a[0];
++    ltr[3] = (int64_t) info->a[2] * info->a[4] -
++            (int64_t) info->a[5] * info->a[1];
++    ltr[4] = (int64_t) info->a[0] * info->y;
++    ltr[5] = (int64_t) info->a[3] * info->x;
++    ltr[6] = (int64_t) info->a[4] * info->a[0] -
++            (int64_t) info->a[1] * info->a[3];
++    ltr[7] = (int64_t) info->a[2] * info->a[3] -
++            (int64_t) info->a[5] * info->a[0];
++
++    /* Avoid integer overflow */
++    s->tr[0] = ltr[0] >> 11;
++    s->tr[1] = ltr[1] >> 11;
++    s->tr[2] = muldiv64(ltr[2], 1, info->a[6]);
++    s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]);
++    s->tr[4] = ltr[4] >> 11;
++    s->tr[5] = ltr[5] >> 11;
++    s->tr[6] = muldiv64(ltr[6], 1, info->a[6]);
++    s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]);
++#else
++
++    if (abs(info->a[0]) > abs(info->a[1])) {
++        s->tr[0] = 0;
++        s->tr[1] = -info->a[6] * info->x;
++        s->tr[2] = info->a[0];
++        s->tr[3] = -info->a[2] / info->a[0];
++        s->tr[4] = info->a[6] * info->y;
++        s->tr[5] = 0;
++        s->tr[6] = info->a[4];
++        s->tr[7] = -info->a[5] / info->a[4];
++    } else {
++        s->tr[0] = info->a[6] * info->y;
++        s->tr[1] = 0;
++        s->tr[2] = info->a[1];
++        s->tr[3] = -info->a[2] / info->a[1];
++        s->tr[4] = 0;
++        s->tr[5] = -info->a[6] * info->x;
++        s->tr[6] = info->a[3];
++        s->tr[7] = -info->a[5] / info->a[3];
++    }
++
++    s->tr[0] >>= 11;
++    s->tr[1] >>= 11;
++    s->tr[3] <<= 4;
++    s->tr[4] >>= 11;
++    s->tr[5] >>= 11;
++    s->tr[7] <<= 4;
++#endif
++}
++
++void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down)
++{
++    struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque;
++
++    if (down)
++        s->kb.down |= 1 << key;
++    else
++        s->kb.down &= ~(1 << key);
++
++    if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) {
++        s->kb.intr = 1;
++        qemu_irq_lower(s->kbint);
++    } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) &&
++                    !(s->kb.mode & 1)) {
++        s->kb.intr = 0;
++        qemu_irq_raise(s->kbint);
++    }
++}
+diff --git a/hw/twl92230.c b/hw/twl92230.c
+new file mode 100644
+index 0000000..11a5d1a
+--- /dev/null
++++ b/hw/twl92230.c
+@@ -0,0 +1,923 @@
++/*
++ * TI TWL92230C energy-management companion device for the OMAP24xx.
++ * Aka. Menelaus (N4200 MENELAUS1_V2.2)
++ *
++ * Copyright (C) 2008 Nokia Corporation
++ * Written by Andrzej Zaborowski <andrew@openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include "hw.h"
++#include "qemu-timer.h"
++#include "i2c.h"
++#include "sysemu.h"
++#include "console.h"
++
++#define VERBOSE 1
++
++struct menelaus_s {
++    i2c_slave i2c;
++    qemu_irq irq;
++
++    int firstbyte;
++    uint8_t reg;
++
++    uint8_t vcore[5];
++    uint8_t dcdc[3];
++    uint8_t ldo[8];
++    uint8_t sleep[2];
++    uint8_t osc;
++    uint8_t detect;
++    uint16_t mask;
++    uint16_t status;
++    uint8_t dir;
++    uint8_t inputs;
++    uint8_t outputs;
++    uint8_t bbsms;
++    uint8_t pull[4];
++    uint8_t mmc_ctrl[3];
++    uint8_t mmc_debounce;
++    struct {
++        uint8_t ctrl;
++        uint16_t comp;
++        QEMUTimer *hz;
++        int64_t next;
++        struct tm tm;
++        struct tm new;
++        struct tm alm;
++        time_t sec;
++        time_t alm_sec;
++        time_t next_comp;
++        struct tm *(*gettime)(const time_t *timep, struct tm *result);
++    } rtc;
++    qemu_irq handler[3];
++    qemu_irq *in;
++    int pwrbtn_state;
++    qemu_irq pwrbtn;
++};
++
++static inline void menelaus_update(struct menelaus_s *s)
++{
++    qemu_set_irq(s->irq, s->status & ~s->mask);
++}
++
++static inline void menelaus_rtc_start(struct menelaus_s *s)
++{
++    s->rtc.next =+ qemu_get_clock(rt_clock);
++    qemu_mod_timer(s->rtc.hz, s->rtc.next);
++}
++
++static inline void menelaus_rtc_stop(struct menelaus_s *s)
++{
++    qemu_del_timer(s->rtc.hz);
++    s->rtc.next =- qemu_get_clock(rt_clock);
++    if (s->rtc.next < 1)
++        s->rtc.next = 1;
++}
++
++static void menelaus_rtc_update(struct menelaus_s *s)
++{
++    s->rtc.gettime(&s->rtc.sec, &s->rtc.tm);
++}
++
++static void menelaus_alm_update(struct menelaus_s *s)
++{
++    if ((s->rtc.ctrl & 3) == 3)
++        s->rtc.alm_sec = mktime(&s->rtc.alm);
++}
++
++static void menelaus_rtc_hz(void *opaque)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++
++    s->rtc.sec ++;
++    s->rtc.next += 1000;
++    qemu_mod_timer(s->rtc.hz, s->rtc.next);
++    if ((s->rtc.ctrl >> 3) & 3) {                             /* EVERY */
++        menelaus_rtc_update(s);
++        if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
++            s->status |= 1 << 8;                              /* RTCTMR */
++        else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
++            s->status |= 1 << 8;                              /* RTCTMR */
++        else if (!s->rtc.tm.tm_hour)
++            s->status |= 1 << 8;                              /* RTCTMR */
++    } else
++        s->status |= 1 << 8;                                  /* RTCTMR */
++    if ((s->rtc.ctrl >> 1) & 1) {                             /* RTC_AL_EN */
++        if (s->rtc.sec == s->rtc.alm_sec)
++            s->status |= 1 << 9;                              /* RTCALM */
++        /* TODO: wake-up */
++    }
++    if (s->rtc.next_comp >= s->rtc.sec) {
++        s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
++        s->rtc.next_comp = s->rtc.sec + 3600;
++    }
++    menelaus_update(s);
++}
++
++void menelaus_reset(i2c_slave *i2c)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++    time_t ti;
++    s->reg = 0x00;
++
++    s->vcore[0] = 0x0c;       /* XXX: X-loader needs 0x8c? check!  */
++    s->vcore[1] = 0x05;
++    s->vcore[2] = 0x02;
++    s->vcore[3] = 0x0c;
++    s->vcore[4] = 0x03;
++    s->dcdc[0] = 0x33;        /* Depends on wiring */
++    s->dcdc[1] = 0x03;
++    s->dcdc[2] = 0x00;
++    s->ldo[0] = 0x95;
++    s->ldo[1] = 0x7e;
++    s->ldo[2] = 0x00;
++    s->ldo[3] = 0x00; /* Depends on wiring */
++    s->ldo[4] = 0x03; /* Depends on wiring */
++    s->ldo[5] = 0x00;
++    s->ldo[6] = 0x00;
++    s->ldo[7] = 0x00;
++    s->sleep[0] = 0x00;
++    s->sleep[1] = 0x00;
++    s->osc = 0x01;
++    s->detect = 0x09;
++    s->mask = 0x0fff;
++    s->status = 0;
++    s->dir = 0x07;
++    s->outputs = 0x00;
++    s->bbsms = 0x00;
++    s->pull[0] = 0x00;
++    s->pull[1] = 0x00;
++    s->pull[2] = 0x00;
++    s->pull[3] = 0x00;
++    s->mmc_ctrl[0] = 0x03;
++    s->mmc_ctrl[1] = 0xc0;
++    s->mmc_ctrl[2] = 0x00;
++    s->mmc_debounce = 0x05;
++
++    time(&ti);
++    if (s->rtc.ctrl & 1)
++        menelaus_rtc_stop(s);
++    s->rtc.ctrl = 0x00;
++    s->rtc.comp = 0x0000;
++    s->rtc.next = 1000;
++    s->rtc.sec = ti;
++    s->rtc.next_comp = s->rtc.sec + 1800;
++    s->rtc.alm.tm_sec = 0x00;
++    s->rtc.alm.tm_min = 0x00;
++    s->rtc.alm.tm_hour = 0x00;
++    s->rtc.alm.tm_mday = 0x01;
++    s->rtc.alm.tm_mon = 0x00;
++    s->rtc.alm.tm_year = 2004;
++    menelaus_update(s);
++}
++
++static inline uint8_t to_bcd(int val)
++{
++    return ((val / 10) << 4) | (val % 10);
++}
++
++static inline int from_bcd(uint8_t val)
++{
++    return ((val >> 4) * 10) + (val & 0x0f);
++}
++
++static void menelaus_gpio_set(void *opaque, int line, int level)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++
++    /* No interrupt generated */
++    s->inputs &= ~(1 << line);
++    s->inputs |= level << line;
++}
++
++static void menelaus_pwrbtn_set(void *opaque, int line, int level)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++
++    if (!s->pwrbtn_state && level) {
++        s->status |= 1 << 11;                                 /* PSHBTN */
++        menelaus_update(s);
++    }
++    s->pwrbtn_state = level;
++}
++
++#define MENELAUS_REV          0x01
++#define MENELAUS_VCORE_CTRL1  0x02
++#define MENELAUS_VCORE_CTRL2  0x03
++#define MENELAUS_VCORE_CTRL3  0x04
++#define MENELAUS_VCORE_CTRL4  0x05
++#define MENELAUS_VCORE_CTRL5  0x06
++#define MENELAUS_DCDC_CTRL1   0x07
++#define MENELAUS_DCDC_CTRL2   0x08
++#define MENELAUS_DCDC_CTRL3   0x09
++#define MENELAUS_LDO_CTRL1    0x0a
++#define MENELAUS_LDO_CTRL2    0x0b
++#define MENELAUS_LDO_CTRL3    0x0c
++#define MENELAUS_LDO_CTRL4    0x0d
++#define MENELAUS_LDO_CTRL5    0x0e
++#define MENELAUS_LDO_CTRL6    0x0f
++#define MENELAUS_LDO_CTRL7    0x10
++#define MENELAUS_LDO_CTRL8    0x11
++#define MENELAUS_SLEEP_CTRL1  0x12
++#define MENELAUS_SLEEP_CTRL2  0x13
++#define MENELAUS_DEVICE_OFF   0x14
++#define MENELAUS_OSC_CTRL     0x15
++#define MENELAUS_DETECT_CTRL  0x16
++#define MENELAUS_INT_MASK1    0x17
++#define MENELAUS_INT_MASK2    0x18
++#define MENELAUS_INT_STATUS1  0x19
++#define MENELAUS_INT_STATUS2  0x1a
++#define MENELAUS_INT_ACK1     0x1b
++#define MENELAUS_INT_ACK2     0x1c
++#define MENELAUS_GPIO_CTRL    0x1d
++#define MENELAUS_GPIO_IN      0x1e
++#define MENELAUS_GPIO_OUT     0x1f
++#define MENELAUS_BBSMS                0x20
++#define MENELAUS_RTC_CTRL     0x21
++#define MENELAUS_RTC_UPDATE   0x22
++#define MENELAUS_RTC_SEC      0x23
++#define MENELAUS_RTC_MIN      0x24
++#define MENELAUS_RTC_HR               0x25
++#define MENELAUS_RTC_DAY      0x26
++#define MENELAUS_RTC_MON      0x27
++#define MENELAUS_RTC_YR               0x28
++#define MENELAUS_RTC_WKDAY    0x29
++#define MENELAUS_RTC_AL_SEC   0x2a
++#define MENELAUS_RTC_AL_MIN   0x2b
++#define MENELAUS_RTC_AL_HR    0x2c
++#define MENELAUS_RTC_AL_DAY   0x2d
++#define MENELAUS_RTC_AL_MON   0x2e
++#define MENELAUS_RTC_AL_YR    0x2f
++#define MENELAUS_RTC_COMP_MSB 0x30
++#define MENELAUS_RTC_COMP_LSB 0x31
++#define MENELAUS_S1_PULL_EN   0x32
++#define MENELAUS_S1_PULL_DIR  0x33
++#define MENELAUS_S2_PULL_EN   0x34
++#define MENELAUS_S2_PULL_DIR  0x35
++#define MENELAUS_MCT_CTRL1    0x36
++#define MENELAUS_MCT_CTRL2    0x37
++#define MENELAUS_MCT_CTRL3    0x38
++#define MENELAUS_MCT_PIN_ST   0x39
++#define MENELAUS_DEBOUNCE1    0x3a
++
++static uint8_t menelaus_read(void *opaque, uint8_t addr)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++    int reg = 0;
++
++    switch (addr) {
++    case MENELAUS_REV:
++        return 0x22;
++
++    case MENELAUS_VCORE_CTRL5: reg ++;
++    case MENELAUS_VCORE_CTRL4: reg ++;
++    case MENELAUS_VCORE_CTRL3: reg ++;
++    case MENELAUS_VCORE_CTRL2: reg ++;
++    case MENELAUS_VCORE_CTRL1:
++        return s->vcore[reg];
++
++    case MENELAUS_DCDC_CTRL3: reg ++;
++    case MENELAUS_DCDC_CTRL2: reg ++;
++    case MENELAUS_DCDC_CTRL1:
++        return s->dcdc[reg];
++
++    case MENELAUS_LDO_CTRL8: reg ++;
++    case MENELAUS_LDO_CTRL7: reg ++;
++    case MENELAUS_LDO_CTRL6: reg ++;
++    case MENELAUS_LDO_CTRL5: reg ++;
++    case MENELAUS_LDO_CTRL4: reg ++;
++    case MENELAUS_LDO_CTRL3: reg ++;
++    case MENELAUS_LDO_CTRL2: reg ++;
++    case MENELAUS_LDO_CTRL1:
++        return s->ldo[reg];
++
++    case MENELAUS_SLEEP_CTRL2: reg ++;
++    case MENELAUS_SLEEP_CTRL1:
++        return s->sleep[reg];
++
++    case MENELAUS_DEVICE_OFF:
++        return 0;
++
++    case MENELAUS_OSC_CTRL:
++        return s->osc | (1 << 7);                     /* CLK32K_GOOD */
++
++    case MENELAUS_DETECT_CTRL:
++        return s->detect;
++
++    case MENELAUS_INT_MASK1:
++        return (s->mask >> 0) & 0xff;
++    case MENELAUS_INT_MASK2:
++        return (s->mask >> 8) & 0xff;
++
++    case MENELAUS_INT_STATUS1:
++        return (s->status >> 0) & 0xff;
++    case MENELAUS_INT_STATUS2:
++        return (s->status >> 8) & 0xff;
++
++    case MENELAUS_INT_ACK1:
++    case MENELAUS_INT_ACK2:
++        return 0;
++
++    case MENELAUS_GPIO_CTRL:
++        return s->dir;
++    case MENELAUS_GPIO_IN:
++        return s->inputs | (~s->dir & s->outputs);
++    case MENELAUS_GPIO_OUT:
++        return s->outputs;
++
++    case MENELAUS_BBSMS:
++        return s->bbsms;
++
++    case MENELAUS_RTC_CTRL:
++        return s->rtc.ctrl;
++    case MENELAUS_RTC_UPDATE:
++        return 0x00;
++    case MENELAUS_RTC_SEC:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_sec);
++    case MENELAUS_RTC_MIN:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_min);
++    case MENELAUS_RTC_HR:
++        menelaus_rtc_update(s);
++        if ((s->rtc.ctrl >> 2) & 1)                   /* MODE12_n24 */
++            return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
++                    (!!(s->rtc.tm.tm_hour >= 12) << 7);       /* PM_nAM */
++        else
++            return to_bcd(s->rtc.tm.tm_hour);
++    case MENELAUS_RTC_DAY:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_mday);
++    case MENELAUS_RTC_MON:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_mon + 1);
++    case MENELAUS_RTC_YR:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_year - 2000);
++    case MENELAUS_RTC_WKDAY:
++        menelaus_rtc_update(s);
++        return to_bcd(s->rtc.tm.tm_wday);
++    case MENELAUS_RTC_AL_SEC:
++        return to_bcd(s->rtc.alm.tm_sec);
++    case MENELAUS_RTC_AL_MIN:
++        return to_bcd(s->rtc.alm.tm_min);
++    case MENELAUS_RTC_AL_HR:
++        if ((s->rtc.ctrl >> 2) & 1)                   /* MODE12_n24 */
++            return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
++                    (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
++        else
++            return to_bcd(s->rtc.alm.tm_hour);
++    case MENELAUS_RTC_AL_DAY:
++        return to_bcd(s->rtc.alm.tm_mday);
++    case MENELAUS_RTC_AL_MON:
++        return to_bcd(s->rtc.alm.tm_mon + 1);
++    case MENELAUS_RTC_AL_YR:
++        return to_bcd(s->rtc.alm.tm_year - 2000);
++    case MENELAUS_RTC_COMP_MSB:
++        return (s->rtc.comp >> 8) & 0xff;
++    case MENELAUS_RTC_COMP_LSB:
++        return (s->rtc.comp >> 0) & 0xff;
++
++    case MENELAUS_S1_PULL_EN:
++        return s->pull[0];
++    case MENELAUS_S1_PULL_DIR:
++        return s->pull[1];
++    case MENELAUS_S2_PULL_EN:
++        return s->pull[2];
++    case MENELAUS_S2_PULL_DIR:
++        return s->pull[3];
++
++    case MENELAUS_MCT_CTRL3: reg ++;
++    case MENELAUS_MCT_CTRL2: reg ++;
++    case MENELAUS_MCT_CTRL1:
++        return s->mmc_ctrl[reg];
++    case MENELAUS_MCT_PIN_ST:
++        /* TODO: return the real Card Detect */
++        return 0;
++    case MENELAUS_DEBOUNCE1:
++        return s->mmc_debounce;
++
++    default:
++#ifdef VERBOSE
++        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
++#endif
++        break;
++    }
++    return 0;
++}
++
++static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++    int line;
++    int reg = 0;
++    struct tm tm;
++
++    switch (addr) {
++    case MENELAUS_VCORE_CTRL1:
++        s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
++        break;
++    case MENELAUS_VCORE_CTRL2:
++        s->vcore[1] = value;
++        break;
++    case MENELAUS_VCORE_CTRL3:
++        s->vcore[2] = MIN(value & 0x1f, 0x12);
++        break;
++    case MENELAUS_VCORE_CTRL4:
++        s->vcore[3] = MIN(value & 0x1f, 0x12);
++        break;
++    case MENELAUS_VCORE_CTRL5:
++        s->vcore[4] = value & 3;
++        /* XXX
++         * auto set to 3 on M_Active, nRESWARM
++         * auto set to 0 on M_WaitOn, M_Backup
++         */
++        break;
++
++    case MENELAUS_DCDC_CTRL1:
++        s->dcdc[0] = value & 0x3f;
++        break;
++    case MENELAUS_DCDC_CTRL2:
++        s->dcdc[1] = value & 0x07;
++        /* XXX
++         * auto set to 3 on M_Active, nRESWARM
++         * auto set to 0 on M_WaitOn, M_Backup
++         */
++        break;
++    case MENELAUS_DCDC_CTRL3:
++        s->dcdc[2] = value & 0x07;
++        break;
++
++    case MENELAUS_LDO_CTRL1:
++        s->ldo[0] = value;
++        break;
++    case MENELAUS_LDO_CTRL2:
++        s->ldo[1] = value & 0x7f;
++        /* XXX
++         * auto set to 0x7e on M_WaitOn, M_Backup
++         */
++        break;
++    case MENELAUS_LDO_CTRL3:
++        s->ldo[2] = value & 3;
++        /* XXX
++         * auto set to 3 on M_Active, nRESWARM
++         * auto set to 0 on M_WaitOn, M_Backup
++         */
++        break;
++    case MENELAUS_LDO_CTRL4:
++        s->ldo[3] = value & 3;
++        /* XXX
++         * auto set to 3 on M_Active, nRESWARM
++         * auto set to 0 on M_WaitOn, M_Backup
++         */
++        break;
++    case MENELAUS_LDO_CTRL5:
++        s->ldo[4] = value & 3;
++        /* XXX
++         * auto set to 3 on M_Active, nRESWARM
++         * auto set to 0 on M_WaitOn, M_Backup
++         */
++        break;
++    case MENELAUS_LDO_CTRL6:
++        s->ldo[5] = value & 3;
++        break;
++    case MENELAUS_LDO_CTRL7:
++        s->ldo[6] = value & 3;
++        break;
++    case MENELAUS_LDO_CTRL8:
++        s->ldo[7] = value & 3;
++        break;
++
++    case MENELAUS_SLEEP_CTRL2: reg ++;
++    case MENELAUS_SLEEP_CTRL1:
++        s->sleep[reg] = value;
++        break;
++
++    case MENELAUS_DEVICE_OFF:
++        if (value & 1)
++            menelaus_reset(&s->i2c);
++        break;
++
++    case MENELAUS_OSC_CTRL:
++        s->osc = value & 7;
++        break;
++
++    case MENELAUS_DETECT_CTRL:
++        s->detect = value & 0x7f;
++        break;
++
++    case MENELAUS_INT_MASK1:
++        s->mask &= 0xf00;
++        s->mask |= value << 0;
++        menelaus_update(s);
++        break;
++    case MENELAUS_INT_MASK2:
++        s->mask &= 0x0ff;
++        s->mask |= value << 8;
++        menelaus_update(s);
++        break;
++
++    case MENELAUS_INT_ACK1:
++        s->status &= ~(((uint16_t) value) << 0);
++        menelaus_update(s);
++        break;
++    case MENELAUS_INT_ACK2:
++        s->status &= ~(((uint16_t) value) << 8);
++        menelaus_update(s);
++        break;
++
++    case MENELAUS_GPIO_CTRL:
++        for (line = 0; line < 3; line ++)
++            if (((s->dir ^ value) >> line) & 1)
++                if (s->handler[line])
++                    qemu_set_irq(s->handler[line],
++                                    ((s->outputs & ~s->dir) >> line) & 1);
++        s->dir = value & 0x67;
++        break;
++    case MENELAUS_GPIO_OUT:
++        for (line = 0; line < 3; line ++)
++            if ((((s->outputs ^ value) & ~s->dir) >> line) & 1)
++                if (s->handler[line])
++                    qemu_set_irq(s->handler[line], (s->outputs >> line) & 1);
++        s->outputs = value & 0x07;
++        break;
++
++    case MENELAUS_BBSMS:
++        s->bbsms = 0x0d;
++        break;
++
++    case MENELAUS_RTC_CTRL:
++        if ((s->rtc.ctrl ^ value) & 1) {                      /* RTC_EN */
++            if (value & 1)
++                menelaus_rtc_start(s);
++            else
++                menelaus_rtc_stop(s);
++        }
++        s->rtc.ctrl = value & 0x1f;
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_UPDATE:
++        menelaus_rtc_update(s);
++        memcpy(&tm, &s->rtc.tm, sizeof(tm));
++        switch (value & 0xf) {
++        case 0:
++            break;
++        case 1:
++            tm.tm_sec = s->rtc.new.tm_sec;
++            break;
++        case 2:
++            tm.tm_min = s->rtc.new.tm_min;
++            break;
++        case 3:
++            if (s->rtc.new.tm_hour > 23)
++                goto rtc_badness;
++            tm.tm_hour = s->rtc.new.tm_hour;
++            break;
++        case 4:
++            if (s->rtc.new.tm_mday < 1)
++                goto rtc_badness;
++            /* TODO check range */
++            tm.tm_mday = s->rtc.new.tm_mday;
++            break;
++        case 5:
++            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
++                goto rtc_badness;
++            tm.tm_mon = s->rtc.new.tm_mon;
++            break;
++        case 6:
++            tm.tm_year = s->rtc.new.tm_year;
++            break;
++        case 7:
++            /* TODO set .tm_mday instead */
++            tm.tm_wday = s->rtc.new.tm_wday;
++            break;
++        case 8:
++            if (s->rtc.new.tm_hour > 23)
++                goto rtc_badness;
++            if (s->rtc.new.tm_mday < 1)
++                goto rtc_badness;
++            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
++                goto rtc_badness;
++            tm.tm_sec = s->rtc.new.tm_sec;
++            tm.tm_min = s->rtc.new.tm_min;
++            tm.tm_hour = s->rtc.new.tm_hour;
++            tm.tm_mday = s->rtc.new.tm_mday;
++            tm.tm_mon = s->rtc.new.tm_mon;
++            tm.tm_year = s->rtc.new.tm_year;
++            break;
++        rtc_badness:
++        default:
++            fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
++                            __FUNCTION__, value);
++            s->status |= 1 << 10;                             /* RTCERR */
++            menelaus_update(s);
++        }
++        s->rtc.sec += difftime(mktime(&tm), mktime(&s->rtc.tm));
++        break;
++    case MENELAUS_RTC_SEC:
++        s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
++        break;
++    case MENELAUS_RTC_MIN:
++        s->rtc.tm.tm_min = from_bcd(value & 0x7f);
++        break;
++    case MENELAUS_RTC_HR:
++        s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ?        /* MODE12_n24 */
++                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
++                from_bcd(value & 0x3f);
++        break;
++    case MENELAUS_RTC_DAY:
++        s->rtc.tm.tm_mday = from_bcd(value);
++        break;
++    case MENELAUS_RTC_MON:
++        s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
++        break;
++    case MENELAUS_RTC_YR:
++        s->rtc.tm.tm_year = 2000 + from_bcd(value);
++        break;
++    case MENELAUS_RTC_WKDAY:
++        s->rtc.tm.tm_mday = from_bcd(value);
++        break;
++    case MENELAUS_RTC_AL_SEC:
++        s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_AL_MIN:
++        s->rtc.alm.tm_min = from_bcd(value & 0x7f);
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_AL_HR:
++        s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ?       /* MODE12_n24 */
++                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
++                from_bcd(value & 0x3f);
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_AL_DAY:
++        s->rtc.alm.tm_mday = from_bcd(value);
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_AL_MON:
++        s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_AL_YR:
++        s->rtc.alm.tm_year = 2000 + from_bcd(value);
++        menelaus_alm_update(s);
++        break;
++    case MENELAUS_RTC_COMP_MSB:
++        s->rtc.comp &= 0xff;
++        s->rtc.comp |= value << 8;
++        break;
++    case MENELAUS_RTC_COMP_LSB:
++        s->rtc.comp &= 0xff << 8;
++        s->rtc.comp |= value;
++        break;
++
++    case MENELAUS_S1_PULL_EN:
++        s->pull[0] = value;
++        break;
++    case MENELAUS_S1_PULL_DIR:
++        s->pull[1] = value & 0x1f;
++        break;
++    case MENELAUS_S2_PULL_EN:
++        s->pull[2] = value;
++        break;
++    case MENELAUS_S2_PULL_DIR:
++        s->pull[3] = value & 0x1f;
++        break;
++
++    case MENELAUS_MCT_CTRL1:
++        s->mmc_ctrl[0] = value & 0x7f;
++        break;
++    case MENELAUS_MCT_CTRL2:
++        s->mmc_ctrl[1] = value;
++        /* TODO update Card Detect interrupts */
++        break;
++    case MENELAUS_MCT_CTRL3:
++        s->mmc_ctrl[2] = value & 0xf;
++        break;
++    case MENELAUS_DEBOUNCE1:
++        s->mmc_debounce = value & 0x3f;
++        break;
++
++    default:
++#ifdef VERBOSE
++        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
++#endif
++    }
++}
++
++static void menelaus_event(i2c_slave *i2c, enum i2c_event event)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++
++    if (event == I2C_START_SEND)
++        s->firstbyte = 1;
++}
++
++static int menelaus_tx(i2c_slave *i2c, uint8_t data)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++    /* Interpret register address byte */
++    if (s->firstbyte) {
++        s->reg = data;
++        s->firstbyte = 0;
++    } else
++        menelaus_write(s, s->reg ++, data);
++
++    return 0;
++}
++
++static int menelaus_rx(i2c_slave *i2c)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++
++    return menelaus_read(s, s->reg ++);
++}
++
++static void tm_put(QEMUFile *f, struct tm *tm) {
++    qemu_put_be16(f, tm->tm_sec);
++    qemu_put_be16(f, tm->tm_min);
++    qemu_put_be16(f, tm->tm_hour);
++    qemu_put_be16(f, tm->tm_mday);
++    qemu_put_be16(f, tm->tm_min);
++    qemu_put_be16(f, tm->tm_year);
++}
++
++static void tm_get(QEMUFile *f, struct tm *tm) {
++    tm->tm_sec = qemu_get_be16(f);
++    tm->tm_min = qemu_get_be16(f);
++    tm->tm_hour = qemu_get_be16(f);
++    tm->tm_mday = qemu_get_be16(f);
++    tm->tm_min = qemu_get_be16(f);
++    tm->tm_year = qemu_get_be16(f);
++}
++
++static void menelaus_save(QEMUFile *f, void *opaque)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++
++    qemu_put_be32(f, s->firstbyte);
++    qemu_put_8s(f, &s->reg);
++
++    qemu_put_8s(f, &s->vcore[0]);
++    qemu_put_8s(f, &s->vcore[1]);
++    qemu_put_8s(f, &s->vcore[2]);
++    qemu_put_8s(f, &s->vcore[3]);
++    qemu_put_8s(f, &s->vcore[4]);
++    qemu_put_8s(f, &s->dcdc[3]);
++    qemu_put_8s(f, &s->dcdc[3]);
++    qemu_put_8s(f, &s->dcdc[3]);
++    qemu_put_8s(f, &s->ldo[0]);
++    qemu_put_8s(f, &s->ldo[1]);
++    qemu_put_8s(f, &s->ldo[2]);
++    qemu_put_8s(f, &s->ldo[3]);
++    qemu_put_8s(f, &s->ldo[4]);
++    qemu_put_8s(f, &s->ldo[5]);
++    qemu_put_8s(f, &s->ldo[6]);
++    qemu_put_8s(f, &s->ldo[7]);
++    qemu_put_8s(f, &s->sleep[0]);
++    qemu_put_8s(f, &s->sleep[1]);
++    qemu_put_8s(f, &s->osc);
++    qemu_put_8s(f, &s->detect);
++    qemu_put_be16s(f, &s->mask);
++    qemu_put_be16s(f, &s->status);
++    qemu_put_8s(f, &s->dir);
++    qemu_put_8s(f, &s->inputs);
++    qemu_put_8s(f, &s->outputs);
++    qemu_put_8s(f, &s->bbsms);
++    qemu_put_8s(f, &s->pull[0]);
++    qemu_put_8s(f, &s->pull[1]);
++    qemu_put_8s(f, &s->pull[2]);
++    qemu_put_8s(f, &s->pull[3]);
++    qemu_put_8s(f, &s->mmc_ctrl[0]);
++    qemu_put_8s(f, &s->mmc_ctrl[1]);
++    qemu_put_8s(f, &s->mmc_ctrl[2]);
++    qemu_put_8s(f, &s->mmc_debounce);
++    qemu_put_8s(f, &s->rtc.ctrl);
++    qemu_put_be16s(f, &s->rtc.comp);
++    /* Should be <= 1000 */
++    qemu_put_be16(f, s->rtc.next - qemu_get_clock(rt_clock));
++    tm_put(f, &s->rtc.new);
++    tm_put(f, &s->rtc.alm);
++    qemu_put_byte(f, s->pwrbtn_state);
++
++    i2c_slave_save(f, &s->i2c);
++}
++
++static int menelaus_load(QEMUFile *f, void *opaque, int version_id)
++{
++    struct menelaus_s *s = (struct menelaus_s *) opaque;
++
++    s->firstbyte = qemu_get_be32(f);
++    qemu_get_8s(f, &s->reg);
++
++    if (s->rtc.ctrl & 1)                                      /* RTC_EN */
++        menelaus_rtc_stop(s);
++    qemu_get_8s(f, &s->vcore[0]);
++    qemu_get_8s(f, &s->vcore[1]);
++    qemu_get_8s(f, &s->vcore[2]);
++    qemu_get_8s(f, &s->vcore[3]);
++    qemu_get_8s(f, &s->vcore[4]);
++    qemu_get_8s(f, &s->dcdc[3]);
++    qemu_get_8s(f, &s->dcdc[3]);
++    qemu_get_8s(f, &s->dcdc[3]);
++    qemu_get_8s(f, &s->ldo[0]);
++    qemu_get_8s(f, &s->ldo[1]);
++    qemu_get_8s(f, &s->ldo[2]);
++    qemu_get_8s(f, &s->ldo[3]);
++    qemu_get_8s(f, &s->ldo[4]);
++    qemu_get_8s(f, &s->ldo[5]);
++    qemu_get_8s(f, &s->ldo[6]);
++    qemu_get_8s(f, &s->ldo[7]);
++    qemu_get_8s(f, &s->sleep[0]);
++    qemu_get_8s(f, &s->sleep[1]);
++    qemu_get_8s(f, &s->osc);
++    qemu_get_8s(f, &s->detect);
++    qemu_get_be16s(f, &s->mask);
++    qemu_get_be16s(f, &s->status);
++    qemu_get_8s(f, &s->dir);
++    qemu_get_8s(f, &s->inputs);
++    qemu_get_8s(f, &s->outputs);
++    qemu_get_8s(f, &s->bbsms);
++    qemu_get_8s(f, &s->pull[0]);
++    qemu_get_8s(f, &s->pull[1]);
++    qemu_get_8s(f, &s->pull[2]);
++    qemu_get_8s(f, &s->pull[3]);
++    qemu_get_8s(f, &s->mmc_ctrl[0]);
++    qemu_get_8s(f, &s->mmc_ctrl[1]);
++    qemu_get_8s(f, &s->mmc_ctrl[2]);
++    qemu_get_8s(f, &s->mmc_debounce);
++    qemu_get_8s(f, &s->rtc.ctrl);
++    qemu_get_be16s(f, &s->rtc.comp);
++    s->rtc.next = qemu_get_be16(f);
++    tm_get(f, &s->rtc.new);
++    tm_get(f, &s->rtc.alm);
++    s->pwrbtn_state = qemu_get_byte(f);
++    menelaus_alm_update(s);
++    menelaus_update(s);
++    if (s->rtc.ctrl & 1)                                      /* RTC_EN */
++        menelaus_rtc_start(s);
++
++    i2c_slave_load(f, &s->i2c);
++    return 0;
++}
++
++static int menelaus_iid = 0;
++
++i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq)
++{
++    struct menelaus_s *s = (struct menelaus_s *)
++            i2c_slave_init(bus, 0, sizeof(struct menelaus_s));
++
++    s->i2c.event = menelaus_event;
++    s->i2c.recv = menelaus_rx;
++    s->i2c.send = menelaus_tx;
++
++    /* TODO: use the qemu gettime functions */
++    s->rtc.gettime = localtime_r;
++
++    s->irq = irq;
++    s->rtc.hz = qemu_new_timer(rt_clock, menelaus_rtc_hz, s);
++    s->in = qemu_allocate_irqs(menelaus_gpio_set, s, 3);
++    s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
++
++    menelaus_reset(&s->i2c);
++
++    register_savevm("menelaus", menelaus_iid ++,
++                    0, menelaus_save, menelaus_load, s);
++
++    return &s->i2c;
++}
++
++qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++
++    return s->in;
++}
++
++void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
++{
++    struct menelaus_s *s = (struct menelaus_s *) i2c;
++
++    if (line >= 3 || line < 0) {
++        fprintf(stderr, "%s: No GPO line %i\n", __FUNCTION__, line);
++        exit(-1);
++    }
++    s->handler[line] = handler;
++}
+diff --git a/hw/versatilepb.c b/hw/versatilepb.c
+index da6e4ec..ecc0037 100644
+--- a/hw/versatilepb.c
++++ b/hw/versatilepb.c
+@@ -157,6 +157,8 @@ static qemu_irq *vpb_sic_init(uint32_t base, qemu_irq *parent, int irq)
+    peripherans and expansion busses.  For now we emulate a subset of the
+    PB peripherals and just change the board ID.  */
++static struct arm_boot_info versatile_binfo;
++
+ static void versatile_init(int ram_size, int vga_ram_size,
+                      const char *boot_device, DisplayState *ds,
+                      const char *kernel_filename, const char *kernel_cmdline,
+@@ -283,8 +285,12 @@ static void versatile_init(int ram_size, int vga_ram_size,
+     /*  0x101f3000 UART2.  */
+     /* 0x101f4000 SSPI.  */
+-    arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
+-                    initrd_filename, board_id, 0x0);
++    versatile_binfo.ram_size = ram_size;
++    versatile_binfo.kernel_filename = kernel_filename;
++    versatile_binfo.kernel_cmdline = kernel_cmdline;
++    versatile_binfo.initrd_filename = initrd_filename;
++    versatile_binfo.board_id = board_id;
++    arm_load_kernel(env, &versatile_binfo);
+ }
+ static void vpb_init(int ram_size, int vga_ram_size,
+diff --git a/softmmu_template.h b/softmmu_template.h
+index 0a4bc7e..d480f34 100644
+--- a/softmmu_template.h
++++ b/softmmu_template.h
+@@ -51,12 +51,15 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                         int mmu_idx,
+                                                         void *retaddr);
+ static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
+-                                              target_ulong tlb_addr)
++                                              target_ulong tlb_addr,
++                                              target_ulong tlb_io)
+ {
+     DATA_TYPE res;
+     int index;
+-    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
++    index = (tlb_addr & ~TARGET_PAGE_MASK) >> IO_MEM_SHIFT;
++    if (index > 4)
++        index = (tlb_io >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ #if SHIFT <= 2
+     res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
+ #else
+@@ -95,7 +98,9 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+             /* IO access */
+             if ((addr & (DATA_SIZE - 1)) != 0)
+                 goto do_unaligned_access;
+-            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
++            res = glue(io_read, SUFFIX)(physaddr, tlb_addr,
++                                        env->tlb_table[mmu_idx]
++                                        [index].addr_code);
+         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+             /* slow unaligned access (it spans two pages or IO) */
+         do_unaligned_access:
+@@ -147,7 +152,9 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+             /* IO access */
+             if ((addr & (DATA_SIZE - 1)) != 0)
+                 goto do_unaligned_access;
+-            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
++            res = glue(io_read, SUFFIX)(physaddr, tlb_addr,
++                                        env->tlb_table[mmu_idx]
++                                        [index].addr_code);
+         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+         do_unaligned_access:
+             /* slow unaligned access (it spans two pages) */
+@@ -186,11 +193,14 @@ static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
+                                           DATA_TYPE val,
+                                           target_ulong tlb_addr,
+-                                          void *retaddr)
++                                          void *retaddr,
++                                          target_ulong tlb_io)
+ {
+     int index;
+-    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
++    index = (tlb_addr & ~TARGET_PAGE_MASK) >> IO_MEM_SHIFT;
++    if (index > 4)
++        index = (tlb_io >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+     env->mem_write_vaddr = tlb_addr;
+     env->mem_write_pc = (unsigned long)retaddr;
+ #if SHIFT <= 2
+@@ -228,7 +238,8 @@ void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+             if ((addr & (DATA_SIZE - 1)) != 0)
+                 goto do_unaligned_access;
+             retaddr = GETPC();
+-            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
++            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr,
++                                   env->tlb_table[mmu_idx][index].addr_code);
+         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+         do_unaligned_access:
+             retaddr = GETPC();
+@@ -278,7 +289,8 @@ static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+             /* IO access */
+             if ((addr & (DATA_SIZE - 1)) != 0)
+                 goto do_unaligned_access;
+-            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
++            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr,
++                                   env->tlb_table[mmu_idx][index].addr_code);
+         } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+         do_unaligned_access:
+             /* XXX: not efficient, but simple */
+diff --git a/target-arm/cpu.h b/target-arm/cpu.h
+index b284a21..633b335 100644
+--- a/target-arm/cpu.h
++++ b/target-arm/cpu.h
+@@ -198,12 +198,16 @@ typedef struct CPUARMState {
+     CPU_COMMON
+     /* These fields after the common ones so they are preserved on reset.  */
+-    int ram_size;
+-    const char *kernel_filename;
+-    const char *kernel_cmdline;
+-    const char *initrd_filename;
+-    int board_id;
+-    target_phys_addr_t loader_start;
++    struct arm_boot_info {
++        int ram_size;
++        const char *kernel_filename;
++        const char *kernel_cmdline;
++        const char *initrd_filename;
++        target_phys_addr_t loader_start;
++        int nb_cpus;
++        int board_id;
++        int (*atag_board)(struct arm_boot_info *info, void *p);
++    } *boot_info;
+ } CPUARMState;
+ CPUARMState *cpu_arm_init(const char *cpu_model);
+@@ -377,6 +381,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+ #define ARM_CPUID_PXA270_C0   0x69054114
+ #define ARM_CPUID_PXA270_C5   0x69054117
+ #define ARM_CPUID_ARM1136     0x4117b363
++#define ARM_CPUID_ARM1136_R2  0x4107b362
+ #define ARM_CPUID_ARM11MPCORE 0x410fb022
+ #define ARM_CPUID_CORTEXA8    0x410fc080
+ #define ARM_CPUID_CORTEXM3    0x410fc231
+diff --git a/target-arm/helper.c b/target-arm/helper.c
+index 86470db..0709129 100644
+--- a/target-arm/helper.c
++++ b/target-arm/helper.c
+@@ -53,6 +53,9 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
+         env->cp15.c0_cachetype = 0x1dd20d2;
+         env->cp15.c1_sys = 0x00090078;
+         break;
++    case ARM_CPUID_ARM1136_R2:
++        /* TODO! */
++        env->GE = 0x5;
+     case ARM_CPUID_ARM1136:
+         set_feature(env, ARM_FEATURE_V6);
+         set_feature(env, ARM_FEATURE_VFP);
+@@ -198,6 +201,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
+     { ARM_CPUID_ARM946, "arm946"},
+     { ARM_CPUID_ARM1026, "arm1026"},
+     { ARM_CPUID_ARM1136, "arm1136"},
++    { ARM_CPUID_ARM1136_R2, "arm1136-r2"},
+     { ARM_CPUID_ARM11MPCORE, "arm11mpcore"},
+     { ARM_CPUID_CORTEXM3, "cortex-m3"},
+     { ARM_CPUID_CORTEXA8, "cortex-a8"},
+@@ -1539,6 +1543,7 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
+             case ARM_CPUID_ARM1026:
+                 return 1;
+             case ARM_CPUID_ARM1136:
++            case ARM_CPUID_ARM1136_R2:
+                 return 7;
+             case ARM_CPUID_ARM11MPCORE:
+                 return 1;
+@@ -1721,6 +1726,10 @@ uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
+             case 8: /* TI925T_status */
+                 return 0;
+             }
++            /* TODO: Peripheral port remap register:
++             * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt
++             * controller base address at $rn & ~0xfff and map size of
++             * 0x200 << ($rn & 0xfff), when MMU is off.  */
+             goto bad_reg;
+         }
+         return 0;
+diff --git a/vl.c b/vl.c
+index d371af7..76d8def 100644
+--- a/vl.c
++++ b/vl.c
+@@ -8006,6 +8006,7 @@ static void register_machines(void)
+     qemu_register_machine(&borzoipda_machine);
+     qemu_register_machine(&terrierpda_machine);
+     qemu_register_machine(&palmte_machine);
++    qemu_register_machine(&n800_machine);
+     qemu_register_machine(&lm3s811evb_machine);
+     qemu_register_machine(&lm3s6965evb_machine);
+     qemu_register_machine(&connex_machine);
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/series b/meta/packages/qemu/qemu-0.9.1+svnr4027/series
new file mode 100644 (file)
index 0000000..126da88
--- /dev/null
@@ -0,0 +1,25 @@
+02_snapshot_use_tmpdir.patch -p0
+05_non-fatal_if_linux_hd_missing.patch -p1
+06_exit_segfault.patch -p0
+10_signal_jobs.patch -p0
+11_signal_sigaction.patch -p0
+22_net_tuntap_stall.patch -p0
+31_syscalls.patch -p0
+32_syscall_sysctl.patch -p0
+33_syscall_ppc_clone.patch -p0
+39_syscall_fadvise64.patch -p0
+41_arm_fpa_sigfpe.patch -p0
+52_ne2000_return.patch -p1
+61_safe_64bit_int.patch -p0
+63_sparc_build.patch -p0
+64_ppc_asm_constraints.patch -p1
+65_kfreebsd.patch -p0
+66_tls_ld.patch -p0
+91-oh-sdl-cursor.patch -p0
+qemu-0.9.0-nptl.patch -p1
+qemu-0.9.0-nptl-update.patch -p1
+qemu-amd64-32b-mapping-0.9.0.patch -p1
+workaround_bad_futex_headers.patch -p1
+fix_segfault.patch -p1
+no-strip.patch -p1
+qemu-n800-support.patch -p1
diff --git a/meta/packages/qemu/qemu-0.9.1+svnr4027/workaround_bad_futex_headers.patch b/meta/packages/qemu/qemu-0.9.1+svnr4027/workaround_bad_futex_headers.patch
new file mode 100644 (file)
index 0000000..cc122eb
--- /dev/null
@@ -0,0 +1,25 @@
+---
+ linux-user/syscall.c |   10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+Index: qemu/linux-user/syscall.c
+===================================================================
+--- qemu.orig/linux-user/syscall.c     2007-08-09 20:28:06.000000000 +0100
++++ qemu/linux-user/syscall.c  2007-08-09 20:28:41.000000000 +0100
+@@ -61,7 +61,15 @@
+ #define tchars host_tchars /* same as target */
+ #define ltchars host_ltchars /* same as target */
+-#include <linux/futex.h>
++#define FUTEX_WAIT              0
++#define FUTEX_WAKE              1
++#define FUTEX_FD                2
++#define FUTEX_REQUEUE           3
++#define FUTEX_CMP_REQUEUE       4
++#define FUTEX_WAKE_OP           5
++#define FUTEX_LOCK_PI           6
++#define FUTEX_UNLOCK_PI         7
++
+ #include <linux/termios.h>
+ #include <linux/unistd.h>
+ #include <linux/utsname.h>
index 5400afb055a6665d36a0779f4a0e9a812b688a15..a51e437f77c1df6f0e3d15737db21e368350a935 100644 (file)
@@ -7,6 +7,7 @@ FILESDIR = "${WORKDIR}"
 
 SRC_URI = "\
     http://fabrice.bellard.free.fr/qemu/qemu-0.9.1.tar.gz \
+    file://02_snapshot_use_tmpdir.patch;patch=1;pnum=0 \
     file://04_do_not_print_rtc_freq_if_ok.patch;patch=1;pnum=1 \
     file://05_non-fatal_if_linux_hd_missing.patch;patch=1;pnum=1 \
     file://06_exit_segfault.patch;patch=1;pnum=0 \
index 1fc9446877d21440ac3609a6e3019cb0ad9ca668..3ff3f4413b1771475688c3f1717e99c84890ef2f 100644 (file)
@@ -1,12 +1,13 @@
 LICENSE = "GPL"
 DEPENDS = "zlib"
 PV = "0.9.1+svnr${SRCREV}"
-PR = "r7"
+PR = "r8"
 
-FILESPATH = "${FILE_DIRNAME}/qemu-0.9.1+svn/"
+FILESPATH = "${FILE_DIRNAME}/qemu-${PV}/:${FILE_DIRNAME}/qemu-0.9.1+svn/"
 
 SRC_URI = "\
     svn://svn.savannah.nongnu.org/qemu;module=trunk \
+    file://02_snapshot_use_tmpdir.patch;patch=1;pnum=0;maxrev=4028 \
     file://05_non-fatal_if_linux_hd_missing.patch;patch=1;pnum=1 \
     file://06_exit_segfault.patch;patch=1;pnum=0 \
     file://10_signal_jobs.patch;patch=1;pnum=0 \
@@ -16,6 +17,7 @@ SRC_URI = "\
     file://32_syscall_sysctl.patch;patch=1;pnum=0 \
     file://33_syscall_ppc_clone.patch;patch=1;pnum=0 \
     file://39_syscall_fadvise64.patch;patch=1;pnum=0 \
+    file://41_arm_fpa_sigfpe.patch;patch=1;pnum=0;maxrev=4028 \
     file://52_ne2000_return.patch;patch=1;pnum=1 \
     file://61_safe_64bit_int.patch;patch=1;pnum=0 \
     file://63_sparc_build.patch;patch=1;pnum=0 \
@@ -24,6 +26,7 @@ SRC_URI = "\
     file://66_tls_ld.patch;patch=1;pnum=0 \
     file://91-oh-sdl-cursor.patch;patch=1;pnum=0 \
     file://qemu-0.9.0-nptl.patch;patch=1 \
+    file://qemu-0.9.0-nptl-update.patch;patch=1;maxrev=4028 \
     file://qemu-amd64-32b-mapping-0.9.0.patch;patch=1 \
     file://workaround_bad_futex_headers.patch;patch=1 \
     file://fix_segfault.patch;patch=1 \