Browse Source
A virtual reference platform for SymbianOS development/debugging. Signed-off-by: Paul Brook <paul@codesourcery.com>stable-0.11
13 changed files with 2110 additions and 0 deletions
@ -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, |
|||
}; |
|||
@ -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 |
|||
@ -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) |
|||
@ -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) |
|||
@ -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) |
|||
@ -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) |
|||
@ -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) |
|||
@ -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) |
|||
@ -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) |
|||
Loading…
Reference in new issue