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:
pbrook 2006-04-09 01:32:52 +00:00
parent 95219897ff
commit cdbdb648b7
13 changed files with 1830 additions and 655 deletions

View File

@ -339,7 +339,8 @@ VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
endif
endif
ifeq ($(TARGET_BASE_ARCH), arm)
VL_OBJS+= integratorcp.o ps2.o smc91c111.o pl110.o
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
VL_OBJS+= pl011.o pl050.o pl080.o pl110.o pl190.o
endif
ifdef CONFIG_GDBSTUB
VL_OBJS+=gdbstub.o

73
hw/arm_pic.c Normal file
View File

@ -0,0 +1,73 @@
/*
* Generic ARM Programmable Interrupt Controller support.
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the LGPL
*/
#include "vl.h"
#include "arm_pic.h"
/* 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 pic_set_irq_new(void *opaque, int irq, int level)
{
arm_pic_handler *p = (arm_pic_handler *)opaque;
/* Call the real handler. */
(*p)(opaque, irq, level);
}
/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
Input 0 is IRQ and input 1 is FIQ. */
typedef struct
{
arm_pic_handler handler;
CPUState *cpu_env;
} arm_pic_cpu_state;
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
{
arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque;
switch (irq) {
case ARM_PIC_CPU_IRQ:
if (level)
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
else
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
break;
case ARM_PIC_CPU_FIQ:
if (level)
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
else
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
break;
default:
cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n",
irq);
}
}
void *arm_pic_init_cpu(CPUState *env)
{
arm_pic_cpu_state *s;
s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state));
s->handler = arm_pic_cpu_handler;
s->cpu_env = env;
return s;
}

27
hw/arm_pic.h Normal file
View File

@ -0,0 +1,27 @@
/*
* Generic ARM Programmable Interrupt Controller support.
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the LGPL.
*
* Arm hardware uses a wide variety of interrupt handling hardware.
* This provides a generic framework for connecting interrupt sources and
* inputs.
*/
#ifndef ARM_INTERRUPT_H
#define ARM_INTERRUPT_H 1
/* The first element of an individual PIC state structures should
be a pointer to the handler routine. */
typedef void (*arm_pic_handler)(void *opaque, int irq, int level);
/* The CPU is also modeled as an interrupt controller. */
#define ARM_PIC_CPU_IRQ 0
#define ARM_PIC_CPU_FIQ 1
void *arm_pic_init_cpu(CPUState *env);
#endif /* !ARM_INTERRUPT_H */

383
hw/arm_timer.c Normal file
View File

