From 4af396115a1722dfaf8f7d4ecee59202dd7d5ea8 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Thu, 14 May 2009 23:11:09 +0100 Subject: [PATCH] Syborg (Symbian Virtual Platform) board A virtual reference platform for SymbianOS development/debugging. Signed-off-by: Paul Brook --- Makefile.target | 2 + hw/boards.h | 3 + hw/syborg.c | 91 ++++++++ hw/syborg.h | 18 ++ hw/syborg_fb.c | 547 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/syborg_interrupt.c | 227 ++++++++++++++++++++ hw/syborg_keyboard.c | 234 +++++++++++++++++++++ hw/syborg_pointer.c | 233 +++++++++++++++++++++ hw/syborg_rtc.c | 147 +++++++++++++ hw/syborg_serial.c | 349 +++++++++++++++++++++++++++++++ hw/syborg_timer.c | 235 +++++++++++++++++++++ qemu-doc.texi | 23 +++ target-arm/machine.c | 1 + 13 files changed, 2110 insertions(+) create mode 100644 hw/syborg.c create mode 100644 hw/syborg.h create mode 100644 hw/syborg_fb.c create mode 100644 hw/syborg_interrupt.c create mode 100644 hw/syborg_keyboard.c create mode 100644 hw/syborg_pointer.c create mode 100644 hw/syborg_rtc.c create mode 100644 hw/syborg_serial.c create mode 100644 hw/syborg_timer.c diff --git a/Makefile.target b/Makefile.target index 6bfd5fb..dd1c442 100644 --- a/Makefile.target +++ b/Makefile.target @@ -655,6 +655,8 @@ OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o OBJS+= mst_fpga.o mainstone.o OBJS+= musicpal.o pflash_cfi02.o OBJS+= framebuffer.o +OBJS+= syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o +OBJS+= syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o CPPFLAGS += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), sh4) diff --git a/hw/boards.h b/hw/boards.h index 3866e93..9a99a85 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -128,4 +128,7 @@ extern QEMUMachine musicpal_machine; /* tosa.c */ extern QEMUMachine tosapda_machine; +/* syborg.c */ +extern QEMUMachine syborg_machine; + #endif diff --git a/hw/syborg.c b/hw/syborg.c new file mode 100644 index 0000000..e54fc95 --- /dev/null +++ b/hw/syborg.c @@ -0,0 +1,91 @@ +/* + * Syborg (Symbian Virtual Platform) reference board + * + * Copyright (c) 2009 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "boards.h" +#include "arm-misc.h" +#include "sysemu.h" + +static struct arm_boot_info syborg_binfo; + +static void syborg_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + qemu_irq *cpu_pic; + qemu_irq pic[64]; + ram_addr_t ram_addr; + DeviceState *dev; + int i; + + if (!cpu_model) + cpu_model = "cortex-a8"; + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + /* RAM at address zero. */ + ram_addr = qemu_ram_alloc(ram_size); + cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM); + + cpu_pic = arm_pic_init_cpu(env); + dev = sysbus_create_simple("syborg,interrupt", 0xC0000000, + cpu_pic[ARM_PIC_CPU_IRQ]); + for (i = 0; i < 64; i++) { + pic[i] = qdev_get_irq_sink(dev, i); + } + + sysbus_create_simple("syborg,rtc", 0xC0001000, NULL); + + dev = qdev_create(NULL, "syborg,timer"); + qdev_set_prop_int(dev, "frequency", 1000000); + qdev_init(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xC0002000); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[1]); + + sysbus_create_simple("syborg,keyboard", 0xC0003000, pic[2]); + sysbus_create_simple("syborg,pointer", 0xC0004000, pic[3]); + sysbus_create_simple("syborg,framebuffer", 0xC0005000, pic[4]); + sysbus_create_simple("syborg,serial", 0xC0006000, pic[5]); + sysbus_create_simple("syborg,serial", 0xC0007000, pic[6]); + sysbus_create_simple("syborg,serial", 0xC0008000, pic[7]); + sysbus_create_simple("syborg,serial", 0xC0009000, pic[8]); + + syborg_binfo.ram_size = ram_size; + syborg_binfo.kernel_filename = kernel_filename; + syborg_binfo.kernel_cmdline = kernel_cmdline; + syborg_binfo.initrd_filename = initrd_filename; + syborg_binfo.board_id = 0; + arm_load_kernel(env, &syborg_binfo); +} + +QEMUMachine syborg_machine = { + .name = "syborg", + .desc = "Syborg (Symbian Virtual Platform)", + .init = syborg_init, +}; diff --git a/hw/syborg.h b/hw/syborg.h new file mode 100644 index 0000000..b82ce4a --- /dev/null +++ b/hw/syborg.h @@ -0,0 +1,18 @@ +#ifndef _SYBORG_H +#define _SYBORG_H + +#define SYBORG_ID_PLATFORM 0xc51d1000 +#define SYBORG_ID_INT 0xc51d0000 +#define SYBORG_ID_SERIAL 0xc51d0001 +#define SYBORG_ID_KEYBOARD 0xc51d0002 +#define SYBORG_ID_TIMER 0xc51d0003 +#define SYBORG_ID_RTC 0xc51d0004 +#define SYBORG_ID_MOUSE 0xc51d0005 +#define SYBORG_ID_TOUCHSCREEN 0xc51d0006 +#define SYBORG_ID_FRAMEBUFFER 0xc51d0007 +#define SYBORG_ID_HOSTFS 0xc51d0008 +#define SYBORG_ID_SNAPSHOT 0xc51d0009 +#define SYBORG_ID_VIRTIO 0xc51d000a +#define SYBORG_ID_NAND 0xc51d000b + +#endif diff --git a/hw/syborg_fb.c b/hw/syborg_fb.c new file mode 100644 index 0000000..90254e4 --- /dev/null +++ b/hw/syborg_fb.c @@ -0,0 +1,547 @@ +/* + * Syborg Framebuffer + * + * Copyright (c) 2009 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "console.h" +#include "syborg.h" +#include "framebuffer.h" + +//#define DEBUG_SYBORG_FB + +#ifdef DEBUG_SYBORG_FB +#define DPRINTF(fmt, ...) \ +do { printf("syborg_fb: " fmt , ## __VA_ARGS__); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__); \ + exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__);} while (0) +#endif + +enum { + FB_ID = 0, + FB_BASE = 1, + FB_HEIGHT = 2, + FB_WIDTH = 3, + FB_ORIENTATION = 4, + FB_BLANK = 5, + FB_INT_MASK = 6, + FB_INTERRUPT_CAUSE = 7, + FB_BPP = 8, + FB_COLOR_ORDER = 9, + FB_BYTE_ORDER = 10, + FB_PIXEL_ORDER = 11, + FB_ROW_PITCH = 12, + FB_ENABLED = 13, + FB_PALETTE_START = 0x400 >> 2, + FB_PALETTE_END = FB_PALETTE_START+256-1, +}; + +#define FB_INT_VSYNC (1U << 0) +#define FB_INT_BASE_UPDATE_DONE (1U << 1) + +typedef struct { + SysBusDevice busdev; + DisplayState *ds; + /*QEMUConsole *console;*/ + uint32_t need_update : 1; + uint32_t need_int : 1; + uint32_t enabled : 1; + uint32_t int_status; + uint32_t int_enable; + qemu_irq irq; + + uint32_t base; + uint32_t pitch; + int rows; + int cols; + int blank; + int bpp; + int rgb; /* 0 = BGR, 1 = RGB */ + int endian; /* 0 = Little, 1 = Big */ + uint32_t raw_palette[256]; + uint32_t palette[256]; +} SyborgFBState; + +enum { + BPP_SRC_1, + BPP_SRC_2, + BPP_SRC_4, + BPP_SRC_8, + BPP_SRC_16, + BPP_SRC_32, + /* TODO: Implement these. */ + BPP_SRC_15 = -1, + BPP_SRC_24 = -2 +}; + +#include "pixel_ops.h" + +#define BITS 8 +#include "pl110_template.h" +#define BITS 15 +#include "pl110_template.h" +#define BITS 16 +#include "pl110_template.h" +#define BITS 24 +#include "pl110_template.h" +#define BITS 32 +#include "pl110_template.h" + +/* Update interrupts. */ +static void syborg_fb_update(SyborgFBState *s) +{ + if ((s->int_status & s->int_enable) != 0) { + DPRINTF("Raise IRQ\n"); + qemu_irq_raise(s->irq); + } else { + DPRINTF("Lower IRQ\n"); + qemu_irq_lower(s->irq); + } +} + +static int syborg_fb_enabled(const SyborgFBState *s) +{ + return s->enabled; +} + +static void syborg_fb_update_palette(SyborgFBState *s) +{ + int n, i; + uint32_t raw; + unsigned int r, g, b; + + switch (s->bpp) { + case BPP_SRC_1: n = 2; break; + case BPP_SRC_2: n = 4; break; + case BPP_SRC_4: n = 16; break; + case BPP_SRC_8: n = 256; break; + default: return; + } + + for (i = 0; i < n; i++) { + raw = s->raw_palette[i]; + r = (raw >> 16) & 0xff; + g = (raw >> 8) & 0xff; + b = raw & 0xff; + switch (ds_get_bits_per_pixel(s->ds)) { + case 8: + s->palette[i] = rgb_to_pixel8(r, g, b); + break; + case 15: + s->palette[i] = rgb_to_pixel15(r, g, b); + break; + case 16: + s->palette[i] = rgb_to_pixel16(r, g, b); + break; + case 24: + case 32: + s->palette[i] = rgb_to_pixel32(r, g, b); + break; + default: + abort(); + } + } + +} + +static void syborg_fb_update_display(void *opaque) +{ + SyborgFBState *s = (SyborgFBState *)opaque; + drawfn* fntable; + drawfn fn; + int dest_width; + int src_width; + int bpp_offset; + int first; + int last; + + if (!syborg_fb_enabled(s)) + return; + + switch (ds_get_bits_per_pixel(s->ds)) { + case 0: + return; + case 8: + fntable = pl110_draw_fn_8; + dest_width = 1; + break; + case 15: + fntable = pl110_draw_fn_15; + dest_width = 2; + break; + case 16: + fntable = pl110_draw_fn_16; + dest_width = 2; + break; + case 24: + fntable = pl110_draw_fn_24; + dest_width = 3; + break; + case 32: + fntable = pl110_draw_fn_32; + dest_width = 4; + break; + default: + fprintf(stderr, "syborg_fb: Bad color depth\n"); + exit(1); + } + + if (s->need_int) { + s->int_status |= FB_INT_BASE_UPDATE_DONE; + syborg_fb_update(s); + s->need_int = 0; + } + + if (s->rgb) { + bpp_offset = 18; + } else { + bpp_offset = 0; + } + if (s->endian) { + bpp_offset += 6; + } + + fn = fntable[s->bpp + bpp_offset]; + + if (s->pitch) { + src_width = s->pitch; + } else { + src_width = s->cols; + switch (s->bpp) { + case BPP_SRC_1: + src_width >>= 3; + break; + case BPP_SRC_2: + src_width >>= 2; + break; + case BPP_SRC_4: + src_width >>= 1; + break; + case BPP_SRC_8: + break; + case BPP_SRC_15: + case BPP_SRC_16: + src_width <<= 1; + break; + case BPP_SRC_24: + src_width *= 3; + break; + case BPP_SRC_32: + src_width <<= 2; + break; + } + } + dest_width *= s->cols; + first = 0; + /* TODO: Implement blanking. */ + if (!s->blank) { + if (s->need_update && s->bpp <= BPP_SRC_8) { + syborg_fb_update_palette(s); + } + framebuffer_update_display(s->ds, + s->base, s->cols, s->rows, + src_width, dest_width, 0, + s->need_update, + fn, s->palette, + &first, &last); + if (first >= 0) { + dpy_update(s->ds, 0, first, s->cols, last - first + 1); + } + + s->int_status |= FB_INT_VSYNC; + syborg_fb_update(s); + } + + s->need_update = 0; +} + +static void syborg_fb_invalidate_display(void * opaque) +{ + SyborgFBState *s = (SyborgFBState *)opaque; + s->need_update = 1; +} + +static uint32_t syborg_fb_read(void *opaque, target_phys_addr_t offset) +{ + SyborgFBState *s = opaque; + + DPRINTF("read reg %d\n", (int)offset); + offset &= 0xfff; + switch (offset >> 2) { + case FB_ID: + return SYBORG_ID_FRAMEBUFFER; + + case FB_BASE: + return s->base; + + case FB_HEIGHT: + return s->rows; + + case FB_WIDTH: + return s->cols; + + case FB_ORIENTATION: + return 0; + + case FB_BLANK: + return s->blank; + + case FB_INT_MASK: + return s->int_enable; + + case FB_INTERRUPT_CAUSE: + return s->int_status; + + case FB_BPP: + switch (s->bpp) { + case BPP_SRC_1: return 1; + case BPP_SRC_2: return 2; + case BPP_SRC_4: return 4; + case BPP_SRC_8: return 8; + case BPP_SRC_15: return 15; + case BPP_SRC_16: return 16; + case BPP_SRC_24: return 24; + case BPP_SRC_32: return 32; + default: return 0; + } + + case FB_COLOR_ORDER: + return s->rgb; + + case FB_BYTE_ORDER: + return s->endian; + + case FB_PIXEL_ORDER: + return 0; + + case FB_ROW_PITCH: + return s->pitch; + + case FB_ENABLED: + return s->enabled; + + default: + if ((offset >> 2) >= FB_PALETTE_START + && (offset >> 2) <= FB_PALETTE_END) { + return s->raw_palette[(offset >> 2) - FB_PALETTE_START]; + } else { + cpu_abort (cpu_single_env, "syborg_fb_read: Bad offset %x\n", + (int)offset); + } + return 0; + } +} + +static void syborg_fb_write(void *opaque, target_phys_addr_t offset, + uint32_t val) +{ + SyborgFBState *s = opaque; + + DPRINTF("write reg %d = %d\n", (int)offset, val); + s->need_update = 1; + offset &= 0xfff; + switch (offset >> 2) { + case FB_BASE: + s->base = val; + s->need_int = 1; + s->need_update = 1; + syborg_fb_update(s); + break; + + case FB_HEIGHT: + s->rows = val; + break; + + case FB_WIDTH: + s->cols = val; + break; + + case FB_ORIENTATION: + /* TODO: Implement rotation. */ + break; + + case FB_BLANK: + s->blank = val & 1; + break; + + case FB_INT_MASK: + s->int_enable = val; + syborg_fb_update(s); + break; + + case FB_INTERRUPT_CAUSE: + s->int_status &= ~val; + syborg_fb_update(s); + break; + + case FB_BPP: + switch (val) { + case 1: val = BPP_SRC_1; break; + case 2: val = BPP_SRC_2; break; + case 4: val = BPP_SRC_4; break; + case 8: val = BPP_SRC_8; break; + /* case 15: val = BPP_SRC_15; break; */ + case 16: val = BPP_SRC_16; break; + /* case 24: val = BPP_SRC_24; break; */ + case 32: val = BPP_SRC_32; break; + default: val = s->bpp; break; + } + s->bpp = val; + break; + + case FB_COLOR_ORDER: + s->rgb = (val != 0); + break; + + case FB_BYTE_ORDER: + s->endian = (val != 0); + break; + + case FB_PIXEL_ORDER: + /* TODO: Implement this. */ + break; + + case FB_ROW_PITCH: + s->pitch = val; + break; + + case FB_ENABLED: + s->enabled = val; + break; + + default: + if ((offset >> 2) >= FB_PALETTE_START + && (offset >> 2) <= FB_PALETTE_END) { + s->raw_palette[(offset >> 2) - FB_PALETTE_START] = val; + } else { + cpu_abort (cpu_single_env, "syborg_fb_write: Bad offset %x\n", + (int)offset); + } + break; + } +} + +static CPUReadMemoryFunc *syborg_fb_readfn[] = { + syborg_fb_read, + syborg_fb_read, + syborg_fb_read +}; + +static CPUWriteMemoryFunc *syborg_fb_writefn[] = { + syborg_fb_write, + syborg_fb_write, + syborg_fb_write +}; + +static void syborg_fb_save(QEMUFile *f, void *opaque) +{ + SyborgFBState *s = opaque; + int i; + + qemu_put_be32(f, s->need_int); + qemu_put_be32(f, s->int_status); + qemu_put_be32(f, s->int_enable); + qemu_put_be32(f, s->enabled); + qemu_put_be32(f, s->base); + qemu_put_be32(f, s->pitch); + qemu_put_be32(f, s->rows); + qemu_put_be32(f, s->cols); + qemu_put_be32(f, s->bpp); + qemu_put_be32(f, s->rgb); + for (i = 0; i < 256; i++) { + qemu_put_be32(f, s->raw_palette[i]); + } +} + +static int syborg_fb_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgFBState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + s->need_int = qemu_get_be32(f); + s->int_status = qemu_get_be32(f); + s->int_enable = qemu_get_be32(f); + s->enabled = qemu_get_be32(f); + s->base = qemu_get_be32(f); + s->pitch = qemu_get_be32(f); + s->rows = qemu_get_be32(f); + s->cols = qemu_get_be32(f); + s->bpp = qemu_get_be32(f); + s->rgb = qemu_get_be32(f); + for (i = 0; i < 256; i++) { + s->raw_palette[i] = qemu_get_be32(f); + } + s->need_update = 1; + + return 0; +} + +static void syborg_fb_init(SysBusDevice *dev) +{ + SyborgFBState *s = FROM_SYSBUS(SyborgFBState, dev); + int iomemtype; + int width; + int height; + + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(0, syborg_fb_readfn, + syborg_fb_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + width = qdev_get_prop_int(&dev->qdev, "width", 0); + height = qdev_get_prop_int(&dev->qdev, "height", 0); + + s->ds = graphic_console_init(syborg_fb_update_display, + syborg_fb_invalidate_display, + NULL, NULL, s); + + if (width != 0 && height != 0) { + qemu_console_resize(s->ds, width, height); + } + + if (!width) + width = ds_get_width(s->ds); + if (!height) + height = ds_get_height(s->ds); + + s->cols = width; + s->rows = height; + + register_savevm("syborg_framebuffer", -1, 1, + syborg_fb_save, syborg_fb_load, s); +} + +static void syborg_fb_register_devices(void) +{ + sysbus_register_dev("syborg,framebuffer", sizeof(SyborgFBState), + syborg_fb_init); +} + +device_init(syborg_fb_register_devices) diff --git a/hw/syborg_interrupt.c b/hw/syborg_interrupt.c new file mode 100644 index 0000000..e3fbbf7 --- /dev/null +++ b/hw/syborg_interrupt.c @@ -0,0 +1,227 @@ +/* + * Syborg interrupt controller. + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "syborg.h" + +//#define DEBUG_SYBORG_INT + +#ifdef DEBUG_SYBORG_INT +#define DPRINTF(fmt, ...) \ +do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \ + exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0) +#endif +enum { + INT_ID = 0, + INT_STATUS = 1, /* number of pending interrupts */ + INT_CURRENT = 2, /* next interrupt to be serviced */ + INT_DISABLE_ALL = 3, + INT_DISABLE = 4, + INT_ENABLE = 5, + INT_TOTAL = 6 +}; + +typedef struct { + unsigned level:1; + unsigned enabled:1; +} syborg_int_flags; + +typedef struct { + SysBusDevice busdev; + int pending_count; + int num_irqs; + syborg_int_flags *flags; + qemu_irq parent_irq; +} SyborgIntState; + +static void syborg_int_update(SyborgIntState *s) +{ + DPRINTF("pending %d\n", s->pending_count); + qemu_set_irq(s->parent_irq, s->pending_count > 0); +} + +static void syborg_int_set_irq(void *opaque, int irq, int level) +{ + SyborgIntState *s = (SyborgIntState *)opaque; + + if (s->flags[irq].level == level) + return; + + s->flags[irq].level = level; + if (s->flags[irq].enabled) { + if (level) + s->pending_count++; + else + s->pending_count--; + syborg_int_update(s); + } +} + +static uint32_t syborg_int_read(void *opaque, target_phys_addr_t offset) +{ + SyborgIntState *s = (SyborgIntState *)opaque; + int i; + + offset &= 0xfff; + switch (offset >> 2) { + case INT_ID: + return SYBORG_ID_INT; + case INT_STATUS: + DPRINTF("read status=%d\n", s->pending_count); + return s->pending_count; + + case INT_CURRENT: + for (i = 0; i < s->num_irqs; i++) { + if (s->flags[i].level & s->flags[i].enabled) { + DPRINTF("read current=%d\n", i); + return i; + } + } + DPRINTF("read current=none\n"); + return 0xffffffffu; + + default: + cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_int_write(void *opaque, target_phys_addr_t offset, uint32_t value) +{ + SyborgIntState *s = (SyborgIntState *)opaque; + int i; + offset &= 0xfff; + + DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value); + switch (offset >> 2) { + case INT_DISABLE_ALL: + s->pending_count = 0; + for (i = 0; i < s->num_irqs; i++) + s->flags[i].enabled = 0; + break; + + case INT_DISABLE: + if (value >= s->num_irqs) + break; + if (s->flags[value].enabled) { + if (s->flags[value].enabled) + s->pending_count--; + s->flags[value].enabled = 0; + } + break; + + case INT_ENABLE: + if (value >= s->num_irqs) + break; + if (!(s->flags[value].enabled)) { + if(s->flags[value].level) + s->pending_count++; + s->flags[value].enabled = 1; + } + break; + + default: + cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n", + (int)offset); + return; + } + syborg_int_update(s); +} + +static CPUReadMemoryFunc *syborg_int_readfn[] = { + syborg_int_read, + syborg_int_read, + syborg_int_read +}; + +static CPUWriteMemoryFunc *syborg_int_writefn[] = { + syborg_int_write, + syborg_int_write, + syborg_int_write +}; + +static void syborg_int_save(QEMUFile *f, void *opaque) +{ + SyborgIntState *s = (SyborgIntState *)opaque; + int i; + + qemu_put_be32(f, s->num_irqs); + qemu_put_be32(f, s->pending_count); + for (i = 0; i < s->num_irqs; i++) { + qemu_put_be32(f, s->flags[i].enabled + | ((unsigned)s->flags[i].level << 1)); + } +} + +static int syborg_int_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgIntState *s = (SyborgIntState *)opaque; + uint32_t val; + int i; + + if (version_id != 1) + return -EINVAL; + + val = qemu_get_be32(f); + if (val != s->num_irqs) + return -EINVAL; + s->pending_count = qemu_get_be32(f); + for (i = 0; i < s->num_irqs; i++) { + val = qemu_get_be32(f); + s->flags[i].enabled = val & 1; + s->flags[i].level = (val >> 1) & 1; + } + return 0; +} + +static void syborg_int_init(SysBusDevice *dev) +{ + SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->parent_irq); + s->num_irqs = qdev_get_prop_int(&dev->qdev, "num-interrupts", 64); + qdev_init_irq_sink(&dev->qdev, syborg_int_set_irq, s->num_irqs); + iomemtype = cpu_register_io_memory(0, syborg_int_readfn, + syborg_int_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + s->flags = qemu_mallocz(s->num_irqs * sizeof(syborg_int_flags)); + + register_savevm("syborg_int", -1, 1, syborg_int_save, syborg_int_load, s); +} + +static void syborg_interrupt_register_devices(void) +{ + sysbus_register_dev("syborg,interrupt", sizeof(SyborgIntState), + syborg_int_init); +} + +device_init(syborg_interrupt_register_devices) diff --git a/hw/syborg_keyboard.c b/hw/syborg_keyboard.c new file mode 100644 index 0000000..69976e4 --- /dev/null +++ b/hw/syborg_keyboard.c @@ -0,0 +1,234 @@ +/* + * Syborg keyboard controller. + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "console.h" +#include "syborg.h" + +//#define DEBUG_SYBORG_KEYBOARD + +#ifdef DEBUG_SYBORG_KEYBOARD +#define DPRINTF(fmt, ...) \ +do { printf("syborg_keyboard: " fmt , ##args); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \ + exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \ +} while (0) +#endif + +enum { + KBD_ID = 0, + KBD_DATA = 1, + KBD_FIFO_COUNT = 2, + KBD_INT_ENABLE = 3, + KBD_FIFO_SIZE = 4 +}; + +typedef struct { + SysBusDevice busdev; + int int_enabled; + int extension_bit; + int fifo_size; + uint32_t *key_fifo; + int read_pos, read_count; + qemu_irq irq; +} SyborgKeyboardState; + +static void syborg_keyboard_update(SyborgKeyboardState *s) +{ + int level = s->read_count && s->int_enabled; + DPRINTF("Update IRQ %d\n", level); + qemu_set_irq(s->irq, level); +} + +static uint32_t syborg_keyboard_read(void *opaque, target_phys_addr_t offset) +{ + SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; + int c; + + DPRINTF("reg read %d\n", (int)offset); + offset &= 0xfff; + switch (offset >> 2) { + case KBD_ID: + return SYBORG_ID_KEYBOARD; + case KBD_FIFO_COUNT: + return s->read_count; + case KBD_DATA: + if (s->read_count == 0) { + c = -1; + DPRINTF("FIFO underflow\n"); + } else { + c = s->key_fifo[s->read_pos]; + DPRINTF("FIFO read 0x%x\n", c); + s->read_count--; + s->read_pos++; + if (s->read_pos == s->fifo_size) + s->read_pos = 0; + } + syborg_keyboard_update(s); + return c; + case KBD_INT_ENABLE: + return s->int_enabled; + case KBD_FIFO_SIZE: + return s->fifo_size; + default: + cpu_abort(cpu_single_env, "syborg_keyboard_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_keyboard_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; + + DPRINTF("reg write %d\n", (int)offset); + offset &= 0xfff; + switch (offset >> 2) { + case KBD_INT_ENABLE: + s->int_enabled = value; + syborg_keyboard_update(s); + break; + default: + cpu_abort(cpu_single_env, "syborg_keyboard_write: Bad offset %x\n", + (int)offset); + } +} + +static CPUReadMemoryFunc *syborg_keyboard_readfn[] = { + syborg_keyboard_read, + syborg_keyboard_read, + syborg_keyboard_read +}; + +static CPUWriteMemoryFunc *syborg_keyboard_writefn[] = { + syborg_keyboard_write, + syborg_keyboard_write, + syborg_keyboard_write +}; + +static void syborg_keyboard_event(void *opaque, int keycode) +{ + SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; + int slot; + uint32_t val; + + /* Strip off 0xe0 prefixes and reconstruct the full scancode. */ + if (keycode == 0xe0 && !s->extension_bit) { + DPRINTF("Extension bit\n"); + s->extension_bit = 0x80; + return; + } + val = (keycode & 0x7f) | s->extension_bit; + if (keycode & 0x80) + val |= 0x80000000u; + s->extension_bit = 0; + + DPRINTF("FIFO push 0x%x\n", val); + slot = s->read_pos + s->read_count; + if (slot >= s->fifo_size) + slot -= s->fifo_size; + + if (s->read_count < s->fifo_size) { + s->read_count++; + s->key_fifo[slot] = val; + } else { + fprintf(stderr, "syborg_keyboard error! FIFO overflow\n"); + } + + syborg_keyboard_update(s); +} + +static void syborg_keyboard_save(QEMUFile *f, void *opaque) +{ + SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; + int i; + + qemu_put_be32(f, s->fifo_size); + qemu_put_be32(f, s->int_enabled); + qemu_put_be32(f, s->extension_bit); + qemu_put_be32(f, s->read_pos); + qemu_put_be32(f, s->read_count); + for (i = 0; i < s->fifo_size; i++) { + qemu_put_be32(f, s->key_fifo[i]); + } +} + +static int syborg_keyboard_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; + uint32_t val; + int i; + + if (version_id != 1) + return -EINVAL; + + val = qemu_get_be32(f); + if (val != s->fifo_size) + return -EINVAL; + + s->int_enabled = qemu_get_be32(f); + s->extension_bit = qemu_get_be32(f); + s->read_pos = qemu_get_be32(f); + s->read_count = qemu_get_be32(f); + for (i = 0; i < s->fifo_size; i++) { + s->key_fifo[i] = qemu_get_be32(f); + } + return 0; +} + +static void syborg_keyboard_init(SysBusDevice *dev) +{ + SyborgKeyboardState *s = FROM_SYSBUS(SyborgKeyboardState, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(0, syborg_keyboard_readfn, + syborg_keyboard_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + s->fifo_size = qdev_get_prop_int(&dev->qdev, "fifo-size", 16); + if (s->fifo_size <= 0) { + fprintf(stderr, "syborg_keyboard: fifo too small\n"); + s->fifo_size = 16; + } + s->key_fifo = qemu_mallocz(s->fifo_size * sizeof(s->key_fifo[0])); + + qemu_add_kbd_event_handler(syborg_keyboard_event, s); + + register_savevm("syborg_keyboard", -1, 1, + syborg_keyboard_save, syborg_keyboard_load, s); +} + +static void syborg_keyboard_register_devices(void) +{ + sysbus_register_dev("syborg,keyboard", sizeof(SyborgKeyboardState), + syborg_keyboard_init); +} + +device_init(syborg_keyboard_register_devices) diff --git a/hw/syborg_pointer.c b/hw/syborg_pointer.c new file mode 100644 index 0000000..e5a72d3 --- /dev/null +++ b/hw/syborg_pointer.c @@ -0,0 +1,233 @@ +/* + * Syborg pointing device (mouse/touchscreen) + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "console.h" +#include "syborg.h" + +enum { + POINTER_ID = 0, + POINTER_LATCH = 1, + POINTER_FIFO_COUNT = 2, + POINTER_X = 3, + POINTER_Y = 4, + POINTER_Z = 5, + POINTER_BUTTONS = 6, + POINTER_INT_ENABLE = 7, + POINTER_FIFO_SIZE = 8 +}; + +typedef struct { + int x, y, z, pointer_buttons; +} event_data; + +typedef struct { + SysBusDevice busdev; + int int_enabled; + int fifo_size; + event_data *event_fifo; + int read_pos, read_count; + qemu_irq irq; + int absolute; +} SyborgPointerState; + +static void syborg_pointer_update(SyborgPointerState *s) +{ + qemu_set_irq(s->irq, s->read_count && s->int_enabled); +} + +static uint32_t syborg_pointer_read(void *opaque, target_phys_addr_t offset) +{ + SyborgPointerState *s = (SyborgPointerState *)opaque; + + offset &= 0xfff; + switch (offset >> 2) { + case POINTER_ID: + return s->absolute ? SYBORG_ID_TOUCHSCREEN : SYBORG_ID_MOUSE; + case POINTER_FIFO_COUNT: + return s->read_count; + case POINTER_X: + return s->event_fifo[s->read_pos].x; + case POINTER_Y: + return s->event_fifo[s->read_pos].y; + case POINTER_Z: + return s->event_fifo[s->read_pos].z; + case POINTER_BUTTONS: + return s->event_fifo[s->read_pos].pointer_buttons; + case POINTER_INT_ENABLE: + return s->int_enabled; + case POINTER_FIFO_SIZE: + return s->fifo_size; + default: + cpu_abort(cpu_single_env, "syborg_pointer_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_pointer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + SyborgPointerState *s = (SyborgPointerState *)opaque; + + offset &= 0xfff; + switch (offset >> 2) { + case POINTER_LATCH: + if (s->read_count > 0) { + s->read_count--; + if (++s->read_pos == s->fifo_size) + s->read_pos = 0; + } + break; + case POINTER_INT_ENABLE: + s->int_enabled = value; + break; + default: + cpu_abort(cpu_single_env, "syborg_pointer_write: Bad offset %x\n", + (int)offset); + } + syborg_pointer_update(s); +} + +static CPUReadMemoryFunc *syborg_pointer_readfn[] = { + syborg_pointer_read, + syborg_pointer_read, + syborg_pointer_read +}; + +static CPUWriteMemoryFunc *syborg_pointer_writefn[] = { + syborg_pointer_write, + syborg_pointer_write, + syborg_pointer_write +}; + +static void syborg_pointer_event(void *opaque, int dx, int dy, int dz, + int buttons_state) +{ + SyborgPointerState *s = (SyborgPointerState *)opaque; + int slot = s->read_pos + s->read_count; + + /* This first FIFO entry is used to store current register state. */ + if (s->read_count < s->fifo_size - 1) { + s->read_count++; + slot++; + } + + if (slot >= s->fifo_size) + slot -= s->fifo_size; + + if (s->read_count == s->fifo_size && !s->absolute) { + /* Merge existing entries. */ + s->event_fifo[slot].x += dx; + s->event_fifo[slot].y += dy; + s->event_fifo[slot].z += dz; + } else { + s->event_fifo[slot].x = dx; + s->event_fifo[slot].y = dy; + s->event_fifo[slot].z = dz; + } + s->event_fifo[slot].pointer_buttons = buttons_state; + + syborg_pointer_update(s); +} + +static void syborg_pointer_save(QEMUFile *f, void *opaque) +{ + SyborgPointerState *s = (SyborgPointerState *)opaque; + int i; + + qemu_put_be32(f, s->fifo_size); + qemu_put_be32(f, s->absolute); + qemu_put_be32(f, s->int_enabled); + qemu_put_be32(f, s->read_pos); + qemu_put_be32(f, s->read_count); + for (i = 0; i < s->fifo_size; i++) { + qemu_put_be32(f, s->event_fifo[i].x); + qemu_put_be32(f, s->event_fifo[i].y); + qemu_put_be32(f, s->event_fifo[i].z); + qemu_put_be32(f, s->event_fifo[i].pointer_buttons); + } +} + +static int syborg_pointer_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgPointerState *s = (SyborgPointerState *)opaque; + uint32_t val; + int i; + + if (version_id != 1) + return -EINVAL; + + val = qemu_get_be32(f); + if (val != s->fifo_size) + return -EINVAL; + + val = qemu_get_be32(f); + if (val != s->absolute) + return -EINVAL; + + s->int_enabled = qemu_get_be32(f); + s->read_pos = qemu_get_be32(f); + s->read_count = qemu_get_be32(f); + for (i = 0; i < s->fifo_size; i++) { + s->event_fifo[i].x = qemu_get_be32(f); + s->event_fifo[i].y = qemu_get_be32(f); + s->event_fifo[i].z = qemu_get_be32(f); + s->event_fifo[i].pointer_buttons = qemu_get_be32(f); + } + return 0; +} + +static void syborg_pointer_init(SysBusDevice *dev) +{ + SyborgPointerState *s = FROM_SYSBUS(SyborgPointerState, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(0, syborg_pointer_readfn, + syborg_pointer_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + s->absolute = qdev_get_prop_int(&dev->qdev, "absolute", 1); + s->fifo_size = qdev_get_prop_int(&dev->qdev, "fifo-size", 16); + if (s->fifo_size <= 0) { + fprintf(stderr, "syborg_pointer: fifo too small\n"); + s->fifo_size = 16; + } + s->event_fifo = qemu_mallocz(s->fifo_size * sizeof(s->event_fifo[0])); + + qemu_add_mouse_event_handler(syborg_pointer_event, s, s->absolute, + "Syborg Pointer"); + + register_savevm("syborg_pointer", -1, 1, + syborg_pointer_save, syborg_pointer_load, s); +} + +static void syborg_pointer_register_devices(void) +{ + sysbus_register_dev("syborg,pointer", sizeof(SyborgPointerState), + syborg_pointer_init); +} + +device_init(syborg_pointer_register_devices) diff --git a/hw/syborg_rtc.c b/hw/syborg_rtc.c new file mode 100644 index 0000000..b480d53 --- /dev/null +++ b/hw/syborg_rtc.c @@ -0,0 +1,147 @@ +/* + * Syborg RTC + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "qemu-timer.h" +#include "syborg.h" + +enum { + RTC_ID = 0, + RTC_LATCH = 1, + RTC_DATA_LOW = 2, + RTC_DATA_HIGH = 3 +}; + +typedef struct { + SysBusDevice busdev; + int64_t offset; + int64_t data; + qemu_irq irq; +} SyborgRTCState; + +static uint32_t syborg_rtc_read(void *opaque, target_phys_addr_t offset) +{ + SyborgRTCState *s = (SyborgRTCState *)opaque; + offset &= 0xfff; + switch (offset >> 2) { + case RTC_ID: + return SYBORG_ID_RTC; + case RTC_DATA_LOW: + return (uint32_t)s->data; + case RTC_DATA_HIGH: + return (uint32_t)(s->data >> 32); + default: + cpu_abort(cpu_single_env, "syborg_rtc_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value) +{ + SyborgRTCState *s = (SyborgRTCState *)opaque; + uint64_t now; + + offset &= 0xfff; + switch (offset >> 2) { + case RTC_LATCH: + now = qemu_get_clock(vm_clock); + if (value >= 4) { + s->offset = s->data - now; + } else { + s->data = now + s->offset; + while (value) { + s->data /= 1000; + value--; + } + } + break; + case RTC_DATA_LOW: + s->data = (s->data & ~(uint64_t)0xffffffffu) | value; + break; + case RTC_DATA_HIGH: + s->data = (s->data & 0xffffffffu) | ((uint64_t)value << 32); + break; + default: + cpu_abort(cpu_single_env, "syborg_rtc_write: Bad offset %x\n", + (int)offset); + break; + } +} + +static CPUReadMemoryFunc *syborg_rtc_readfn[] = { + syborg_rtc_read, + syborg_rtc_read, + syborg_rtc_read +}; + +static CPUWriteMemoryFunc *syborg_rtc_writefn[] = { + syborg_rtc_write, + syborg_rtc_write, + syborg_rtc_write +}; + +static void syborg_rtc_save(QEMUFile *f, void *opaque) +{ + SyborgRTCState *s = opaque; + + qemu_put_be64(f, s->offset); + qemu_put_be64(f, s->data); +} + +static int syborg_rtc_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgRTCState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + s->offset = qemu_get_be64(f); + s->data = qemu_get_be64(f); + + return 0; +} + +static void syborg_rtc_init(SysBusDevice *dev) +{ + SyborgRTCState *s = FROM_SYSBUS(SyborgRTCState, dev); + struct tm tm; + int iomemtype; + + iomemtype = cpu_register_io_memory(0, syborg_rtc_readfn, + syborg_rtc_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + qemu_get_timedate(&tm, 0); + s->offset = (uint64_t)mktime(&tm) * 1000000000; + + register_savevm("syborg_rtc", -1, 1, syborg_rtc_save, syborg_rtc_load, s); +} + +static void syborg_rtc_register_devices(void) +{ + sysbus_register_dev("syborg,rtc", sizeof(SyborgRTCState), syborg_rtc_init); +} + +device_init(syborg_rtc_register_devices) diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c new file mode 100644 index 0000000..48f11e9 --- /dev/null +++ b/hw/syborg_serial.c @@ -0,0 +1,349 @@ +/* + * Syborg serial port + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "qemu-char.h" +#include "syborg.h" + +//#define DEBUG_SYBORG_SERIAL + +#ifdef DEBUG_SYBORG_SERIAL +#define DPRINTF(fmt, ...) \ +do { printf("syborg_serial: " fmt , ##args); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__); \ + exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__);} while (0) +#endif + +enum { + SERIAL_ID = 0, + SERIAL_DATA = 1, + SERIAL_FIFO_COUNT = 2, + SERIAL_INT_ENABLE = 3, + SERIAL_DMA_TX_ADDR = 4, + SERIAL_DMA_TX_COUNT = 5, /* triggers dma */ + SERIAL_DMA_RX_ADDR = 6, + SERIAL_DMA_RX_COUNT = 7, /* triggers dma */ + SERIAL_FIFO_SIZE = 8 +}; + +#define SERIAL_INT_FIFO (1u << 0) +#define SERIAL_INT_DMA_TX (1u << 1) +#define SERIAL_INT_DMA_RX (1u << 2) + +typedef struct { + SysBusDevice busdev; + uint32_t int_enable; + int fifo_size; + uint32_t *read_fifo; + int read_pos; + int read_count; + CharDriverState *chr; + qemu_irq irq; + uint32_t dma_tx_ptr; + uint32_t dma_rx_ptr; + uint32_t dma_rx_size; +} SyborgSerialState; + +static void syborg_serial_update(SyborgSerialState *s) +{ + int level; + level = 0; + if ((s->int_enable & SERIAL_INT_FIFO) && s->read_count) + level = 1; + if (s->int_enable & SERIAL_INT_DMA_TX) + level = 1; + if ((s->int_enable & SERIAL_INT_DMA_RX) && s->dma_rx_size == 0) + level = 1; + + qemu_set_irq(s->irq, level); +} + +static uint32_t fifo_pop(SyborgSerialState *s) +{ + const uint32_t c = s->read_fifo[s->read_pos]; + s->read_count--; + s->read_pos++; + if (s->read_pos == s->fifo_size) + s->read_pos = 0; + + DPRINTF("FIFO pop %x (%d)\n", c, s->read_count); + return c; +} + +static void fifo_push(SyborgSerialState *s, uint32_t new_value) +{ + int slot; + + DPRINTF("FIFO push %x (%d)\n", new_value, s->read_count); + slot = s->read_pos + s->read_count; + if (slot >= s->fifo_size) + slot -= s->fifo_size; + s->read_fifo[slot] = new_value; + s->read_count++; +} + +static void do_dma_tx(SyborgSerialState *s, uint32_t count) +{ + unsigned char ch; + + if (count == 0) + return; + + if (s->chr != NULL) { + /* optimize later. Now, 1 byte per iteration */ + while (count--) { + cpu_physical_memory_read(s->dma_tx_ptr, &ch, 1); + qemu_chr_write(s->chr, &ch, 1); + s->dma_tx_ptr++; + } + } else { + s->dma_tx_ptr += count; + } + /* QEMU char backends do not have a nonblocking mode, so we transmit all + the data imediately and the interrupt status will be unchanged. */ +} + +/* Initiate RX DMA, and transfer data from the FIFO. */ +static void dma_rx_start(SyborgSerialState *s, uint32_t len) +{ + uint32_t dest; + unsigned char ch; + + dest = s->dma_rx_ptr; + if (s->read_count < len) { + s->dma_rx_size = len - s->read_count; + len = s->read_count; + } else { + s->dma_rx_size = 0; + } + + while (len--) { + ch = fifo_pop(s); + cpu_physical_memory_write(dest, &ch, 1); + dest++; + } + s->dma_rx_ptr = dest; + syborg_serial_update(s); +} + +static uint32_t syborg_serial_read(void *opaque, target_phys_addr_t offset) +{ + SyborgSerialState *s = (SyborgSerialState *)opaque; + uint32_t c; + + offset &= 0xfff; + DPRINTF("read 0x%x\n", (int)offset); + switch(offset >> 2) { + case SERIAL_ID: + return SYBORG_ID_SERIAL; + case SERIAL_DATA: + if (s->read_count > 0) + c = fifo_pop(s); + else + c = -1; + syborg_serial_update(s); + return c; + case SERIAL_FIFO_COUNT: + return s->read_count; + case SERIAL_INT_ENABLE: + return s->int_enable; + case SERIAL_DMA_TX_ADDR: + return s->dma_tx_ptr; + case SERIAL_DMA_TX_COUNT: + return 0; + case SERIAL_DMA_RX_ADDR: + return s->dma_rx_ptr; + case SERIAL_DMA_RX_COUNT: + return s->dma_rx_size; + case SERIAL_FIFO_SIZE: + return s->fifo_size; + + default: + cpu_abort(cpu_single_env, "syborg_serial_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_serial_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + SyborgSerialState *s = (SyborgSerialState *)opaque; + unsigned char ch; + + offset &= 0xfff; + DPRINTF("Write 0x%x=0x%x\n", (int)offset, value); + switch (offset >> 2) { + case SERIAL_DATA: + ch = value; + if (s->chr) + qemu_chr_write(s->chr, &ch, 1); + break; + case SERIAL_INT_ENABLE: + s->int_enable = value; + syborg_serial_update(s); + break; + case SERIAL_DMA_TX_ADDR: + s->dma_tx_ptr = value; + break; + case SERIAL_DMA_TX_COUNT: + do_dma_tx(s, value); + break; + case SERIAL_DMA_RX_ADDR: + /* For safety, writes to this register cancel any pending DMA. */ + s->dma_rx_size = 0; + s->dma_rx_ptr = value; + break; + case SERIAL_DMA_RX_COUNT: + dma_rx_start(s, value); + break; + default: + cpu_abort(cpu_single_env, "syborg_serial_write: Bad offset %x\n", + (int)offset); + break; + } +} + +static int syborg_serial_can_receive(void *opaque) +{ + SyborgSerialState *s = (SyborgSerialState *)opaque; + + if (s->dma_rx_size) + return s->dma_rx_size; + return s->fifo_size - s->read_count; +} + +static void syborg_serial_receive(void *opaque, const uint8_t *buf, int size) +{ + SyborgSerialState *s = (SyborgSerialState *)opaque; + + if (s->dma_rx_size) { + /* Place it in the DMA buffer. */ + cpu_physical_memory_write(s->dma_rx_ptr, buf, size); + s->dma_rx_size -= size; + s->dma_rx_ptr += size; + } else { + while (size--) + fifo_push(s, *buf); + } + + syborg_serial_update(s); +} + +static void syborg_serial_event(void *opaque, int event) +{ + /* TODO: Report BREAK events? */ +} + +static CPUReadMemoryFunc *syborg_serial_readfn[] = { + syborg_serial_read, + syborg_serial_read, + syborg_serial_read +}; + +static CPUWriteMemoryFunc *syborg_serial_writefn[] = { + syborg_serial_write, + syborg_serial_write, + syborg_serial_write +}; + +static void syborg_serial_save(QEMUFile *f, void *opaque) +{ + SyborgSerialState *s = opaque; + int i; + + qemu_put_be32(f, s->fifo_size); + qemu_put_be32(f, s->int_enable); + qemu_put_be32(f, s->read_pos); + qemu_put_be32(f, s->read_count); + qemu_put_be32(f, s->dma_tx_ptr); + qemu_put_be32(f, s->dma_rx_ptr); + qemu_put_be32(f, s->dma_rx_size); + for (i = 0; i < s->fifo_size; i++) { + qemu_put_be32(f, s->read_fifo[i]); + } +} + +static int syborg_serial_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgSerialState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + i = qemu_get_be32(f); + if (s->fifo_size != i) + return -EINVAL; + + s->int_enable = qemu_get_be32(f); + s->read_pos = qemu_get_be32(f); + s->read_count = qemu_get_be32(f); + s->dma_tx_ptr = qemu_get_be32(f); + s->dma_rx_ptr = qemu_get_be32(f); + s->dma_rx_size = qemu_get_be32(f); + for (i = 0; i < s->fifo_size; i++) { + s->read_fifo[i] = qemu_get_be32(f); + } + + return 0; +} + +static void syborg_serial_init(SysBusDevice *dev) +{ + SyborgSerialState *s = FROM_SYSBUS(SyborgSerialState, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(0, syborg_serial_readfn, + syborg_serial_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { + qemu_chr_add_handlers(s->chr, syborg_serial_can_receive, + syborg_serial_receive, syborg_serial_event, s); + } + s->fifo_size = qdev_get_prop_int(&dev->qdev, "fifo-size", 16); + if (s->fifo_size <= 0) { + fprintf(stderr, "syborg_serial: fifo too small\n"); + s->fifo_size = 16; + } + s->read_fifo = qemu_mallocz(s->fifo_size * sizeof(s->read_fifo[0])); + + register_savevm("syborg_serial", -1, 1, + syborg_serial_save, syborg_serial_load, s); +} + +static void syborg_serial_register_devices(void) +{ + sysbus_register_dev("syborg,serial", sizeof(SyborgSerialState), + syborg_serial_init); +} + +device_init(syborg_serial_register_devices) diff --git a/hw/syborg_timer.c b/hw/syborg_timer.c new file mode 100644 index 0000000..a84ad86 --- /dev/null +++ b/hw/syborg_timer.c @@ -0,0 +1,235 @@ +/* + * Syborg Interval Timer. + * + * Copyright (c) 2008 CodeSourcery + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "qemu-timer.h" +#include "syborg.h" + +//#define DEBUG_SYBORG_TIMER + +#ifdef DEBUG_SYBORG_TIMER +#define DPRINTF(fmt, ...) \ +do { printf("syborg_timer: " fmt , ##args); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__); \ + exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__);} while (0) +#endif + +enum { + TIMER_ID = 0, + TIMER_RUNNING = 1, + TIMER_ONESHOT = 2, + TIMER_LIMIT = 3, + TIMER_VALUE = 4, + TIMER_INT_ENABLE = 5, + TIMER_INT_STATUS = 6, + TIMER_FREQ = 7 +}; + +typedef struct { + SysBusDevice busdev; + ptimer_state *timer; + int running; + int oneshot; + uint32_t limit; + uint32_t freq; + uint32_t int_level; + uint32_t int_enabled; + qemu_irq irq; +} SyborgTimerState; + +static void syborg_timer_update(SyborgTimerState *s) +{ + /* Update interrupt. */ + if (s->int_level && s->int_enabled) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void syborg_timer_tick(void *opaque) +{ + SyborgTimerState *s = (SyborgTimerState *)opaque; + //DPRINTF("Timer Tick\n"); + s->int_level = 1; + if (s->oneshot) + s->running = 0; + syborg_timer_update(s); +} + +static uint32_t syborg_timer_read(void *opaque, target_phys_addr_t offset) +{ + SyborgTimerState *s = (SyborgTimerState *)opaque; + + DPRINTF("Reg read %d\n", (int)offset); + offset &= 0xfff; + switch (offset >> 2) { + case TIMER_ID: + return SYBORG_ID_TIMER; + case TIMER_RUNNING: + return s->running; + case TIMER_ONESHOT: + return s->oneshot; + case TIMER_LIMIT: + return s->limit; + case TIMER_VALUE: + return ptimer_get_count(s->timer); + case TIMER_INT_ENABLE: + return s->int_enabled; + case TIMER_INT_STATUS: + return s->int_level; + case TIMER_FREQ: + return s->freq; + default: + cpu_abort(cpu_single_env, "syborg_timer_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void syborg_timer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + SyborgTimerState *s = (SyborgTimerState *)opaque; + + DPRINTF("Reg write %d\n", (int)offset); + offset &= 0xfff; + switch (offset >> 2) { + case TIMER_RUNNING: + if (value == s->running) + break; + s->running = value; + if (value) { + ptimer_run(s->timer, s->oneshot); + } else { + ptimer_stop(s->timer); + } + break; + case TIMER_ONESHOT: + if (s->running) { + ptimer_stop(s->timer); + } + s->oneshot = value; + if (s->running) { + ptimer_run(s->timer, s->oneshot); + } + break; + case TIMER_LIMIT: + s->limit = value; + ptimer_set_limit(s->timer, value, 1); + break; + case TIMER_VALUE: + ptimer_set_count(s->timer, value); + break; + case TIMER_INT_ENABLE: + s->int_enabled = value; + syborg_timer_update(s); + break; + case TIMER_INT_STATUS: + s->int_level &= ~value; + syborg_timer_update(s); + break; + default: + cpu_abort(cpu_single_env, "syborg_timer_write: Bad offset %x\n", + (int)offset); + break; + } +} + +static CPUReadMemoryFunc *syborg_timer_readfn[] = { + syborg_timer_read, + syborg_timer_read, + syborg_timer_read +}; + +static CPUWriteMemoryFunc *syborg_timer_writefn[] = { + syborg_timer_write, + syborg_timer_write, + syborg_timer_write +}; + +static void syborg_timer_save(QEMUFile *f, void *opaque) +{ + SyborgTimerState *s = opaque; + + qemu_put_be32(f, s->running); + qemu_put_be32(f, s->oneshot); + qemu_put_be32(f, s->limit); + qemu_put_be32(f, s->int_level); + qemu_put_be32(f, s->int_enabled); + qemu_put_ptimer(f, s->timer); +} + +static int syborg_timer_load(QEMUFile *f, void *opaque, int version_id) +{ + SyborgTimerState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + s->running = qemu_get_be32(f); + s->oneshot = qemu_get_be32(f); + s->limit = qemu_get_be32(f); + s->int_level = qemu_get_be32(f); + s->int_enabled = qemu_get_be32(f); + qemu_get_ptimer(f, s->timer); + + return 0; +} + +static void syborg_timer_init(SysBusDevice *dev) +{ + SyborgTimerState *s = FROM_SYSBUS(SyborgTimerState, dev); + QEMUBH *bh; + int iomemtype; + + s->freq = qdev_get_prop_int(&dev->qdev, "frequency", 0); + if (s->freq == 0) { + fprintf(stderr, "syborg_timer: Zero/unset frequency\n"); + exit(1); + } + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(0, syborg_timer_readfn, + syborg_timer_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + bh = qemu_bh_new(syborg_timer_tick, s); + s->timer = ptimer_init(bh); + ptimer_set_freq(s->timer, s->freq); + register_savevm("syborg_timer", -1, 1, + syborg_timer_save, syborg_timer_load, s); +} + +static void syborg_timer_register_devices(void) +{ + sysbus_register_dev("syborg,timer", sizeof(SyborgTimerState), + syborg_timer_init); +} + +device_init(syborg_timer_register_devices) diff --git a/qemu-doc.texi b/qemu-doc.texi index 3742b45..0780f06 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -91,6 +91,7 @@ For system emulation, the following hardware targets are supported: @item MusicPal (MV88W8618 ARM processor) @item Gumstix "Connex" and "Verdex" motherboards (PXA255/270). @item Siemens SX1 smartphone (OMAP310 processor) +@item Syborg SVP base model (ARM Cortex-A8). @end itemize For user emulation, x86, PowerPC, ARM, 32-bit MIPS, Sparc32/64 and ColdFire(m68k) CPUs are supported. @@ -2172,6 +2173,28 @@ Secure Digital card connected to OMAP MMC/SD host Three on-chip UARTs @end itemize +The "Syborg" Symbian Virtual Platform base model includes the following +elements: + +@itemize @minus +@item +ARM Cortex-A8 CPU +@item +Interrupt controller +@item +Timer +@item +Real Time Clock +@item +Keyboard +@item +Framebuffer +@item +Touchscreen +@item +UARTs +@end itemize + A Linux 2.6 test image is available on the QEMU web site. More information is available in the QEMU mailing-list archive. diff --git a/target-arm/machine.c b/target-arm/machine.c index 323bace..d5a4914 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -23,6 +23,7 @@ void register_machines(void) qemu_register_machine(&mainstone2_machine); qemu_register_machine(&musicpal_machine); qemu_register_machine(&tosapda_machine); + qemu_register_machine(&syborg_machine); } void cpu_save(QEMUFile *f, void *opaque) -- 1.7.9.5