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:
		| @@ -476,6 +476,7 @@ CPPFLAGS += -DHAS_AUDIO | |||||||
| endif | endif | ||||||
| ifeq ($(TARGET_BASE_ARCH), sh4) | ifeq ($(TARGET_BASE_ARCH), sh4) | ||||||
| VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | ||||||
|  | VL_OBJS+= sh_timer.o ptimer.o | ||||||
| endif | endif | ||||||
| ifeq ($(TARGET_BASE_ARCH), m68k) | ifeq ($(TARGET_BASE_ARCH), m68k) | ||||||
| VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								hw/sh7750.c
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								hw/sh7750.c
									
									
									
									
									
								
							| @@ -64,13 +64,6 @@ typedef struct SH7750State { | |||||||
|     uint8_t scbrr2; |     uint8_t scbrr2; | ||||||
|     fifo serial2_receive_fifo; |     fifo serial2_receive_fifo; | ||||||
|     fifo serial2_transmit_fifo; |     fifo serial2_transmit_fifo; | ||||||
|     /* Timers */ |  | ||||||
|     uint8_t tstr; |  | ||||||
|     /* Timer 0 */ |  | ||||||
|     QEMUTimer *timer0; |  | ||||||
|     uint16_t tcr0; |  | ||||||
|     uint32_t tcor0; |  | ||||||
|     uint32_t tcnt0; |  | ||||||
|     /* IO ports */ |     /* IO ports */ | ||||||
|     uint16_t gpioic; |     uint16_t gpioic; | ||||||
|     uint32_t pctra; |     uint32_t pctra; | ||||||
| @@ -88,84 +81,9 @@ typedef struct SH7750State { | |||||||
|     sh7750_io_device *devices[NB_DEVICES];	/* External peripherals */ |     sh7750_io_device *devices[NB_DEVICES];	/* External peripherals */ | ||||||
|     /* Cache */ |     /* Cache */ | ||||||
|     uint32_t ccr; |     uint32_t ccr; | ||||||
|  |  | ||||||
| } SH7750State; | } 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 |  First serial port | ||||||
| **********************************************************************/ | **********************************************************************/ | ||||||
| @@ -581,8 +499,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) | |||||||
| 	fprintf(stderr, | 	fprintf(stderr, | ||||||
| 		"Read access to refresh count register, incrementing\n"); | 		"Read access to refresh count register, incrementing\n"); | ||||||
| 	return s->rfcr++; | 	return s->rfcr++; | ||||||
|     case SH7750_TCR0_A7: |  | ||||||
| 	return s->tcr0; |  | ||||||
|     case SH7750_SCLSR2_A7: |     case SH7750_SCLSR2_A7: | ||||||
| 	/* Read and clear overflow bit */ | 	/* Read and clear overflow bit */ | ||||||
| 	r = s->sclsr2; | 	r = s->sclsr2; | ||||||
| @@ -649,10 +565,6 @@ static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr, | |||||||
|     case SH7750_SCBRR2_A7: |     case SH7750_SCBRR2_A7: | ||||||
| 	s->scbrr2 = mem_value; | 	s->scbrr2 = mem_value; | ||||||
| 	return; | 	return; | ||||||
|     case SH7750_TSTR_A7: |  | ||||||
| 	s->tstr = mem_value; |  | ||||||
| 	timer_start_changed(s); |  | ||||||
| 	return; |  | ||||||
|     case SH7750_SCSCR1_A7: |     case SH7750_SCSCR1_A7: | ||||||
| 	s->scscr1 = mem_value; | 	s->scscr1 = mem_value; | ||||||
| 	scscr1_changed(s); | 	scscr1_changed(s); | ||||||
| @@ -721,9 +633,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, | |||||||
|     case SH7750_SCSMR2_A7: |     case SH7750_SCSMR2_A7: | ||||||
| 	s->scsmr2 = mem_value; | 	s->scsmr2 = mem_value; | ||||||
| 	return; | 	return; | ||||||
|     case SH7750_TCR0_A7: |  | ||||||
| 	s->tcr0 = mem_value; |  | ||||||
| 	return; |  | ||||||
|     case SH7750_GPIOIC_A7: |     case SH7750_GPIOIC_A7: | ||||||
| 	s->gpioic = mem_value; | 	s->gpioic = mem_value; | ||||||
| 	if (mem_value != 0) { | 	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); | 	s->portpullupb = portpullup(mem_value); | ||||||
| 	portb_changed(s, temp); | 	portb_changed(s, temp); | ||||||
| 	return; | 	return; | ||||||
|     case SH7750_TCNT0_A7: |  | ||||||
| 	s->tcnt0 = mem_value & 0xf; |  | ||||||
| 	return; |  | ||||||
|     case SH7750_MMUCR_A7: |     case SH7750_MMUCR_A7: | ||||||
| 	s->cpu->mmucr = mem_value; | 	s->cpu->mmucr = mem_value; | ||||||
| 	return; | 	return; | ||||||
| @@ -828,7 +734,11 @@ SH7750State *sh7750_init(CPUSH4State * cpu) | |||||||
| 					      sh7750_mem_read, | 					      sh7750_mem_read, | ||||||
| 					      sh7750_mem_write, s); | 					      sh7750_mem_write, s); | ||||||
|     cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); |     cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); | ||||||
|     init_timers(s); |  | ||||||
|     init_serial_ports(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; |     return s; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -42,18 +42,6 @@ static regname_t regnames[] = { | |||||||
| 	REGNAME(SH7750_RMONAR_A7) | 	REGNAME(SH7750_RMONAR_A7) | ||||||
| 	REGNAME(SH7750_RCR1_A7) | 	REGNAME(SH7750_RCR1_A7) | ||||||
| 	REGNAME(SH7750_RCR2_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_BCR1_A7) | ||||||
| 	REGNAME(SH7750_BCR2_A7) | 	REGNAME(SH7750_BCR2_A7) | ||||||
| 	REGNAME(SH7750_WCR1_A7) | 	REGNAME(SH7750_WCR1_A7) | ||||||
|   | |||||||
| @@ -524,94 +524,6 @@ | |||||||
| 					   year counters are stopped | 					   year counters are stopped | ||||||
| 					   1 - sec, min, hr, day-of-week, month, | 					   1 - sec, min, hr, day-of-week, month, | ||||||
| 					   year counters operate normally */ | 					   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 |  * Bus State Controller - BSC | ||||||
|  */ |  */ | ||||||
|   | |||||||
							
								
								
									
										323
									
								
								hw/sh_timer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								hw/sh_timer.c
									
									
									
									
									
										Normal 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
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								vl.h
									
									
									
									
									
								
							| @@ -1517,6 +1517,12 @@ typedef struct { | |||||||
|  |  | ||||||
| int sh7750_register_io_device(struct SH7750State *s, | int sh7750_register_io_device(struct SH7750State *s, | ||||||
| 			      sh7750_io_device * device); | 			      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 */ | /* tc58128.c */ | ||||||
| int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); | int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user