@ -0,0 +1,383 @@
/*
* ARM PrimeCell Timer modules.
*
* Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#include "arm_pic.h"
/* Common timer implementation. */
#define TIMER_CTRL_ONESHOT (1 << 0)
#define TIMER_CTRL_32BIT (1 << 1)
#define TIMER_CTRL_DIV1 (0 << 2)
#define TIMER_CTRL_DIV16 (1 << 2)
#define TIMER_CTRL_DIV256 (2 << 2)
#define TIMER_CTRL_IE (1 << 5)
#define TIMER_CTRL_PERIODIC (1 << 6)
#define TIMER_CTRL_ENABLE (1 << 7)
typedef struct {
int64_t next_time;
int64_t expires;
int64_t loaded;
QEMUTimer *timer;
uint32_t control;
uint32_t count;
uint32_t limit;
int raw_freq;
int freq;
int int_level;
void *pic;
int irq;
} arm_timer_state;
/* Calculate the new expiry time of the given timer. */
static void arm_timer_reload(arm_timer_state *s)
{
int64_t delay;
s->loaded = s->expires;
delay = muldiv64(s->count, ticks_per_sec, s->freq);
if (delay == 0)
delay = 1;
s->expires += delay;
}
/* Check all active timers, and schedule the next timer interrupt. */
static void arm_timer_update(arm_timer_state *s, int64_t now)
{
int64_t next;
/* Ignore disabled timers. */
if ((s->control & TIMER_CTRL_ENABLE) == 0)
return;
/* Ignore expired one-shot timers. */
if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))
return;
if (s->expires - now <= 0) {
/* Timer has expired. */
s->int_level = 1;
if (s->control & TIMER_CTRL_ONESHOT) {
/* One-shot. */
s->count = 0;
} else {
if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
/* Free running. */
if (s->control & TIMER_CTRL_32BIT)
s->count = 0xffffffff;
else
s->count = 0xffff;
} else {
/* Periodic. */
s->count = s->limit;
}
}
}
while (s->expires - now <= 0) {
arm_timer_reload(s);
}
/* Update interrupts. */
if (s->int_level && (s->control & TIMER_CTRL_IE)) {
pic_set_irq_new(s->pic, s->irq, 1);
} else {
pic_set_irq_new(s->pic, s->irq, 0);
}
next = now;
if (next - s->expires < 0)
next = s->expires;
/* 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 arm_timer_getcount(arm_timer_state *s, int64_t now)
{
int64_t elapsed;
int64_t period;
if (s->count == 0)
return 0;
if ((s->control & TIMER_CTRL_ENABLE) == 0)
return s->count;
elapsed = now - s->loaded;
period = s->expires - s->loaded;
/* 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 * (uint64_t)(int32_t)elapsed)
/ (int32_t)period;
}
uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
{
arm_timer_state *s = (arm_timer_state *)opaque;
switch (offset >> 2) {
case 0: /* TimerLoad */
case 6: /* TimerBGLoad */
return s->limit;
case 1: /* TimerValue */
return arm_timer_getcount(s, qemu_get_clock(vm_clock));
case 2: /* TimerControl */
return s->control;
case 4: /* TimerRIS */
return s->int_level;
case 5: /* TimerMIS */
if ((s->control & TIMER_CTRL_IE) == 0)
return 0;
return s->int_level;
default:
cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);
return 0;
}
}
static void arm_timer_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
arm_timer_state *s = (arm_timer_state *)opaque;
int64_t now;
now = qemu_get_clock(vm_clock);
switch (offset >> 2) {
case 0: /* TimerLoad */
s->limit = value;
s->count = value;
s->expires = now;
arm_timer_reload(s);
break;
case 1: /* TimerValue */
/* ??? Linux seems to want to write to this readonly register.
Ignore it. */
break;
case 2: /* TimerControl */
if (s->control & TIMER_CTRL_ENABLE) {
/* 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 = arm_timer_getcount(s, now);
}
s->control = value;
s->freq = s->raw_freq;
/* ??? Need to recalculate expiry time after changing divisor. */
switch ((value >> 2) & 3) {
case 1: s->freq >>= 4; break;
case 2: s->freq >>= 8; break;
}
if (s->control & TIMER_CTRL_ENABLE) {
/* Restart the timer if still enabled. */
s->expires = now;
arm_timer_reload(s);
}
break;
case 3: /* TimerIntClr */
s->int_level = 0;
break;
case 6: /* TimerBGLoad */
s->limit = value;
break;
default:
cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);
}
arm_timer_update(s, now);
}
static void arm_timer_tick(void *opaque)
{
int64_t now;
now = qemu_get_clock(vm_clock);
arm_timer_update((arm_timer_state *)opaque, now);
}
static void *arm_timer_init(uint32_t freq, void *pic, int irq)
{
arm_timer_state *s;
s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
s->pic = pic;
s->irq = irq;
s->raw_freq = s->freq = 1000000;
s->control = TIMER_CTRL_IE;
s->count = 0xffffffff;
s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);
/* ??? Save/restore. */
return s;
}
/* ARM PrimeCell SP804 dual timer module.
Docs for this device don't seem to be publicly available. This
implementation is based on gueswork, the linux kernel sources and the
Integrator/CP timer modules. */
typedef struct {
/* Include a pseudo-PIC device to merge the two interrupt sources. */
arm_pic_handler handler;
void *timer[2];
int level[2];
uint32_t base;
/* The output PIC device. */
void *pic;
int irq;
} sp804_state;
static void sp804_set_irq(void *opaque, int irq, int level)
{
sp804_state *s = (sp804_state *)opaque;
s->level[irq] = level;
pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);
}
static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
{
sp804_state *s = (sp804_state *)opaque;
/* ??? Don't know the PrimeCell ID for this device. */
offset -= s->base;
if (offset < 0x20) {
return arm_timer_read(s->timer[0], offset);
} else {
return arm_timer_read(s->timer[1], offset - 0x20);
}
}
static void sp804_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
sp804_state *s = (sp804_state *)opaque;
offset -= s->base;
if (offset < 0x20) {
arm_timer_write(s->timer[0], offset, value);
} else {
arm_timer_write(s->timer[1], offset - 0x20, value);
}
}
static CPUReadMemoryFunc *sp804_readfn[] = {
sp804_read,
sp804_read,
sp804_read
};
static CPUWriteMemoryFunc *sp804_writefn[] = {
sp804_write,
sp804_write,
sp804_write
};
void sp804_init(uint32_t base, void *pic, int irq)
{
int iomemtype;
sp804_state *s;
s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
s->handler = sp804_set_irq;
s->base = base;
s->pic = pic;
s->irq = irq;
/* ??? The timers are actually configurable between 32kHz and 1MHz, but
we don't implement that. */
s->timer[0] = arm_timer_init(1000000, s, 0);
s->timer[1] = arm_timer_init(1000000, s, 1);
iomemtype = cpu_register_io_memory(0, sp804_readfn,
sp804_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
/* ??? Save/restore. */
}
/* Integrator/CP timer module. */
typedef struct {
void *timer[3];
uint32_t base;
} icp_pit_state;
static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
{
icp_pit_state *s = (icp_pit_state *)opaque;
int n;
/* ??? Don't know the PrimeCell ID for this device. */
offset -= s->base;
n = offset >> 8;
if (n > 3)
cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
return arm_timer_read(s->timer[n], offset & 0xff);
}
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;
offset -= s->base;
n = offset >> 8;
if (n > 3)
cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
arm_timer_write(s->timer[n], offset & 0xff, value);
}
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
};
void icp_pit_init(uint32_t base, void *pic, int irq)
{
int iomemtype;
icp_pit_state *s;
s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
s->base = base;
/* Timer 0 runs at the system clock speed (40MHz). */
s->timer[0] = arm_timer_init(40000000, pic, irq);
/* The other two timers run at 1MHz. */
s->timer[1] = arm_timer_init(1000000, pic, irq + 1);
s->timer[2] = arm_timer_init(1000000, pic, irq + 2);
iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
icp_pit_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
/* ??? Save/restore. */
}

