aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard@openedhand.com>2008-04-10 09:50:09 +0000
committerRichard Purdie <richard@openedhand.com>2008-04-10 09:50:09 +0000
commit5091c0bd8842b2a5f94e47c391dc6fe73eac8901 (patch)
tree48288911ebbed8f7b557a3a828b340f06e155056
parent37ddd0087fac5868851113d3d7b11c262653f782 (diff)
downloadopenembedded-core-5091c0bd8842b2a5f94e47c391dc6fe73eac8901.tar.gz
qemu-cvs: Add nokia800 emulation
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@4225 311d38ba-8fff-0310-9ca6-ca027cbcb966
-rw-r--r--meta/packages/qemu/qemu-native-0.9.1+cvs20080307/qemu-n800-support.patch13970
-rw-r--r--meta/packages/qemu/qemu-native-0.9.1+cvs20080307/series1
-rw-r--r--meta/packages/qemu/qemu_cvs.bb5
3 files changed, 13974 insertions, 2 deletions
diff --git a/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/qemu-n800-support.patch b/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/qemu-n800-support.patch
new file mode 100644
index 0000000000..b1b6649efc
--- /dev/null
+++ b/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/qemu-n800-support.patch
@@ -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-native-0.9.1+cvs20080307/series b/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/series
index 9f6ca0cd5d..126da88288 100644
--- a/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/series
+++ b/meta/packages/qemu/qemu-native-0.9.1+cvs20080307/series
@@ -22,3 +22,4 @@ 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_cvs.bb b/meta/packages/qemu/qemu_cvs.bb
index 4af21f4a89..dd74aeee61 100644
--- a/meta/packages/qemu/qemu_cvs.bb
+++ b/meta/packages/qemu/qemu_cvs.bb
@@ -1,7 +1,7 @@
LICENSE = "GPL"
DEPENDS = "zlib"
PV = "0.9.1+cvs${SRCDATE}"
-PR = "r1"
+PR = "r2"
DEFAULT_PREFERENCE = "-1"
FILESDIR = "${WORKDIR}"
@@ -31,7 +31,8 @@ SRC_URI = "\
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 \
- file://no-strip.patch;patch=1"
+ file://no-strip.patch;patch=1 \
+ file://qemu-n800-support.patch;patch=1"
S = "${WORKDIR}/qemu"