Stand-alone TMU emulation code, by Magnus Damm.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3269 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
ths 2007-09-29 19:40:09 +00:00
parent 0d78f544de
commit cd1a3f6840
6 changed files with 336 additions and 196 deletions

View File

@ -476,6 +476,7 @@ CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), sh4)
VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
VL_OBJS+= sh_timer.o ptimer.o
endif
ifeq ($(TARGET_BASE_ARCH), m68k)
VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o

View File

@ -64,13 +64,6 @@ typedef struct SH7750State {
uint8_t scbrr2;
fifo serial2_receive_fifo;
fifo serial2_transmit_fifo;
/* Timers */
uint8_t tstr;
/* Timer 0 */
QEMUTimer *timer0;
uint16_t tcr0;
uint32_t tcor0;
uint32_t tcnt0;
/* IO ports */
uint16_t gpioic;
uint32_t pctra;
@ -88,84 +81,9 @@ typedef struct SH7750State {
sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */
/* Cache */
uint32_t ccr;
} SH7750State;
/**********************************************************************
Timers
**********************************************************************/
/* XXXXX At this time, timer0 works in underflow only mode, that is
the value of tcnt0 is read at alarm computation time and cannot
be read back by the guest OS */
static void start_timer0(SH7750State * s)
{
uint64_t now, next, prescaler;
if ((s->tcr0 & 6) == 6) {
fprintf(stderr, "rtc clock for timer 0 not supported\n");
assert(0);
}
if ((s->tcr0 & 7) == 5) {
fprintf(stderr, "timer 0 configuration not supported\n");
assert(0);
}
if ((s->tcr0 & 4) == 4)
prescaler = 1024;
else
prescaler = 4 << (s->tcr0 & 3);
now = qemu_get_clock(vm_clock);
/* XXXXX */
next =
now + muldiv64(prescaler * s->tcnt0, ticks_per_sec,
s->periph_freq);
if (next == now)
next = now + 1;
fprintf(stderr, "now=%016" PRIx64 ", next=%016" PRIx64 "\n", now, next);
fprintf(stderr, "timer will underflow in %f seconds\n",
(float) (next - now) / (float) ticks_per_sec);
qemu_mod_timer(s->timer0, next);
}
static void timer_start_changed(SH7750State * s)
{
if (s->tstr & SH7750_TSTR_STR0) {
start_timer0(s);
} else {
fprintf(stderr, "timer 0 is stopped\n");
qemu_del_timer(s->timer0);
}
}
static void timer0_cb(void *opaque)
{
SH7750State *s = opaque;
s->tcnt0 = (uint32_t) 0; /* XXXXX */
if (--s->tcnt0 == (uint32_t) - 1) {
fprintf(stderr, "timer 0 underflow\n");
s->tcnt0 = s->tcor0;
s->tcr0 |= SH7750_TCR_UNF;
if (s->tcr0 & SH7750_TCR_UNIE) {
fprintf(stderr,
"interrupt generation for timer 0 not supported\n");
assert(0);
}
}
start_timer0(s);
}
static void init_timers(SH7750State * s)
{
s->tcor0 = 0xffffffff;
s->tcnt0 = 0xffffffff;
s->timer0 = qemu_new_timer(vm_clock, &timer0_cb, s);
}
/**********************************************************************
First serial port
**********************************************************************/
@ -581,8 +499,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr)
fprintf(stderr,
"Read access to refresh count register, incrementing\n");
return s->rfcr++;
case SH7750_TCR0_A7:
return s->tcr0;
case SH7750_SCLSR2_A7:
/* Read and clear overflow bit */
r = s->sclsr2;
@ -649,10 +565,6 @@ static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr,
case SH7750_SCBRR2_A7:
s->scbrr2 = mem_value;
return;
case SH7750_TSTR_A7:
s->tstr = mem_value;
timer_start_changed(s);
return;
case SH7750_SCSCR1_A7:
s->scscr1 = mem_value;
scscr1_changed(s);
@ -721,9 +633,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr,
case SH7750_SCSMR2_A7:
s->scsmr2 = mem_value;
return;
case SH7750_TCR0_A7:
s->tcr0 = mem_value;
return;
case SH7750_GPIOIC_A7:
s->gpioic = mem_value;
if (mem_value != 0) {
@ -768,9 +677,6 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr,
s->portpullupb = portpullup(mem_value);
portb_changed(s, temp);
return;
case SH7750_TCNT0_A7:
s->tcnt0 = mem_value & 0xf;
return;
case SH7750_MMUCR_A7:
s->cpu->mmucr = mem_value;
return;
@ -828,7 +734,11 @@ SH7750State *sh7750_init(CPUSH4State * cpu)
sh7750_mem_read,
sh7750_mem_write, s);
cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory);
init_timers(s);
init_serial_ports(s);
tmu012_init(0x1fd80000,
TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,
s->periph_freq);
tmu012_init(0x1e100000, 0, s->periph_freq);
return s;
}

