mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 08:13:54 -06:00
ARM Versatile Platform Baseboard emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1804 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
95219897ff
commit
cdbdb648b7
13 changed files with 1830 additions and 655 deletions
|
@ -1,32 +1,19 @@
|
|||
/*
|
||||
* ARM Integrator CP System emulation.
|
||||
*
|
||||
* Copyright (c) 2005 CodeSourcery, LLC.
|
||||
* Copyright (c) 2005-2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL
|
||||
*/
|
||||
|
||||
#include <vl.h>
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
#define INITRD_LOAD_ADDR 0x00800000
|
||||
|
||||
/* Stub functions for hardware that doesn't exist. */
|
||||
void pic_set_irq(int irq, int level)
|
||||
{
|
||||
cpu_abort (cpu_single_env, "pic_set_irq");
|
||||
}
|
||||
|
||||
void pic_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
void irq_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
void DMA_run (void)
|
||||
{
|
||||
}
|
||||
|
@ -284,41 +271,31 @@ static void integratorcm_init(int memsz, uint32_t flash_offset)
|
|||
|
||||
typedef struct icp_pic_state
|
||||
{
|
||||
arm_pic_handler handler;
|
||||
uint32_t base;
|
||||
uint32_t level;
|
||||
uint32_t irq_enabled;
|
||||
uint32_t fiq_enabled;
|
||||
void *parent;
|
||||
/* -1 if parent is a cpu, otherwise IRQ number on parent PIC. */
|
||||
int parent_irq;
|
||||
int parent_fiq;
|
||||
} icp_pic_state;
|
||||
|
||||
static void icp_pic_update(icp_pic_state *s)
|
||||
{
|
||||
CPUState *env;
|
||||
if (s->parent_irq != -1) {
|
||||
uint32_t flags;
|
||||
uint32_t flags;
|
||||
|
||||
if (s->parent_irq != -1) {
|
||||
flags = (s->level & s->irq_enabled);
|
||||
pic_set_irq_new(s->parent, s->parent_irq,
|
||||
flags != 0);
|
||||
return;
|
||||
pic_set_irq_new(s->parent, s->parent_irq, flags != 0);
|
||||
}
|
||||
/* Raise CPU interrupt. */
|
||||
env = (CPUState *)s->parent;
|
||||
if (s->level & s->fiq_enabled) {
|
||||
cpu_interrupt (env, CPU_INTERRUPT_FIQ);
|
||||
} else {
|
||||
cpu_reset_interrupt (env, CPU_INTERRUPT_FIQ);
|
||||
}
|
||||
if (s->level & s->irq_enabled) {
|
||||
cpu_interrupt (env, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt (env, CPU_INTERRUPT_HARD);
|
||||
if (s->parent_fiq != -1) {
|
||||
flags = (s->level & s->fiq_enabled);
|
||||
pic_set_irq_new(s->parent, s->parent_fiq, flags != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void pic_set_irq_new(void *opaque, int irq, int level)
|
||||
static void icp_pic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
icp_pic_state *s = (icp_pic_state *)opaque;
|
||||
if (level)
|
||||
|
@ -408,7 +385,7 @@ static CPUWriteMemoryFunc *icp_pic_writefn[] = {
|
|||
};
|
||||
|
||||
static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
|
||||
int parent_irq)
|
||||
int parent_irq, int parent_fiq)
|
||||
{
|
||||
icp_pic_state *s;
|
||||
int iomemtype;
|
||||
|
@ -416,10 +393,11 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
|
|||
s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->handler = icp_pic_set_irq;
|
||||
s->base = base;
|
||||
s->parent = parent;
|
||||
s->parent_irq = parent_irq;
|
||||
s->parent_fiq = parent_fiq;
|
||||
iomemtype = cpu_register_io_memory(0, icp_pic_readfn,
|
||||
icp_pic_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
|
@ -427,499 +405,6 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
|
|||
return s;
|
||||
}
|
||||
|
||||
/* Timers. */
|
||||
|
||||
/* System bus clock speed (40MHz) for timer 0. Not sure about this value. */
|
||||
#define ICP_BUS_FREQ 40000000
|
||||
|
||||
typedef struct {
|
||||
int64_t next_time;
|
||||
int64_t expires[3];
|
||||
int64_t loaded[3];
|
||||
QEMUTimer *timer;
|
||||
icp_pic_state *pic;
|
||||
uint32_t base;
|
||||
uint32_t control[3];
|
||||
uint32_t count[3];
|
||||
uint32_t limit[3];
|
||||
int freq[3];
|
||||
int int_level[3];
|
||||
} icp_pit_state;
|
||||
|
||||
/* Calculate the new expiry time of the given timer. */
|
||||
|
||||
static void icp_pit_reload(icp_pit_state *s, int n)
|
||||
{
|
||||
int64_t delay;
|
||||
|
||||
s->loaded[n] = s->expires[n];
|
||||
delay = muldiv64(s->count[n], ticks_per_sec, s->freq[n]);
|
||||
if (delay == 0)
|
||||
delay = 1;
|
||||
s->expires[n] += delay;
|
||||
}
|
||||
|
||||
/* Check all active timers, and schedule the next timer interrupt. */
|
||||
|
||||
static void icp_pit_update(icp_pit_state *s, int64_t now)
|
||||
{
|
||||
int n;
|
||||
int64_t next;
|
||||
|
||||
next = now;
|
||||
for (n = 0; n < 3; n++) {
|
||||
/* Ignore disabled timers. */
|
||||
if ((s->control[n] & 0x80) == 0)
|
||||
continue;
|
||||
/* Ignore expired one-shot timers. */
|
||||
if (s->count[n] == 0 && s->control[n] & 1)
|
||||
continue;
|
||||
if (s->expires[n] - now <= 0) {
|
||||
/* Timer has expired. */
|
||||
s->int_level[n] = 1;
|
||||
if (s->control[n] & 1) {
|
||||
/* One-shot. */
|
||||
s->count[n] = 0;
|
||||
} else {
|
||||
if ((s->control[n] & 0x40) == 0) {
|
||||
/* Free running. */
|
||||
if (s->control[n] & 2)
|
||||
s->count[n] = 0xffffffff;
|
||||
else
|
||||
s->count[n] = 0xffff;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
s->count[n] = s->limit[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
while (s->expires[n] - now <= 0) {
|
||||
icp_pit_reload(s, n);
|
||||
}
|
||||
}
|
||||
/* Update interrupts. */
|
||||
for (n = 0; n < 3; n++) {
|
||||
if (s->int_level[n] && (s->control[n] & 0x20)) {
|
||||
pic_set_irq_new(s->pic, 5 + n, 1);
|
||||
} else {
|
||||
pic_set_irq_new(s->pic, 5 + n, 0);
|
||||
}
|
||||
if (next - s->expires[n] < 0)
|
||||
next = s->expires[n];
|
||||
}
|
||||
/* Schedule the next timer interrupt. */
|
||||
if (next == now) {
|
||||
qemu_del_timer(s->timer);
|
||||
s->next_time = 0;
|
||||
} else if (next != s->next_time) {
|
||||
qemu_mod_timer(s->timer, next);
|
||||
s->next_time = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the current value of the timer. */
|
||||
static uint32_t icp_pit_getcount(icp_pit_state *s, int n, int64_t now)
|
||||
{
|
||||
int64_t elapsed;
|
||||
int64_t period;
|
||||
|
||||
if (s->count[n] == 0)
|
||||
return 0;
|
||||
if ((s->control[n] & 0x80) == 0)
|
||||
return s->count[n];
|
||||
elapsed = now - s->loaded[n];
|
||||
period = s->expires[n] - s->loaded[n];
|
||||
/* If the timer should have expired then return 0. This can happen
|
||||
when the host timer signal doesnt occur immediately. It's better to
|
||||
have a timer appear to sit at zero for a while than have it wrap
|
||||
around before the guest interrupt is raised. */
|
||||
/* ??? Could we trigger the interrupt here? */
|
||||
if (elapsed > period)
|
||||
return 0;
|
||||
/* We need to calculate count * elapsed / period without overfowing.
|
||||
Scale both elapsed and period so they fit in a 32-bit int. */
|
||||
while (period != (int32_t)period) {
|
||||
period >>= 1;
|
||||
elapsed >>= 1;
|
||||
}
|
||||
return ((uint64_t)s->count[n] * (uint64_t)(int32_t)elapsed)
|
||||
/ (int32_t)period;
|
||||
}
|
||||
|
||||
static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
int n;
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 2)
|
||||
cpu_abort (cpu_single_env, "icp_pit_read: Bad timer %x\n", offset);
|
||||
switch ((offset & 0xff) >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
case 6: /* TimerBGLoad */
|
||||
return s->limit[n];
|
||||
case 1: /* TimerValue */
|
||||
return icp_pit_getcount(s, n, qemu_get_clock(vm_clock));
|
||||
case 2: /* TimerControl */
|
||||
return s->control[n];
|
||||
case 4: /* TimerRIS */
|
||||
return s->int_level[n];
|
||||
case 5: /* TimerMIS */
|
||||
if ((s->control[n] & 0x20) == 0)
|
||||
return 0;
|
||||
return s->int_level[n];
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_pit_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_pit_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
int n;
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 2)
|
||||
cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset);
|
||||
|
||||
switch ((offset & 0xff) >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
s->limit[n] = value;
|
||||
s->count[n] = value;
|
||||
s->expires[n] = now;
|
||||
icp_pit_reload(s, n);
|
||||
break;
|
||||
case 1: /* TimerValue */
|
||||
/* ??? Linux seems to want to write to this readonly register.
|
||||
Ignore it. */
|
||||
break;
|
||||
case 2: /* TimerControl */
|
||||
if (s->control[n] & 0x80) {
|
||||
/* Pause the timer if it is running. This may cause some
|
||||
inaccuracy dure to rounding, but avoids a whole lot of other
|
||||
messyness. */
|
||||
s->count[n] = icp_pit_getcount(s, n, now);
|
||||
}
|
||||
s->control[n] = value;
|
||||
if (n == 0)
|
||||
s->freq[n] = ICP_BUS_FREQ;
|
||||
else
|
||||
s->freq[n] = 1000000;
|
||||
/* ??? Need to recalculate expiry time after changing divisor. */
|
||||
switch ((value >> 2) & 3) {
|
||||
case 1: s->freq[n] >>= 4; break;
|
||||
case 2: s->freq[n] >>= 8; break;
|
||||
}
|
||||
if (s->control[n] & 0x80) {
|
||||
/* Restart the timer if still enabled. */
|
||||
s->expires[n] = now;
|
||||
icp_pit_reload(s, n);
|
||||
}
|
||||
break;
|
||||
case 3: /* TimerIntClr */
|
||||
s->int_level[n] = 0;
|
||||
break;
|
||||
case 6: /* TimerBGLoad */
|
||||
s->limit[n] = value;
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset);
|
||||
}
|
||||
icp_pit_update(s, now);
|
||||
}
|
||||
|
||||
static void icp_pit_tick(void *opaque)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
icp_pit_update((icp_pit_state *)opaque, now);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *icp_pit_readfn[] = {
|
||||
icp_pit_read,
|
||||
icp_pit_read,
|
||||
icp_pit_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_pit_writefn[] = {
|
||||
icp_pit_write,
|
||||
icp_pit_write,
|
||||
icp_pit_write
|
||||
};
|
||||
|
||||
static void icp_pit_init(uint32_t base, icp_pic_state *pic)
|
||||
{
|
||||
int iomemtype;
|
||||
icp_pit_state *s;
|
||||
int n;
|
||||
|
||||
s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
|
||||
s->base = base;
|
||||
s->pic = pic;
|
||||
s->freq[0] = ICP_BUS_FREQ;
|
||||
s->freq[1] = 1000000;
|
||||
s->freq[2] = 1000000;
|
||||
for (n = 0; n < 3; n++) {
|
||||
s->control[n] = 0x20;
|
||||
s->count[n] = 0xffffffff;
|
||||
}
|
||||
|
||||
iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
|
||||
icp_pit_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
s->timer = qemu_new_timer(vm_clock, icp_pit_tick, s);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
/* ARM PrimeCell PL011 UART */
|
||||
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
uint32_t readbuff;
|
||||
uint32_t flags;
|
||||
uint32_t lcr;
|
||||
uint32_t cr;
|
||||
uint32_t dmacr;
|
||||
uint32_t int_enabled;
|
||||
uint32_t int_level;
|
||||
uint32_t read_fifo[16];
|
||||
uint32_t ilpr;
|
||||
uint32_t ibrd;
|
||||
uint32_t fbrd;
|
||||
uint32_t ifl;
|
||||
int read_pos;
|
||||
int read_count;
|
||||
int read_trigger;
|
||||
CharDriverState *chr;
|
||||
icp_pic_state *pic;
|
||||
int irq;
|
||||
} pl011_state;
|
||||
|
||||
#define PL011_INT_TX 0x20
|
||||
#define PL011_INT_RX 0x10
|
||||
|
||||
#define PL011_FLAG_TXFE 0x80
|
||||
#define PL011_FLAG_RXFF 0x40
|
||||
#define PL011_FLAG_TXFF 0x20
|
||||
#define PL011_FLAG_RXFE 0x10
|
||||
|
||||
static const unsigned char pl011_id[] =
|
||||
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
static void pl011_update(pl011_state *s)
|
||||
{
|
||||
uint32_t flags;
|
||||
|
||||
flags = s->int_level & s->int_enabled;
|
||||
pic_set_irq_new(s->pic, s->irq, flags != 0);
|
||||
}
|
||||
|
||||
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
pl011_state *s = (pl011_state *)opaque;
|
||||
uint32_t c;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
return pl011_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
switch (offset >> 2) {
|
||||
case 0: /* UARTDR */
|
||||
s->flags &= ~PL011_FLAG_RXFF;
|
||||
c = s->read_fifo[s->read_pos];
|
||||
if (s->read_count > 0) {
|
||||
s->read_count--;
|
||||
if (++s->read_pos == 16)
|
||||
s->read_pos = 0;
|
||||
}
|
||||
if (s->read_count == 0) {
|
||||
s->flags |= PL011_FLAG_RXFE;
|
||||
}
|
||||
if (s->read_count == s->read_trigger - 1)
|
||||
s->int_level &= ~ PL011_INT_RX;
|
||||
pl011_update(s);
|
||||
return c;
|
||||
case 1: /* UARTCR */
|
||||
return 0;
|
||||
case 6: /* UARTFR */
|
||||
return s->flags;
|
||||
case 8: /* UARTILPR */
|
||||
return s->ilpr;
|
||||
case 9: /* UARTIBRD */
|
||||
return s->ibrd;
|
||||
case 10: /* UARTFBRD */
|
||||
return s->fbrd;
|
||||
case 11: /* UARTLCR_H */
|
||||
return s->lcr;
|
||||
case 12: /* UARTCR */
|
||||
return s->cr;
|
||||
case 13: /* UARTIFLS */
|
||||
return s->ifl;
|
||||
case 14: /* UARTIMSC */
|
||||
return s->int_enabled;
|
||||
case 15: /* UARTRIS */
|
||||
return s->int_level;
|
||||
case 16: /* UARTMIS */
|
||||
return s->int_level & s->int_enabled;
|
||||
case 18: /* UARTDMACR */
|
||||
return s->dmacr;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl011_set_read_trigger(pl011_state *s)
|
||||
{
|
||||
#if 0
|
||||
/* The docs say the RX interrupt is triggered when the FIFO exceeds
|
||||
the threshold. However linux only reads the FIFO in response to an
|
||||
interrupt. Triggering the interrupt when the FIFO is non-empty seems
|
||||
to make things work. */
|
||||
if (s->lcr & 0x10)
|
||||
s->read_trigger = (s->ifl >> 1) & 0x1c;
|
||||
else
|
||||
#endif
|
||||
s->read_trigger = 1;
|
||||
}
|
||||
|
||||
static void pl011_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
pl011_state *s = (pl011_state *)opaque;
|
||||
unsigned char ch;
|
||||
|
||||
offset -= s->base;
|
||||
switch (offset >> 2) {
|
||||
case 0: /* UARTDR */
|
||||
/* ??? Check if transmitter is enabled. */
|
||||
ch = value;
|
||||
if (s->chr)
|
||||
qemu_chr_write(s->chr, &ch, 1);
|
||||
s->int_level |= PL011_INT_TX;
|
||||
pl011_update(s);
|
||||
break;
|
||||
case 1: /* UARTCR */
|
||||
s->cr = value;
|
||||
break;
|
||||
case 8: /* UARTUARTILPR */
|
||||
s->ilpr = value;
|
||||
break;
|
||||
case 9: /* UARTIBRD */
|
||||
s->ibrd = value;
|
||||
break;
|
||||
case 10: /* UARTFBRD */
|
||||
s->fbrd = value;
|
||||
break;
|
||||
case 11: /* UARTLCR_H */
|
||||
s->lcr = value;
|
||||
pl011_set_read_trigger(s);
|
||||
break;
|
||||
case 12: /* UARTCR */
|
||||
/* ??? Need to implement the enable and loopback bits. */
|
||||
s->cr = value;
|
||||
break;
|
||||
case 13: /* UARTIFS */
|
||||
s->ifl = value;
|
||||
pl011_set_read_trigger(s);
|
||||
break;
|
||||
case 14: /* UARTIMSC */
|
||||
s->int_enabled = value;
|
||||
pl011_update(s);
|
||||
break;
|
||||
case 17: /* UARTICR */
|
||||
s->int_level &= ~value;
|
||||
pl011_update(s);
|
||||
break;
|
||||
case 18: /* UARTDMACR */
|
||||
s->dmacr = value;
|
||||
if (value & 3)
|
||||
cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
|
||||
}
|
||||
}
|
||||
|
||||
static int pl011_can_recieve(void *opaque)
|
||||
{
|
||||
pl011_state *s = (pl011_state *)opaque;
|
||||
|
||||
if (s->lcr & 0x10)
|
||||
return s->read_count < 16;
|
||||
else
|
||||
return s->read_count < 1;
|
||||
}
|
||||
|
||||
static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
pl011_state *s = (pl011_state *)opaque;
|
||||
int slot;
|
||||
|
||||
slot = s->read_pos + s->read_count;
|
||||
if (slot >= 16)
|
||||
slot -= 16;
|
||||
s->read_fifo[slot] = *buf;
|
||||
s->read_count++;
|
||||
s->flags &= ~PL011_FLAG_RXFE;
|
||||
if (s->cr & 0x10 || s->read_count == 16) {
|
||||
s->flags |= PL011_FLAG_RXFF;
|
||||
}
|
||||
if (s->read_count == s->read_trigger) {
|
||||
s->int_level |= PL011_INT_RX;
|
||||
pl011_update(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void pl011_event(void *opaque, int event)
|
||||
{
|
||||
/* ??? Should probably implement break. */
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *pl011_readfn[] = {
|
||||
pl011_read,
|
||||
pl011_read,
|
||||
pl011_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *pl011_writefn[] = {
|
||||
pl011_write,
|
||||
pl011_write,
|
||||
pl011_write
|
||||
};
|
||||
|
||||
static void pl011_init(uint32_t base, icp_pic_state *pic, int irq,
|
||||
CharDriverState *chr)
|
||||
{
|
||||
int iomemtype;
|
||||
pl011_state *s;
|
||||
|
||||
s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
|
||||
iomemtype = cpu_register_io_memory(0, pl011_readfn,
|
||||
pl011_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
s->base = base;
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
s->chr = chr;
|
||||
s->read_trigger = 1;
|
||||
s->ifl = 0x12;
|
||||
s->cr = 0x300;
|
||||
s->flags = 0x90;
|
||||
if (chr){
|
||||
qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s);
|
||||
qemu_chr_add_event_handler(chr, pl011_event);
|
||||
}
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
/* CP control registers. */
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
|
@ -985,122 +470,6 @@ static void icp_control_init(uint32_t base)
|
|||
}
|
||||
|
||||
|
||||
/* Keyboard/Mouse Interface. */
|
||||
|
||||
typedef struct {
|
||||
void *dev;
|
||||
uint32_t base;
|
||||
uint32_t cr;
|
||||
uint32_t clk;
|
||||
uint32_t last;
|
||||
icp_pic_state *pic;
|
||||
int pending;
|
||||
int irq;
|
||||
int is_mouse;
|
||||
} icp_kmi_state;
|
||||
|
||||
static void icp_kmi_update(void *opaque, int level)
|
||||
{
|
||||
icp_kmi_state *s = (icp_kmi_state *)opaque;
|
||||
int raise;
|
||||
|
||||
s->pending = level;
|
||||
raise = (s->pending && (s->cr & 0x10) != 0)
|
||||
|| (s->cr & 0x08) != 0;
|
||||
pic_set_irq_new(s->pic, s->irq, raise);
|
||||
}
|
||||
|
||||
static uint32_t icp_kmi_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
icp_kmi_state *s = (icp_kmi_state *)opaque;
|
||||
offset -= s->base;
|
||||
if (offset >= 0xfe0 && offset < 0x1000)
|
||||
return 0;
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* KMICR */
|
||||
return s->cr;
|
||||
case 1: /* KMISTAT */
|
||||
/* KMIC and KMID bits not implemented. */
|
||||
if (s->pending) {
|
||||
return 0x10;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
case 2: /* KMIDATA */
|
||||
if (s->pending)
|
||||
s->last = ps2_read_data(s->dev);
|
||||
return s->last;
|
||||
case 3: /* KMICLKDIV */
|
||||
return s->clk;
|
||||
case 4: /* KMIIR */
|
||||
return s->pending | 2;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_kmi_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_kmi_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_kmi_state *s = (icp_kmi_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset >> 2) {
|
||||
case 0: /* KMICR */
|
||||
s->cr = value;
|
||||
icp_kmi_update(s, s->pending);
|
||||
/* ??? Need to implement the enable/disable bit. */
|
||||
break;
|
||||
case 2: /* KMIDATA */
|
||||
/* ??? This should toggle the TX interrupt line. */
|
||||
/* ??? This means kbd/mouse can block each other. */
|
||||
if (s->is_mouse) {
|
||||
ps2_write_mouse(s->dev, value);
|
||||
} else {
|
||||
ps2_write_keyboard(s->dev, value);
|
||||
}
|
||||
break;
|
||||
case 3: /* KMICLKDIV */
|
||||
s->clk = value;
|
||||
return;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "icp_kmi_write: Bad offset %x\n", offset);
|
||||
}
|
||||
}
|
||||
static CPUReadMemoryFunc *icp_kmi_readfn[] = {
|
||||
icp_kmi_read,
|
||||
icp_kmi_read,
|
||||
icp_kmi_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_kmi_writefn[] = {
|
||||
icp_kmi_write,
|
||||
icp_kmi_write,
|
||||
icp_kmi_write
|
||||
};
|
||||
|
||||
static void icp_kmi_init(uint32_t base, icp_pic_state * pic, int irq,
|
||||
int is_mouse)
|
||||
{
|
||||
int iomemtype;
|
||||
icp_kmi_state *s;
|
||||
|
||||
s = (icp_kmi_state *)qemu_mallocz(sizeof(icp_kmi_state));
|
||||
iomemtype = cpu_register_io_memory(0, icp_kmi_readfn,
|
||||
icp_kmi_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x007fffff, iomemtype);
|
||||
s->base = base;
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
s->is_mouse = is_mouse;
|
||||
if (is_mouse)
|
||||
s->dev = ps2_mouse_init(icp_kmi_update, s);
|
||||
else
|
||||
s->dev = ps2_kbd_init(icp_kmi_update, s);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
|
||||
static uint32_t bootloader[] = {
|
||||
0xe3a00000, /* mov r0, #0 */
|
||||
|
@ -1162,6 +531,7 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device,
|
|||
CPUState *env;
|
||||
uint32_t bios_offset;
|
||||
icp_pic_state *pic;
|
||||
void *cpu_pic;
|
||||
int kernel_size;
|
||||
int initrd_size;
|
||||
int n;
|
||||
|
@ -1177,14 +547,15 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device,
|
|||
cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM);
|
||||
|
||||
integratorcm_init(ram_size >> 20, bios_offset);
|
||||
pic = icp_pic_init(0x14000000, env, -1);
|
||||
icp_pic_init(0xca000000, pic, 26);
|
||||
icp_pit_init(0x13000000, pic);
|
||||
cpu_pic = arm_pic_init_cpu(env);
|
||||
pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
|
||||
icp_pic_init(0xca000000, pic, 26, -1);
|
||||
icp_pit_init(0x13000000, pic, 5);
|
||||
pl011_init(0x16000000, pic, 1, serial_hds[0]);
|
||||
pl011_init(0x17000000, pic, 2, serial_hds[1]);
|
||||
icp_control_init(0xcb000000);
|
||||
icp_kmi_init(0x18000000, pic, 3, 0);
|
||||
icp_kmi_init(0x19000000, pic, 4, 1);
|
||||
pl050_init(0x18000000, pic, 3, 0);
|
||||
pl050_init(0x19000000, pic, 4, 1);
|
||||
if (nd_table[0].vlan) {
|
||||
if (nd_table[0].model == NULL
|
||||
|| strcmp(nd_table[0].model, "smc91c111") == 0) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue