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
@ -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
73
hw/arm_pic.c
Normal 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
27
hw/arm_pic.h
Normal 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
383
hw/arm_timer.c
Normal 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. */
|
||||
}
|
||||
|
@ -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
251
hw/pl011.c
Normal 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
127
hw/pl050.c
Normal 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
328
hw/pl080.c
Normal 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;
|
||||
}
|
||||
|
27
hw/pl110.c
27
hw/pl110.c
@ -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
252
hw/pl190.c
Normal 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
321
hw/versatilepb.c
Normal 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
1
vl.c
@ -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
19
vl.h
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user