View File

@ -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) {

251
hw/pl011.c Normal file
View File

@ -0,0 +1,251 @@
/*
* Arm PrimeCell PL011 UART
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
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;
void *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
};
void pl011_init(uint32_t base, void *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, 0x00000fff, 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. */
}

127
hw/pl050.c Normal file
View File

@ -0,0 +1,127 @@
/*
* Arm PrimeCell PL050 Kyeboard / Mouse Interface
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
typedef struct {
void *dev;
uint32_t base;
uint32_t cr;
uint32_t clk;
uint32_t last;
void *pic;
int pending;
int irq;
int is_mouse;
} pl050_state;
static const unsigned char pl050_id[] =
{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
static void pl050_update(void *opaque, int level)
{
pl050_state *s = (pl050_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 pl050_read(void *opaque, target_phys_addr_t offset)
{
pl050_state *s = (pl050_state *)opaque;
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000)
return pl050_id[(offset - 0xfe0) >> 2];
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, "pl050_read: Bad offset %x\n", offset);
return 0;
}
}
static void pl050_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
pl050_state *s = (pl050_state *)opaque;
offset -= s->base;
switch (offset >> 2) {
case 0: /* KMICR */
s->cr = value;
pl050_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, "pl050_write: Bad offset %x\n", offset);
}
}
static CPUReadMemoryFunc *pl050_readfn[] = {
pl050_read,
pl050_read,
pl050_read
};
static CPUWriteMemoryFunc *pl050_writefn[] = {
pl050_write,
pl050_write,
pl050_write
};
void pl050_init(uint32_t base, void *pic, int irq, int is_mouse)
{
int iomemtype;
pl050_state *s;
s = (pl050_state *)qemu_mallocz(sizeof(pl050_state));
iomemtype = cpu_register_io_memory(0, pl050_readfn,
pl050_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
s->base = base;
s->pic = pic;
s->irq = irq;
s->is_mouse = is_mouse;
if (is_mouse)
s->dev = ps2_mouse_init(pl050_update, s);
else
s->dev = ps2_kbd_init(pl050_update, s);
/* ??? Save/restore. */
}

328
hw/pl080.c Normal file
View File

@ -0,0 +1,328 @@
/*
* Arm PrimeCell PL080 DMA controller
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#define PL080_NUM_CHANNELS 8
#define PL080_CONF_E 0x1
#define PL080_CONF_M1 0x2
#define PL080_CONF_M2 0x4
#define PL080_CCONF_H 0x40000
#define PL080_CCONF_A 0x20000
#define PL080_CCONF_L 0x10000
#define PL080_CCONF_ITC 0x08000
#define PL080_CCONF_IE 0x04000
#define PL080_CCONF_E 0x00001
#define PL080_CCTRL_I 0x80000000
#define PL080_CCTRL_DI 0x08000000
#define PL080_CCTRL_SI 0x04000000
#define PL080_CCTRL_D 0x02000000
#define PL080_CCTRL_S 0x01000000
typedef struct {
uint32_t src;
uint32_t dest;
uint32_t lli;
uint32_t ctrl;
uint32_t conf;
} pl080_channel;
typedef struct {
uint32_t base;
uint8_t tc_int;
uint8_t tc_mask;
uint8_t err_int;
uint8_t err_mask;
uint32_t conf;
uint32_t sync;
uint32_t req_single;
uint32_t req_burst;
pl080_channel chan[PL080_NUM_CHANNELS];
/* Flag to avoid recursive DMA invocations. */
int running;
void *pic;
int irq;
} pl080_state;
static const unsigned char pl080_id[] =
{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
static void pl080_update(pl080_state *s)
{
if ((s->tc_int & s->tc_mask)
|| (s->err_int & s->err_mask))
pic_set_irq_new(s->pic, s->irq, 1);
else
pic_set_irq_new(s->pic, s->irq, 1);
}
static void pl080_run(pl080_state *s)
{
int c;
int flow;
pl080_channel *ch;
int swidth;
int dwidth;
int xsize;
int n;
int src_id;
int dest_id;
int size;
char buff[4];
uint32_t req;
s->tc_mask = 0;
for (c = 0; c < PL080_NUM_CHANNELS; c++) {
if (s->chan[c].conf & PL080_CCONF_ITC)
s->tc_mask |= 1 << c;
if (s->chan[c].conf & PL080_CCONF_IE)
s->err_mask |= 1 << c;
}
if ((s->conf & PL080_CONF_E) == 0)
return;
cpu_abort(cpu_single_env, "DMA active\n");
/* If we are already in the middle of a DMA operation then indicate that
there may be new DMA requests and return immediately. */
if (s->running) {
s->running++;
return;
}
s->running = 1;
while (s->running) {
for (c = 0; c < PL080_NUM_CHANNELS; c++) {
ch = &s->chan[c];
again:
/* Test if thiws channel has any pending DMA requests. */
if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
!= PL080_CCONF_E)
continue;
flow = (ch->conf >> 11) & 7;
if (flow >= 4) {
cpu_abort(cpu_single_env,
"pl080_run: Peripheral flow control not implemented\n");
}
src_id = (ch->conf >> 1) & 0x1f;
dest_id = (ch->conf >> 6) & 0x1f;
size = ch->ctrl & 0xfff;
req = s->req_single | s->req_burst;
switch (flow) {
case 0:
break;
case 1:
if ((req & (1u << dest_id)) == 0)
size = 0;
break;
case 2:
if ((req & (1u << src_id)) == 0)
size = 0;
break;
case 3:
if ((req & (1u << src_id)) == 0
|| (req & (1u << dest_id)) == 0)
size = 0;
break;
}
if (!size)
continue;
/* Transfer one element. */
/* ??? Should transfer multiple elements for a burst request. */
/* ??? Unclear what the proper behavior is when source and
destination widths are different. */
swidth = 1 << ((ch->ctrl >> 18) & 7);
dwidth = 1 << ((ch->ctrl >> 21) & 7);
for (n = 0; n < dwidth; n+= swidth) {
cpu_physical_memory_read(ch->src, buff + n, swidth);
if (ch->ctrl & PL080_CCTRL_SI)
ch->src += swidth;
}
xsize = (dwidth < swidth) ? swidth : dwidth;
/* ??? This may pad the value incorrectly for dwidth < 32. */
for (n = 0; n < xsize; n += dwidth) {
cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
if (ch->ctrl & PL080_CCTRL_DI)
ch->dest += swidth;
}
size--;
ch->ctrl = (ch->ctrl & 0xfffff000) | size;
if (size == 0) {
/* Transfer complete. */
if (ch->lli) {
ch->src = ldl_phys(ch->lli);
ch->dest = ldl_phys(ch->lli + 4);
ch->ctrl = ldl_phys(ch->lli + 12);
ch->lli = ldl_phys(ch->lli + 8);
} else {
ch->conf &= ~PL080_CCONF_E;
}
if (ch->ctrl & PL080_CCTRL_I) {
s->tc_int |= 1 << c;
}
}
goto again;
}
if (--s->running)
s->running = 1;
}
}
static uint32_t pl080_read(void *opaque, target_phys_addr_t offset)
{
pl080_state *s = (pl080_state *)opaque;
uint32_t i;
uint32_t mask;
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
return pl080_id[(offset - 0xfe0) >> 2];
}
if (offset >= 0x100 && offset < 0x200) {
i = (offset & 0xe0) >> 5;
switch (offset >> 2) {
case 0: /* SrcAddr */
return s->chan[i].src;
case 1: /* DestAddr */
return s->chan[i].dest;
case 2: /* LLI */
return s->chan[i].lli;
case 3: /* Control */
return s->chan[i].ctrl;
case 4: /* Configuration */
return s->chan[i].conf;
default:
goto bad_offset;
}
}
switch (offset >> 2) {
case 0: /* IntStatus */
return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
case 1: /* IntTCStatus */
return (s->tc_int & s->tc_mask);
case 3: /* IntErrorStatus */
return (s->err_int & s->err_mask);
case 5: /* RawIntTCStatus */
return s->tc_int;
case 6: /* RawIntErrorStatus */
return s->err_int;
case 7: /* EnbldChns */
mask = 0;
for (i = 0; i < PL080_NUM_CHANNELS; i++) {
if (s->chan[i].conf & PL080_CCONF_E)
mask |= 1 << i;
}
return mask;
case 8: /* SoftBReq */
case 9: /* SoftSReq */
case 10: /* SoftLBReq */
case 11: /* SoftLSReq */
/* ??? Implement these. */
return 0;
case 12: /* Configuration */
return s->conf;
case 13: /* Sync */
return s->sync;
default:
bad_offset:
cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset);
return 0;
}
}
static void pl080_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
pl080_state *s = (pl080_state *)opaque;
int i;
offset -= s->base;
if (offset >= 0x100 && offset < 0x200) {
i = (offset & 0xe0) >> 5;
switch (offset >> 2) {
case 0: /* SrcAddr */
s->chan[i].src = value;
break;
case 1: /* DestAddr */
s->chan[i].dest = value;
break;
case 2: /* LLI */
s->chan[i].lli = value;
break;
case 3: /* Control */
s->chan[i].ctrl = value;
break;
case 4: /* Configuration */
s->chan[i].conf = value;
pl080_run(s);
break;
}
}
switch (offset >> 2) {
case 2: /* IntTCClear */
s->tc_int &= ~value;
break;
case 4: /* IntErrorClear */
s->err_int &= ~value;
break;
case 8: /* SoftBReq */
case 9: /* SoftSReq */
case 10: /* SoftLBReq */
case 11: /* SoftLSReq */
/* ??? Implement these. */
cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n");
break;
case 12: /* Configuration */
s->conf = value;
if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
cpu_abort(cpu_single_env,
"pl080_write: Big-endian DMA not implemented\n");
}
pl080_run(s);
break;
case 13: /* Sync */
s->sync = value;
break;
default:
cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset);
}
pl080_update(s);
}
static CPUReadMemoryFunc *pl080_readfn[] = {
pl080_read,
pl080_read,
pl080_read
};
static CPUWriteMemoryFunc *pl080_writefn[] = {
pl080_write,
pl080_write,
pl080_write
};
void *pl080_init(uint32_t base, void *pic, int irq)
{
int iomemtype;
pl080_state *s;
s = (pl080_state *)qemu_mallocz(sizeof(pl080_state));
iomemtype = cpu_register_io_memory(0, pl080_readfn,
pl080_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
s->base = base;
s->pic = pic;
s->irq = irq;
/* ??? Save/restore. */
return s;
}

View File

@ -1,7 +1,7 @@
/*
* Arm PrimeCell PL110 Color LCD Controller
*
* Copyright (c) 2005 CodeSourcery, LLC.
* Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GNU LGPL
@ -27,6 +27,8 @@ enum pl110_bppmode
typedef struct {
uint32_t base;
DisplayState *ds;
/* The Versatile/PB uses a slightly modified PL110 controller. */
int versatile;
void *pic;
uint32_t timing[4];
uint32_t cr;
@ -46,6 +48,15 @@ typedef struct {
static const unsigned char pl110_id[] =
{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
has a different ID. However Linux only looks for the normal ID. */
#if 0
static const unsigned char pl110_versatile_id[] =
{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
#else
#define pl110_versatile_id pl110_id
#endif
static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
{
return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
@ -101,7 +112,7 @@ static void pl110_update_display(void *opaque)
int src_width;
uint8_t *dest;
uint8_t *src;
int first, last;
int first, last = 0;
int dirty, new_dirty;
int i;
@ -269,7 +280,10 @@ static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
return pl110_id[(offset - 0xfe0) >> 2];
if (s->versatile)
return pl110_versatile_id[(offset - 0xfe0) >> 2];
else
return pl110_id[(offset - 0xfe0) >> 2];
}
if (offset >= 0x200 && offset < 0x400) {
return s->raw_pallette[(offset - 0x200) >> 2];
@ -347,10 +361,16 @@ static void pl110_write(void *opaque, target_phys_addr_t offset,
s->lpbase = val;
break;
case 6: /* LCDIMSC */
if (s->versatile)
goto control;
imsc:
s->int_mask = val;
pl110_update(s);
break;
case 7: /* LCDControl */
if (s->versatile)
goto imsc;
control:
s->cr = val;
s->bpp = (val >> 1) & 7;
if (pl110_enabled(s)) {
@ -390,6 +410,7 @@ void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq,
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
s->base = base;
s->ds = ds;
s->versatile = versatile;
s->pic = pic;
s->irq = irq;
graphic_console_init(ds, pl110_update_display, pl110_invalidate_display,

252
hw/pl190.c Normal file
View File

@ -0,0 +1,252 @@
/*
* Arm PrimeCell PL190 Vector Interrupt Controller
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#include "arm_pic.h"
/* The number of virtual priority levels. 16 user vectors plus the
unvectored IRQ. Chained interrupts would require an additional level
if implemented. */
#define PL190_NUM_PRIO 17
typedef struct {
arm_pic_handler handler;
uint32_t base;
DisplayState *ds;
uint32_t level;
uint32_t soft_level;
uint32_t irq_enable;
uint32_t fiq_select;
uint32_t default_addr;
uint8_t vect_control[16];
uint32_t vect_addr[PL190_NUM_PRIO];
/* Mask containing interrupts with higher priority than this one. */
uint32_t prio_mask[PL190_NUM_PRIO + 1];
int protected;
/* Current priority level. */
int priority;
int prev_prio[PL190_NUM_PRIO];
void *parent;
int irq;
int fiq;
} pl190_state;
static const unsigned char pl190_id[] =
{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
static inline uint32_t pl190_irq_level(pl190_state *s)
{
return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
}
/* Update interrupts. */
static void pl190_update(pl190_state *s)
{
uint32_t level = pl190_irq_level(s);
int set;
set = (level & s->prio_mask[s->priority]) != 0;
pic_set_irq_new(s->parent, s->irq, set);
set = ((s->level | s->soft_level) & s->fiq_select) != 0;
pic_set_irq_new(s->parent, s->fiq, set);
}
static void pl190_set_irq(void *opaque, int irq, int level)
{
pl190_state *s = (pl190_state *)opaque;
if (level)
s->level |= 1u << irq;
else
s->level &= ~(1u << irq);
pl190_update(s);
}
static void pl190_update_vectors(pl190_state *s)
{
uint32_t mask;
int i;
int n;
mask = 0;
for (i = 0; i < 16; i++)
{
s->prio_mask[i] = mask;
if (s->vect_control[i] & 0x20)
{
n = s->vect_control[i] & 0x1f;
mask |= 1 << n;
}
}
s->prio_mask[16] = mask;
pl190_update(s);
}
static uint32_t pl190_read(void *opaque, target_phys_addr_t offset)
{
pl190_state *s = (pl190_state *)opaque;
int i;
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
return pl190_id[(offset - 0xfe0) >> 2];
}
if (offset >= 0x100 && offset < 0x140) {
return s->vect_addr[(offset - 0x100) >> 2];
}
if (offset >= 0x200 && offset < 0x240) {
return s->vect_control[(offset - 0x200) >> 2];
}
switch (offset >> 2) {
case 0: /* IRQSTATUS */
return pl190_irq_level(s);
case 1: /* FIQSATUS */
return (s->level | s->soft_level) & s->fiq_select;
case 2: /* RAWINTR */
return s->level | s->soft_level;
case 3: /* INTSELECT */
return s->fiq_select;
case 4: /* INTENABLE */
return s->irq_enable;
case 6: /* SOFTINT */
return s->soft_level;
case 8: /* PROTECTION */
return s->protected;
case 12: /* VECTADDR */
/* Read vector address at the start of an ISR. Increases the
current priority level to that of the current interrupt. */
for (i = 0; i < s->priority; i++)
{
if ((s->level | s->soft_level) & s->prio_mask[i])
break;
}
/* Reading this value with no pending interrupts is undefined.
We return the default address. */
if (i == PL190_NUM_PRIO)
return s->vect_addr[16];
if (i < s->priority)
{
s->prev_prio[i] = s->priority;
s->priority = i;
pl190_update(s);
}
return s->vect_addr[s->priority];
case 13: /* DEFVECTADDR */
return s->vect_addr[16];
default:
cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", offset);
return 0;
}
}
static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val)
{
pl190_state *s = (pl190_state *)opaque;
offset -= s->base;
if (offset >= 0x100 && offset < 0x140) {
s->vect_addr[(offset - 0x100) >> 2] = val;
pl190_update_vectors(s);
return;
}
if (offset >= 0x200 && offset < 0x240) {
s->vect_control[(offset - 0x200) >> 2] = val;
pl190_update_vectors(s);
return;
}
switch (offset >> 2) {
case 0: /* SELECT */
/* This is a readonly register, but linux tries to write to it
anyway. Ignore the write. */
break;
case 3: /* INTSELECT */
s->fiq_select = val;
break;
case 4: /* INTENABLE */
s->irq_enable |= val;
break;
case 5: /* INTENCLEAR */
s->irq_enable &= ~val;
break;
case 6: /* SOFTINT */
s->soft_level |= val;
break;
case 7: /* SOFTINTCLEAR */
s->soft_level &= ~val;
break;
case 8: /* PROTECTION */
/* TODO: Protection (supervisor only access) is not implemented. */
s->protected = val & 1;
break;
case 12: /* VECTADDR */
/* Restore the previous priority level. The value written is
ignored. */
if (s->priority < PL190_NUM_PRIO)
s->priority = s->prev_prio[s->priority];
break;
case 13: /* DEFVECTADDR */
s->default_addr = val;
break;
case 0xc0: /* ITCR */
if (val)
cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n");
break;
default:
cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", offset);
return;
}
pl190_update(s);
}
static CPUReadMemoryFunc *pl190_readfn[] = {
pl190_read,
pl190_read,
pl190_read
};
static CPUWriteMemoryFunc *pl190_writefn[] = {
pl190_write,
pl190_write,
pl190_write
};
void pl190_reset(pl190_state *s)
{
int i;
for (i = 0; i < 16; i++)
{
s->vect_addr[i] = 0;
s->vect_control[i] = 0;
}
s->vect_addr[16] = 0;
s->prio_mask[17] = 0xffffffff;
s->priority = PL190_NUM_PRIO;
pl190_update_vectors(s);
}
void *pl190_init(uint32_t base, void *parent, int irq, int fiq)
{
pl190_state *s;
int iomemtype;
s = (pl190_state *)qemu_mallocz(sizeof(pl190_state));
iomemtype = cpu_register_io_memory(0, pl190_readfn,
pl190_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
s->handler = pl190_set_irq;
s->base = base;
s->parent = parent;
s->irq = irq;
s->fiq = fiq;
pl190_reset(s);
/* ??? Save/restore. */
return s;
}

321
hw/versatilepb.c Normal file
View File

@ -0,0 +1,321 @@
/*
* ARM Versatile Platform Baseboard System emulation.
*
* Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#include "arm_pic.h"
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
#define INITRD_LOAD_ADDR 0x00800000
/* Primary interrupt controller. */
typedef struct vpb_sic_state
{
arm_pic_handler handler;
uint32_t base;
uint32_t level;
uint32_t mask;
uint32_t pic_enable;
void *parent;
int irq;
} vpb_sic_state;
static void vpb_sic_update(vpb_sic_state *s)
{
uint32_t flags;
flags = s->level & s->mask;
pic_set_irq_new(s->parent, s->irq, flags != 0);
}
static void vpb_sic_update_pic(vpb_sic_state *s)
{
int i;
uint32_t mask;
for (i = 21; i <= 30; i++) {
mask = 1u << i;
if (!(s->pic_enable & mask))
continue;
pic_set_irq_new(s->parent, i, (s->level & mask) != 0);
}
}
static void vpb_sic_set_irq(void *opaque, int irq, int level)
{
vpb_sic_state *s = (vpb_sic_state *)opaque;
if (level)
s->level |= 1u << irq;
else
s->level &= ~(1u << irq);
if (s->pic_enable & (1u << irq))
pic_set_irq_new(s->parent, irq, level);
vpb_sic_update(s);
}
static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset)
{
vpb_sic_state *s = (vpb_sic_state *)opaque;
offset -= s->base;
switch (offset >> 2) {
case 0: /* STATUS */
return s->level & s->mask;
case 1: /* RAWSTAT */
return s->level;
case 2: /* ENABLE */
return s->mask;
case 4: /* SOFTINT */
return s->level & 1;
case 8: /* PICENABLE */
return s->pic_enable;
default:
printf ("vpb_sic_read: Bad register offset 0x%x\n", offset);
return 0;
}
}
static void vpb_sic_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
vpb_sic_state *s = (vpb_sic_state *)opaque;
offset -= s->base;
switch (offset >> 2) {
case 2: /* ENSET */
s->mask |= value;
break;
case 3: /* ENCLR */
s->mask &= ~value;
break;
case 4: /* SOFTINTSET */
if (value)
s->mask |= 1;
break;
case 5: /* SOFTINTCLR */
if (value)
s->mask &= ~1u;
break;
case 8: /* PICENSET */
s->pic_enable |= (value & 0x7fe00000);
vpb_sic_update_pic(s);
break;
case 9: /* PICENCLR */
s->pic_enable &= ~value;
vpb_sic_update_pic(s);
break;
default:
printf ("vpb_sic_write: Bad register offset 0x%x\n", offset);
return;
}
vpb_sic_update(s);
}
static CPUReadMemoryFunc *vpb_sic_readfn[] = {
vpb_sic_read,
vpb_sic_read,
vpb_sic_read
};
static CPUWriteMemoryFunc *vpb_sic_writefn[] = {
vpb_sic_write,
vpb_sic_write,
vpb_sic_write
};
static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq)
{
vpb_sic_state *s;
int iomemtype;
s = (vpb_sic_state *)qemu_mallocz(sizeof(vpb_sic_state));
if (!s)
return NULL;
s->handler = vpb_sic_set_irq;
s->base = base;
s->parent = parent;
s->irq = irq;
iomemtype = cpu_register_io_memory(0, vpb_sic_readfn,
vpb_sic_writefn, s);
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
/* ??? Save/restore. */
return s;
}
/* Board init. */
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
static uint32_t bootloader[] = {
0xe3a00000, /* mov r0, #0 */
0xe3a01083, /* mov r1, #0x83 */
0xe3811c01, /* orr r1, r1, #0x100 */
0xe59f2000, /* ldr r2, [pc, #0] */
0xe59ff000, /* ldr pc, [pc, #0] */
0, /* Address of kernel args. Set by integratorcp_init. */
0 /* Kernel entry point. Set by integratorcp_init. */
};
static void set_kernel_args(uint32_t ram_size, int initrd_size,
const char *kernel_cmdline)
{
uint32_t *p;
p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
/* ATAG_CORE */
stl_raw(p++, 5);
stl_raw(p++, 0x54410001);
stl_raw(p++, 1);
stl_raw(p++, 0x1000);
stl_raw(p++, 0);
/* ATAG_MEM */
stl_raw(p++, 4);
stl_raw(p++, 0x54410002);
stl_raw(p++, ram_size);
stl_raw(p++, 0);
if (initrd_size) {
/* ATAG_INITRD2 */
stl_raw(p++, 4);
stl_raw(p++, 0x54420005);
stl_raw(p++, INITRD_LOAD_ADDR);
stl_raw(p++, initrd_size);
}
if (kernel_cmdline && *kernel_cmdline) {
/* ATAG_CMDLINE */
int cmdline_size;
cmdline_size = strlen(kernel_cmdline);
memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
cmdline_size = (cmdline_size >> 2) + 1;
stl_raw(p++, cmdline_size + 2);
stl_raw(p++, 0x54410009);
p += cmdline_size;
}
/* ATAG_END */
stl_raw(p++, 0);
stl_raw(p++, 0);
}
static void vpb_init(int ram_size, int vga_ram_size, int boot_device,
DisplayState *ds, const char **fd_filename, int snapshot,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename)
{
CPUState *env;
int kernel_size;
int initrd_size;
int n;
void *pic;
void *sic;
env = cpu_init();
cpu_arm_set_model(env, ARM_CPUID_ARM926);
/* ??? RAM shoud repeat to fill physical memory space. */
/* SDRAM at address zero. */
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
pic = arm_pic_init_cpu(env);
pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
sic = vpb_sic_init(0x10003000, pic, 31);
pl050_init(0x10006000, sic, 3, 0);
pl050_init(0x10007000, sic, 4, 1);
/* TODO: Init PCI NICs. */
if (nd_table[0].vlan) {
if (nd_table[0].model == NULL
|| strcmp(nd_table[0].model, "smc91c111") == 0) {
smc91c111_init(&nd_table[0], 0x10010000, sic, 25);
} else {
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
exit (1);
}
}
pl011_init(0x101f1000, pic, 12, serial_hds[0]);
pl011_init(0x101f2000, pic, 13, serial_hds[1]);
pl011_init(0x101f3000, pic, 14, serial_hds[2]);
pl011_init(0x10009000, sic, 6, serial_hds[3]);
pl080_init(0x10130000, pic, 17);
sp804_init(0x101e2000, pic, 4);
sp804_init(0x101e3000, pic, 5);
/* The versatile/PB actually has a modified Color LCD controller
that includes hardware cursor support from the PL111. */
pl110_init(ds, 0x10120000, pic, 16, 1);
/* 0x10000000 System registers. */
/* 0x10001000 PCI controller config registers. */
/* 0x10002000 Serial bus interface. */
/* 0x10003000 Secondary interrupt controller. */
/* 0x10004000 AACI (audio). */
/* 0x10005000 MMCI0. */
/* 0x10006000 KMI0 (keyboard). */
/* 0x10007000 KMI1 (mouse). */
/* 0x10008000 Character LCD Interface. */
/* 0x10009000 UART3. */
/* 0x1000a000 Smart card 1. */
/* 0x1000b000 MMCI1. */
/* 0x10010000 Ethernet. */
/* 0x10020000 USB. */
/* 0x10100000 SSMC. */
/* 0x10110000 MPMC. */
/* 0x10120000 CLCD Controller. */
/* 0x10130000 DMA Controller. */
/* 0x10140000 Vectored interrupt controller. */
/* 0x101d0000 AHB Monitor Interface. */
/* 0x101e0000 System Controller. */
/* 0x101e1000 Watchdog Interface. */
/* 0x101e2000 Timer 0/1. */
/* 0x101e3000 Timer 2/3. */
/* 0x101e4000 GPIO port 0. */
/* 0x101e5000 GPIO port 1. */
/* 0x101e6000 GPIO port 2. */
/* 0x101e7000 GPIO port 3. */
/* 0x101e8000 RTC. */
/* 0x101f0000 Smart card 0. */
/* 0x101f1000 UART0. */
/* 0x101f2000 UART1. */
/* 0x101f3000 UART2. */
/* 0x101f4000 SSPI. */
/* Load the kernel. */
if (!kernel_filename) {
fprintf(stderr, "Kernel image must be specified\n");
exit(1);
}
kernel_size = load_image(kernel_filename,
phys_ram_base + KERNEL_LOAD_ADDR);
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
exit(1);
}
if (initrd_filename) {
initrd_size = load_image(initrd_filename,
phys_ram_base + INITRD_LOAD_ADDR);
if (initrd_size < 0) {
fprintf(stderr, "qemu: could not load initrd '%s'\n",
initrd_filename);
exit(1);
}
} else {
initrd_size = 0;
}
bootloader[5] = KERNEL_ARGS_ADDR;
bootloader[6] = KERNEL_LOAD_ADDR;
for (n = 0; n < sizeof(bootloader) / 4; n++)
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
set_kernel_args(ram_size, initrd_size, kernel_cmdline);
}
QEMUMachine versatilepb_machine = {
"versatilepb",
"ARM Versatile/PB (ARM926EJ-S)",
vpb_init,
};

1
vl.c
View File

@ -4436,6 +4436,7 @@ void register_machines(void)
#elif defined(TARGET_ARM)
qemu_register_machine(&integratorcp926_machine);
qemu_register_machine(&integratorcp1026_machine);
qemu_register_machine(&versatilepb_machine);
#else
#error unsupported CPU
#endif

19
vl.h
View File

@ -966,6 +966,9 @@ void usb_info(void);
extern QEMUMachine integratorcp926_machine;
extern QEMUMachine integratorcp1026_machine;
/* versatilepb.c */
extern QEMUMachine versatilepb_machine;
/* ps2.c */
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
@ -981,6 +984,22 @@ void smc91c111_init(NICInfo *, uint32_t, void *, int);
/* pl110.c */
void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int);
/* pl011.c */
void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr);
/* pl050.c */
void pl050_init(uint32_t base, void *pic, int irq, int is_mouse);
/* pl080.c */
void *pl080_init(uint32_t base, void *pic, int irq);
/* pl190.c */
void *pl190_init(uint32_t base, void *parent, int irq, int fiq);
/* arm-timer.c */
void sp804_init(uint32_t base, void *pic, int irq);
void icp_pit_init(uint32_t base, void *pic, int irq);
#endif /* defined(QEMU_TOOL) */
/* monitor.c */