From cd1a3f6840e9f4b57860ee0d151347e6ade73d11 Mon Sep 17 00:00:00 2001 From: ths Date: Sat, 29 Sep 2007 19:40:09 +0000 Subject: [PATCH] Stand-alone TMU emulation code, by Magnus Damm. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3269 c046a42c-6fe2-441c-8c8c-71466251a162 --- Makefile.target | 1 + hw/sh7750.c | 102 +------------- hw/sh7750_regnames.c | 12 -- hw/sh7750_regs.h | 88 ------------ hw/sh_timer.c | 323 +++++++++++++++++++++++++++++++++++++++++++ vl.h | 6 + 6 files changed, 336 insertions(+), 196 deletions(-) create mode 100644 hw/sh_timer.c diff --git a/Makefile.target b/Makefile.target index 280f3711b8..fd44a8120b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -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 diff --git a/hw/sh7750.c b/hw/sh7750.c index dcba14e1af..9002da31af 100644 --- a/hw/sh7750.c +++ b/hw/sh7750.c @@ -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; } diff --git a/hw/sh7750_regnames.c b/hw/sh7750_regnames.c index 5fcb0d6cca..6ae491771f 100644 --- a/hw/sh7750_regnames.c +++ b/hw/sh7750_regnames.c @@ -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) diff --git a/hw/sh7750_regs.h b/hw/sh7750_regs.h index 6b18ad2e15..85eab43123 100644 --- a/hw/sh7750_regs.h +++ b/hw/sh7750_regs.h @@ -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 */ diff --git a/hw/sh_timer.c b/hw/sh_timer.c new file mode 100644 index 0000000000..40f3930cf4 --- /dev/null +++ b/hw/sh_timer.c @@ -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. */ +} diff --git a/vl.h b/vl.h index ea7a05e3f2..43adc2450d 100644 --- a/vl.h +++ b/vl.h @@ -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);