View File

@ -42,18 +42,6 @@ static regname_t regnames[] = {
REGNAME(SH7750_RMONAR_A7)
REGNAME(SH7750_RCR1_A7)
REGNAME(SH7750_RCR2_A7)
REGNAME(SH7750_TOCR_A7)
REGNAME(SH7750_TSTR_A7)
REGNAME(SH7750_TCOR0_A7)
REGNAME(SH7750_TCOR1_A7)
REGNAME(SH7750_TCOR2_A7)
REGNAME(SH7750_TCNT0_A7)
REGNAME(SH7750_TCNT1_A7)
REGNAME(SH7750_TCNT2_A7)
REGNAME(SH7750_TCR0_A7)
REGNAME(SH7750_TCR1_A7)
REGNAME(SH7750_TCR2_A7)
REGNAME(SH7750_TCPR2_A7)
REGNAME(SH7750_BCR1_A7)
REGNAME(SH7750_BCR2_A7)
REGNAME(SH7750_WCR1_A7)

View File

@ -524,94 +524,6 @@
year counters are stopped
1 - sec, min, hr, day-of-week, month,
year counters operate normally */
/*
* Timer Unit (TMU)
*/
/* Timer Output Control Register (byte) - TOCR */
#define SH7750_TOCR_REGOFS 0xD80000 /* offset */
#define SH7750_TOCR SH7750_P4_REG32(SH7750_TOCR_REGOFS)
#define SH7750_TOCR_A7 SH7750_A7_REG32(SH7750_TOCR_REGOFS)
#define SH7750_TOCR_TCOE 0x01 /* Timer Clock Pin Control:
0 - TCLK is used as external clock
input or input capture control
1 - TCLK is used as on-chip RTC
output clock pin */
/* Timer Start Register (byte) - TSTR */
#define SH7750_TSTR_REGOFS 0xD80004 /* offset */
#define SH7750_TSTR SH7750_P4_REG32(SH7750_TSTR_REGOFS)
#define SH7750_TSTR_A7 SH7750_A7_REG32(SH7750_TSTR_REGOFS)
#define SH7750_TSTR_STR2 0x04 /* TCNT2 performs count operations */
#define SH7750_TSTR_STR1 0x02 /* TCNT1 performs count operations */
#define SH7750_TSTR_STR0 0x01 /* TCNT0 performs count operations */
#define SH7750_TSTR_STR(n) (1 << (n))
/* Timer Constant Register - TCOR0, TCOR1, TCOR2 */
#define SH7750_TCOR_REGOFS(n) (0xD80008 + ((n)*12)) /* offset */
#define SH7750_TCOR(n) SH7750_P4_REG32(SH7750_TCOR_REGOFS(n))
#define SH7750_TCOR_A7(n) SH7750_A7_REG32(SH7750_TCOR_REGOFS(n))
#define SH7750_TCOR0 SH7750_TCOR(0)
#define SH7750_TCOR1 SH7750_TCOR(1)
#define SH7750_TCOR2 SH7750_TCOR(2)
#define SH7750_TCOR0_A7 SH7750_TCOR_A7(0)
#define SH7750_TCOR1_A7 SH7750_TCOR_A7(1)
#define SH7750_TCOR2_A7 SH7750_TCOR_A7(2)
/* Timer Counter Register - TCNT0, TCNT1, TCNT2 */
#define SH7750_TCNT_REGOFS(n) (0xD8000C + ((n)*12)) /* offset */
#define SH7750_TCNT(n) SH7750_P4_REG32(SH7750_TCNT_REGOFS(n))
#define SH7750_TCNT_A7(n) SH7750_A7_REG32(SH7750_TCNT_REGOFS(n))
#define SH7750_TCNT0 SH7750_TCNT(0)
#define SH7750_TCNT1 SH7750_TCNT(1)
#define SH7750_TCNT2 SH7750_TCNT(2)
#define SH7750_TCNT0_A7 SH7750_TCNT_A7(0)
#define SH7750_TCNT1_A7 SH7750_TCNT_A7(1)
#define SH7750_TCNT2_A7 SH7750_TCNT_A7(2)
/* Timer Control Register (half) - TCR0, TCR1, TCR2 */
#define SH7750_TCR_REGOFS(n) (0xD80010 + ((n)*12)) /* offset */
#define SH7750_TCR(n) SH7750_P4_REG32(SH7750_TCR_REGOFS(n))
#define SH7750_TCR_A7(n) SH7750_A7_REG32(SH7750_TCR_REGOFS(n))
#define SH7750_TCR0 SH7750_TCR(0)
#define SH7750_TCR1 SH7750_TCR(1)
#define SH7750_TCR2 SH7750_TCR(2)
#define SH7750_TCR0_A7 SH7750_TCR_A7(0)
#define SH7750_TCR1_A7 SH7750_TCR_A7(1)
#define SH7750_TCR2_A7 SH7750_TCR_A7(2)
#define SH7750_TCR2_ICPF 0x200 /* Input Capture Interrupt Flag
(1 - input capture has occured) */
#define SH7750_TCR_UNF 0x100 /* Underflow flag */
#define SH7750_TCR2_ICPE 0x0C0 /* Input Capture Control: */
#define SH7750_TCR2_ICPE_DIS 0x000 /* Input Capture function is not used */
#define SH7750_TCR2_ICPE_NOINT 0x080 /* Input Capture function is used, but
input capture interrupt is not
enabled */
#define SH7750_TCR2_ICPE_INT 0x0C0 /* Input Capture function is used,
input capture interrupt enabled */
#define SH7750_TCR_UNIE 0x020 /* Underflow Interrupt Control
(1 - underflow interrupt enabled) */
#define SH7750_TCR_CKEG 0x018 /* Clock Edge selection: */
#define SH7750_TCR_CKEG_RAISE 0x000 /* Count/capture on rising edge */
#define SH7750_TCR_CKEG_FALL 0x008 /* Count/capture on falling edge */
#define SH7750_TCR_CKEG_BOTH 0x018 /* Count/capture on both rising and
falling edges */
#define SH7750_TCR_TPSC 0x007 /* Timer prescaler */
#define SH7750_TCR_TPSC_DIV4 0x000 /* Counts on peripheral clock/4 */
#define SH7750_TCR_TPSC_DIV16 0x001 /* Counts on peripheral clock/16 */
#define SH7750_TCR_TPSC_DIV64 0x002 /* Counts on peripheral clock/64 */
#define SH7750_TCR_TPSC_DIV256 0x003 /* Counts on peripheral clock/256 */
#define SH7750_TCR_TPSC_DIV1024 0x004 /* Counts on peripheral clock/1024 */
#define SH7750_TCR_TPSC_RTC 0x006 /* Counts on on-chip RTC output clk */
#define SH7750_TCR_TPSC_EXT 0x007 /* Counts on external clock */
/* Input Capture Register (read-only) - TCPR2 */
#define SH7750_TCPR2_REGOFS 0xD8002C /* offset */
#define SH7750_TCPR2 SH7750_P4_REG32(SH7750_TCPR2_REGOFS)
#define SH7750_TCPR2_A7 SH7750_A7_REG32(SH7750_TCPR2_REGOFS)
/*
* Bus State Controller - BSC
*/

323
hw/sh_timer.c Normal file
View File

@ -0,0 +1,323 @@
/*
* SuperH Timer modules.
*
* Copyright (c) 2007 Magnus Damm
* Based on arm_timer.c by Paul Brook
* Copyright (c) 2005-2006 CodeSourcery.
*
* This code is licenced under the GPL.
*/
#include "vl.h"
//#define DEBUG_TIMER
#define TIMER_TCR_TPSC (7 << 0)
#define TIMER_TCR_CKEG (3 << 3)
#define TIMER_TCR_UNIE (1 << 5)
#define TIMER_TCR_ICPE (3 << 6)
#define TIMER_TCR_UNF (1 << 8)
#define TIMER_TCR_ICPF (1 << 9)
#define TIMER_TCR_RESERVED (0x3f << 10)
#define TIMER_FEAT_CAPT (1 << 0)
#define TIMER_FEAT_EXTCLK (1 << 1)
typedef struct {
ptimer_state *timer;
uint32_t tcnt;
uint32_t tcor;
uint32_t tcr;
uint32_t tcpr;
int freq;
int int_level;
int feat;
int enabled;
qemu_irq irq;
} sh_timer_state;
/* Check all active timers, and schedule the next timer interrupt. */
static void sh_timer_update(sh_timer_state *s)
{
#if 0 /* not yet */
/* Update interrupts. */
if (s->int_level && (s->tcr & TIMER_TCR_UNIE)) {
qemu_irq_raise(s->irq);
} else {
qemu_irq_lower(s->irq);
}
#endif
}
uint32_t sh_timer_read(void *opaque, target_phys_addr_t offset)
{
sh_timer_state *s = (sh_timer_state *)opaque;
switch (offset >> 2) {
case 0:
return s->tcor;
case 1:
return ptimer_get_count(s->timer);
case 2:
return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
case 3:
if (s->feat & TIMER_FEAT_CAPT)
return s->tcpr;
default:
cpu_abort (cpu_single_env, "sh_timer_read: Bad offset %x\n",
(int)offset);
return 0;
}
}
static void sh_timer_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
sh_timer_state *s = (sh_timer_state *)opaque;
int freq;
switch (offset >> 2) {
case 0:
s->tcor = value;
ptimer_set_limit(s->timer, s->tcor, 0);
break;
case 1:
s->tcnt = value;
ptimer_set_count(s->timer, s->tcnt);
break;
case 2:
if (s->enabled) {
/* Pause the timer if it is running. This may cause some
inaccuracy dure to rounding, but avoids a whole lot of other
messyness. */
ptimer_stop(s->timer);
}
freq = s->freq;
/* ??? Need to recalculate expiry time after changing divisor. */
switch (value & TIMER_TCR_TPSC) {
case 0: freq >>= 2; break;
case 1: freq >>= 4; break;
case 2: freq >>= 6; break;
case 3: freq >>= 8; break;
case 4: freq >>= 10; break;
case 6:
case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
default: cpu_abort (cpu_single_env,
"sh_timer_write: Reserved TPSC value\n"); break;
}
switch ((value & TIMER_TCR_CKEG) >> 3) {
case 0: break;
case 1:
case 2:
case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
default: cpu_abort (cpu_single_env,
"sh_timer_write: Reserved CKEG value\n"); break;
}
switch ((value & TIMER_TCR_ICPE) >> 6) {
case 0: break;
case 2:
case 3: if (s->feat & TIMER_FEAT_CAPT) break;
default: cpu_abort (cpu_single_env,
"sh_timer_write: Reserved ICPE value\n"); break;
}
if ((value & TIMER_TCR_UNF) == 0)
s->int_level = 0;
value &= ~TIMER_TCR_UNF;
if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
cpu_abort (cpu_single_env,
"sh_timer_write: Reserved ICPF value\n");
value &= ~TIMER_TCR_ICPF; /* capture not supported */
if (value & TIMER_TCR_RESERVED)
cpu_abort (cpu_single_env,
"sh_timer_write: Reserved TCR bits set\n");
s->tcr = value;
ptimer_set_limit(s->timer, s->tcor, 0);
ptimer_set_freq(s->timer, freq);
if (s->enabled) {
/* Restart the timer if still enabled. */
ptimer_run(s->timer, 0);
}
break;
case 3:
if (s->feat & TIMER_FEAT_CAPT) {
s->tcpr = value;
break;
}
default:
cpu_abort (cpu_single_env, "sh_timer_write: Bad offset %x\n",
(int)offset);
}
sh_timer_update(s);
}
static void sh_timer_start_stop(void *opaque, int enable)
{
sh_timer_state *s = (sh_timer_state *)opaque;
#ifdef DEBUG_TIMER
printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
#endif
if (s->enabled && !enable) {
ptimer_stop(s->timer);
}
if (!s->enabled && enable) {
ptimer_run(s->timer, 0);
}
s->enabled = !!enable;
#ifdef DEBUG_TIMER
printf("sh_timer_start_stop done %d\n", s->enabled);
#endif
}
static void sh_timer_tick(void *opaque)
{
sh_timer_state *s = (sh_timer_state *)opaque;
s->int_level = s->enabled;
sh_timer_update(s);
}
static void *sh_timer_init(uint32_t freq, int feat)
{
sh_timer_state *s;
QEMUBH *bh;
s = (sh_timer_state *)qemu_mallocz(sizeof(sh_timer_state));
s->freq = freq;
s->feat = feat;
s->tcor = 0xffffffff;
s->tcnt = 0xffffffff;
s->tcpr = 0xdeadbeef;
s->tcor = 0;
s->enabled = 0;
bh = qemu_bh_new(sh_timer_tick, s);
s->timer = ptimer_init(bh);
/* ??? Save/restore. */
return s;
}
typedef struct {
void *timer[3];
int level[3];
uint32_t tocr;
uint32_t tstr;
target_phys_addr_t base;
int feat;
} tmu012_state;
static uint32_t tmu012_read(void *opaque, target_phys_addr_t offset)
{
tmu012_state *s = (tmu012_state *)opaque;
#ifdef DEBUG_TIMER
printf("tmu012_read 0x%lx\n", (unsigned long) offset);
#endif
offset -= s->base;
if (offset >= 0x20) {
if (!(s->feat & TMU012_FEAT_3CHAN))
cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n",
(int)offset);
return sh_timer_read(s->timer[2], offset - 0x20);
}
if (offset >= 0x14)
return sh_timer_read(s->timer[1], offset - 0x14);
if (offset >= 0x08)
return sh_timer_read(s->timer[0], offset - 0x08);
if (offset == 4)
return s->tstr;
if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
return s->tocr;
cpu_abort (cpu_single_env, "tmu012_write: Bad offset %x\n",
(int)offset);
return 0;
}
static void tmu012_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
tmu012_state *s = (tmu012_state *)opaque;
#ifdef DEBUG_TIMER
printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
#endif
offset -= s->base;
if (offset >= 0x20) {
if (!(s->feat & TMU012_FEAT_3CHAN))
cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n",
(int)offset);
sh_timer_write(s->timer[2], offset - 0x20, value);
return;
}
if (offset >= 0x14) {
sh_timer_write(s->timer[1], offset - 0x14, value);
return;
}
if (offset >= 0x08) {
sh_timer_write(s->timer[0], offset - 0x08, value);
return;
}
if (offset == 4) {
sh_timer_start_stop(s->timer[0], value & (1 << 0));
sh_timer_start_stop(s->timer[1], value & (1 << 1));
if (s->feat & TMU012_FEAT_3CHAN)
sh_timer_start_stop(s->timer[2], value & (1 << 2));
else
if (value & (1 << 2))
cpu_abort (cpu_single_env, "tmu012_write: Bad channel\n");
s->tstr = value;
return;
}
if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
s->tocr = value & (1 << 0);
}
}
static CPUReadMemoryFunc *tmu012_readfn[] = {
tmu012_read,
tmu012_read,
tmu012_read
};
static CPUWriteMemoryFunc *tmu012_writefn[] = {
tmu012_write,
tmu012_write,
tmu012_write
};
void tmu012_init(uint32_t base, int feat, uint32_t freq)
{
int iomemtype;
tmu012_state *s;
int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
s = (tmu012_state *)qemu_mallocz(sizeof(tmu012_state));
s->base = base;
s->feat = feat;
s->timer[0] = sh_timer_init(freq, timer_feat);
s->timer[1] = sh_timer_init(freq, timer_feat);
if (feat & TMU012_FEAT_3CHAN)
s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT);
iomemtype = cpu_register_io_memory(0, tmu012_readfn,
tmu012_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
/* ??? Save/restore. */
}

6
vl.h
View File

@ -1517,6 +1517,12 @@ typedef struct {
int sh7750_register_io_device(struct SH7750State *s,
sh7750_io_device * device);
/* sh_timer.c */
#define TMU012_FEAT_TOCR (1 << 0)
#define TMU012_FEAT_3CHAN (1 << 1)
#define TMU012_FEAT_EXTCLK (1 << 2)
void tmu012_init(uint32_t base, int feat, uint32_t freq);
/* tc58128.c */
int tc58128_init(struct SH7750State *s, char *zone1, char *zone2);