Commit cd1a3f6840e9f4b57860ee0d151347e6ade73d11
1 parent
0d78f544
Stand-alone TMU emulation code, by Magnus Damm.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3269 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
336 additions
and
196 deletions
Makefile.target
@@ -476,6 +476,7 @@ CPPFLAGS += -DHAS_AUDIO | @@ -476,6 +476,7 @@ CPPFLAGS += -DHAS_AUDIO | ||
476 | endif | 476 | endif |
477 | ifeq ($(TARGET_BASE_ARCH), sh4) | 477 | ifeq ($(TARGET_BASE_ARCH), sh4) |
478 | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | 478 | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o |
479 | +VL_OBJS+= sh_timer.o ptimer.o | ||
479 | endif | 480 | endif |
480 | ifeq ($(TARGET_BASE_ARCH), m68k) | 481 | ifeq ($(TARGET_BASE_ARCH), m68k) |
481 | VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | 482 | VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o |
hw/sh7750.c
@@ -64,13 +64,6 @@ typedef struct SH7750State { | @@ -64,13 +64,6 @@ typedef struct SH7750State { | ||
64 | uint8_t scbrr2; | 64 | uint8_t scbrr2; |
65 | fifo serial2_receive_fifo; | 65 | fifo serial2_receive_fifo; |
66 | fifo serial2_transmit_fifo; | 66 | fifo serial2_transmit_fifo; |
67 | - /* Timers */ | ||
68 | - uint8_t tstr; | ||
69 | - /* Timer 0 */ | ||
70 | - QEMUTimer *timer0; | ||
71 | - uint16_t tcr0; | ||
72 | - uint32_t tcor0; | ||
73 | - uint32_t tcnt0; | ||
74 | /* IO ports */ | 67 | /* IO ports */ |
75 | uint16_t gpioic; | 68 | uint16_t gpioic; |
76 | uint32_t pctra; | 69 | uint32_t pctra; |
@@ -88,83 +81,8 @@ typedef struct SH7750State { | @@ -88,83 +81,8 @@ typedef struct SH7750State { | ||
88 | sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ | 81 | sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ |
89 | /* Cache */ | 82 | /* Cache */ |
90 | uint32_t ccr; | 83 | uint32_t ccr; |
91 | -} SH7750State; | ||
92 | - | ||
93 | -/********************************************************************** | ||
94 | - Timers | ||
95 | -**********************************************************************/ | ||
96 | - | ||
97 | -/* XXXXX At this time, timer0 works in underflow only mode, that is | ||
98 | - the value of tcnt0 is read at alarm computation time and cannot | ||
99 | - be read back by the guest OS */ | ||
100 | - | ||
101 | -static void start_timer0(SH7750State * s) | ||
102 | -{ | ||
103 | - uint64_t now, next, prescaler; | ||
104 | - | ||
105 | - if ((s->tcr0 & 6) == 6) { | ||
106 | - fprintf(stderr, "rtc clock for timer 0 not supported\n"); | ||
107 | - assert(0); | ||
108 | - } | ||
109 | 84 | ||
110 | - if ((s->tcr0 & 7) == 5) { | ||
111 | - fprintf(stderr, "timer 0 configuration not supported\n"); | ||
112 | - assert(0); | ||
113 | - } | ||
114 | - | ||
115 | - if ((s->tcr0 & 4) == 4) | ||
116 | - prescaler = 1024; | ||
117 | - else | ||
118 | - prescaler = 4 << (s->tcr0 & 3); | ||
119 | - | ||
120 | - now = qemu_get_clock(vm_clock); | ||
121 | - /* XXXXX */ | ||
122 | - next = | ||
123 | - now + muldiv64(prescaler * s->tcnt0, ticks_per_sec, | ||
124 | - s->periph_freq); | ||
125 | - if (next == now) | ||
126 | - next = now + 1; | ||
127 | - fprintf(stderr, "now=%016" PRIx64 ", next=%016" PRIx64 "\n", now, next); | ||
128 | - fprintf(stderr, "timer will underflow in %f seconds\n", | ||
129 | - (float) (next - now) / (float) ticks_per_sec); | ||
130 | - | ||
131 | - qemu_mod_timer(s->timer0, next); | ||
132 | -} | ||
133 | - | ||
134 | -static void timer_start_changed(SH7750State * s) | ||
135 | -{ | ||
136 | - if (s->tstr & SH7750_TSTR_STR0) { | ||
137 | - start_timer0(s); | ||
138 | - } else { | ||
139 | - fprintf(stderr, "timer 0 is stopped\n"); | ||
140 | - qemu_del_timer(s->timer0); | ||
141 | - } | ||
142 | -} | ||
143 | - | ||
144 | -static void timer0_cb(void *opaque) | ||
145 | -{ | ||
146 | - SH7750State *s = opaque; | ||
147 | - | ||
148 | - s->tcnt0 = (uint32_t) 0; /* XXXXX */ | ||
149 | - if (--s->tcnt0 == (uint32_t) - 1) { | ||
150 | - fprintf(stderr, "timer 0 underflow\n"); | ||
151 | - s->tcnt0 = s->tcor0; | ||
152 | - s->tcr0 |= SH7750_TCR_UNF; | ||
153 | - if (s->tcr0 & SH7750_TCR_UNIE) { | ||
154 | - fprintf(stderr, | ||
155 | - "interrupt generation for timer 0 not supported\n"); | ||
156 | - assert(0); | ||
157 | - } | ||
158 | - } | ||
159 | - start_timer0(s); | ||
160 | -} | ||
161 | - | ||
162 | -static void init_timers(SH7750State * s) | ||
163 | -{ | ||
164 | - s->tcor0 = 0xffffffff; | ||
165 | - s->tcnt0 = 0xffffffff; | ||
166 | - s->timer0 = qemu_new_timer(vm_clock, &timer0_cb, s); | ||
167 | -} | 85 | +} SH7750State; |
168 | 86 | ||
169 | /********************************************************************** | 87 | /********************************************************************** |
170 | First serial port | 88 | First serial port |
@@ -581,8 +499,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) | @@ -581,8 +499,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) | ||
581 | fprintf(stderr, | 499 | fprintf(stderr, |
582 | "Read access to refresh count register, incrementing\n"); | 500 | "Read access to refresh count register, incrementing\n"); |
583 | return s->rfcr++; | 501 | return s->rfcr++; |
584 | - case SH7750_TCR0_A7: | ||
585 | - return s->tcr0; | ||
586 | case SH7750_SCLSR2_A7: | 502 | case SH7750_SCLSR2_A7: |
587 | /* Read and clear overflow bit */ | 503 | /* Read and clear overflow bit */ |
588 | r = s->sclsr2; | 504 | r = s->sclsr2; |
@@ -649,10 +565,6 @@ static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr, | @@ -649,10 +565,6 @@ static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr, | ||
649 | case SH7750_SCBRR2_A7: | 565 | case SH7750_SCBRR2_A7: |
650 | s->scbrr2 = mem_value; | 566 | s->scbrr2 = mem_value; |
651 | return; | 567 | return; |
652 | - case SH7750_TSTR_A7: | ||
653 | - s->tstr = mem_value; | ||
654 | - timer_start_changed(s); | ||
655 | - return; | ||
656 | case SH7750_SCSCR1_A7: | 568 | case SH7750_SCSCR1_A7: |
657 | s->scscr1 = mem_value; | 569 | s->scscr1 = mem_value; |
658 | scscr1_changed(s); | 570 | scscr1_changed(s); |
@@ -721,9 +633,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, | @@ -721,9 +633,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, | ||
721 | case SH7750_SCSMR2_A7: | 633 | case SH7750_SCSMR2_A7: |
722 | s->scsmr2 = mem_value; | 634 | s->scsmr2 = mem_value; |
723 | return; | 635 | return; |
724 | - case SH7750_TCR0_A7: | ||
725 | - s->tcr0 = mem_value; | ||
726 | - return; | ||
727 | case SH7750_GPIOIC_A7: | 636 | case SH7750_GPIOIC_A7: |
728 | s->gpioic = mem_value; | 637 | s->gpioic = mem_value; |
729 | if (mem_value != 0) { | 638 | if (mem_value != 0) { |
@@ -768,9 +677,6 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, | @@ -768,9 +677,6 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, | ||
768 | s->portpullupb = portpullup(mem_value); | 677 | s->portpullupb = portpullup(mem_value); |
769 | portb_changed(s, temp); | 678 | portb_changed(s, temp); |
770 | return; | 679 | return; |
771 | - case SH7750_TCNT0_A7: | ||
772 | - s->tcnt0 = mem_value & 0xf; | ||
773 | - return; | ||
774 | case SH7750_MMUCR_A7: | 680 | case SH7750_MMUCR_A7: |
775 | s->cpu->mmucr = mem_value; | 681 | s->cpu->mmucr = mem_value; |
776 | return; | 682 | return; |
@@ -828,7 +734,11 @@ SH7750State *sh7750_init(CPUSH4State * cpu) | @@ -828,7 +734,11 @@ SH7750State *sh7750_init(CPUSH4State * cpu) | ||
828 | sh7750_mem_read, | 734 | sh7750_mem_read, |
829 | sh7750_mem_write, s); | 735 | sh7750_mem_write, s); |
830 | cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); | 736 | cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); |
831 | - init_timers(s); | ||
832 | init_serial_ports(s); | 737 | init_serial_ports(s); |
738 | + | ||
739 | + tmu012_init(0x1fd80000, | ||
740 | + TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, | ||
741 | + s->periph_freq); | ||
742 | + tmu012_init(0x1e100000, 0, s->periph_freq); | ||
833 | return s; | 743 | return s; |
834 | } | 744 | } |
hw/sh7750_regnames.c
@@ -42,18 +42,6 @@ static regname_t regnames[] = { | @@ -42,18 +42,6 @@ static regname_t regnames[] = { | ||
42 | REGNAME(SH7750_RMONAR_A7) | 42 | REGNAME(SH7750_RMONAR_A7) |
43 | REGNAME(SH7750_RCR1_A7) | 43 | REGNAME(SH7750_RCR1_A7) |
44 | REGNAME(SH7750_RCR2_A7) | 44 | REGNAME(SH7750_RCR2_A7) |
45 | - REGNAME(SH7750_TOCR_A7) | ||
46 | - REGNAME(SH7750_TSTR_A7) | ||
47 | - REGNAME(SH7750_TCOR0_A7) | ||
48 | - REGNAME(SH7750_TCOR1_A7) | ||
49 | - REGNAME(SH7750_TCOR2_A7) | ||
50 | - REGNAME(SH7750_TCNT0_A7) | ||
51 | - REGNAME(SH7750_TCNT1_A7) | ||
52 | - REGNAME(SH7750_TCNT2_A7) | ||
53 | - REGNAME(SH7750_TCR0_A7) | ||
54 | - REGNAME(SH7750_TCR1_A7) | ||
55 | - REGNAME(SH7750_TCR2_A7) | ||
56 | - REGNAME(SH7750_TCPR2_A7) | ||
57 | REGNAME(SH7750_BCR1_A7) | 45 | REGNAME(SH7750_BCR1_A7) |
58 | REGNAME(SH7750_BCR2_A7) | 46 | REGNAME(SH7750_BCR2_A7) |
59 | REGNAME(SH7750_WCR1_A7) | 47 | REGNAME(SH7750_WCR1_A7) |
hw/sh7750_regs.h
@@ -524,94 +524,6 @@ | @@ -524,94 +524,6 @@ | ||
524 | year counters are stopped | 524 | year counters are stopped |
525 | 1 - sec, min, hr, day-of-week, month, | 525 | 1 - sec, min, hr, day-of-week, month, |
526 | year counters operate normally */ | 526 | year counters operate normally */ |
527 | - | ||
528 | - | ||
529 | -/* | ||
530 | - * Timer Unit (TMU) | ||
531 | - */ | ||
532 | -/* Timer Output Control Register (byte) - TOCR */ | ||
533 | -#define SH7750_TOCR_REGOFS 0xD80000 /* offset */ | ||
534 | -#define SH7750_TOCR SH7750_P4_REG32(SH7750_TOCR_REGOFS) | ||
535 | -#define SH7750_TOCR_A7 SH7750_A7_REG32(SH7750_TOCR_REGOFS) | ||
536 | -#define SH7750_TOCR_TCOE 0x01 /* Timer Clock Pin Control: | ||
537 | - 0 - TCLK is used as external clock | ||
538 | - input or input capture control | ||
539 | - 1 - TCLK is used as on-chip RTC | ||
540 | - output clock pin */ | ||
541 | - | ||
542 | -/* Timer Start Register (byte) - TSTR */ | ||
543 | -#define SH7750_TSTR_REGOFS 0xD80004 /* offset */ | ||
544 | -#define SH7750_TSTR SH7750_P4_REG32(SH7750_TSTR_REGOFS) | ||
545 | -#define SH7750_TSTR_A7 SH7750_A7_REG32(SH7750_TSTR_REGOFS) | ||
546 | -#define SH7750_TSTR_STR2 0x04 /* TCNT2 performs count operations */ | ||
547 | -#define SH7750_TSTR_STR1 0x02 /* TCNT1 performs count operations */ | ||
548 | -#define SH7750_TSTR_STR0 0x01 /* TCNT0 performs count operations */ | ||
549 | -#define SH7750_TSTR_STR(n) (1 << (n)) | ||
550 | - | ||
551 | -/* Timer Constant Register - TCOR0, TCOR1, TCOR2 */ | ||
552 | -#define SH7750_TCOR_REGOFS(n) (0xD80008 + ((n)*12)) /* offset */ | ||
553 | -#define SH7750_TCOR(n) SH7750_P4_REG32(SH7750_TCOR_REGOFS(n)) | ||
554 | -#define SH7750_TCOR_A7(n) SH7750_A7_REG32(SH7750_TCOR_REGOFS(n)) | ||
555 | -#define SH7750_TCOR0 SH7750_TCOR(0) | ||
556 | -#define SH7750_TCOR1 SH7750_TCOR(1) | ||
557 | -#define SH7750_TCOR2 SH7750_TCOR(2) | ||
558 | -#define SH7750_TCOR0_A7 SH7750_TCOR_A7(0) | ||
559 | -#define SH7750_TCOR1_A7 SH7750_TCOR_A7(1) | ||
560 | -#define SH7750_TCOR2_A7 SH7750_TCOR_A7(2) | ||
561 | - | ||
562 | -/* Timer Counter Register - TCNT0, TCNT1, TCNT2 */ | ||
563 | -#define SH7750_TCNT_REGOFS(n) (0xD8000C + ((n)*12)) /* offset */ | ||
564 | -#define SH7750_TCNT(n) SH7750_P4_REG32(SH7750_TCNT_REGOFS(n)) | ||
565 | -#define SH7750_TCNT_A7(n) SH7750_A7_REG32(SH7750_TCNT_REGOFS(n)) | ||
566 | -#define SH7750_TCNT0 SH7750_TCNT(0) | ||
567 | -#define SH7750_TCNT1 SH7750_TCNT(1) | ||
568 | -#define SH7750_TCNT2 SH7750_TCNT(2) | ||
569 | -#define SH7750_TCNT0_A7 SH7750_TCNT_A7(0) | ||
570 | -#define SH7750_TCNT1_A7 SH7750_TCNT_A7(1) | ||
571 | -#define SH7750_TCNT2_A7 SH7750_TCNT_A7(2) | ||
572 | - | ||
573 | -/* Timer Control Register (half) - TCR0, TCR1, TCR2 */ | ||
574 | -#define SH7750_TCR_REGOFS(n) (0xD80010 + ((n)*12)) /* offset */ | ||
575 | -#define SH7750_TCR(n) SH7750_P4_REG32(SH7750_TCR_REGOFS(n)) | ||
576 | -#define SH7750_TCR_A7(n) SH7750_A7_REG32(SH7750_TCR_REGOFS(n)) | ||
577 | -#define SH7750_TCR0 SH7750_TCR(0) | ||
578 | -#define SH7750_TCR1 SH7750_TCR(1) | ||
579 | -#define SH7750_TCR2 SH7750_TCR(2) | ||
580 | -#define SH7750_TCR0_A7 SH7750_TCR_A7(0) | ||
581 | -#define SH7750_TCR1_A7 SH7750_TCR_A7(1) | ||
582 | -#define SH7750_TCR2_A7 SH7750_TCR_A7(2) | ||
583 | - | ||
584 | -#define SH7750_TCR2_ICPF 0x200 /* Input Capture Interrupt Flag | ||
585 | - (1 - input capture has occured) */ | ||
586 | -#define SH7750_TCR_UNF 0x100 /* Underflow flag */ | ||
587 | -#define SH7750_TCR2_ICPE 0x0C0 /* Input Capture Control: */ | ||
588 | -#define SH7750_TCR2_ICPE_DIS 0x000 /* Input Capture function is not used */ | ||
589 | -#define SH7750_TCR2_ICPE_NOINT 0x080 /* Input Capture function is used, but | ||
590 | - input capture interrupt is not | ||
591 | - enabled */ | ||
592 | -#define SH7750_TCR2_ICPE_INT 0x0C0 /* Input Capture function is used, | ||
593 | - input capture interrupt enabled */ | ||
594 | -#define SH7750_TCR_UNIE 0x020 /* Underflow Interrupt Control | ||
595 | - (1 - underflow interrupt enabled) */ | ||
596 | -#define SH7750_TCR_CKEG 0x018 /* Clock Edge selection: */ | ||
597 | -#define SH7750_TCR_CKEG_RAISE 0x000 /* Count/capture on rising edge */ | ||
598 | -#define SH7750_TCR_CKEG_FALL 0x008 /* Count/capture on falling edge */ | ||
599 | -#define SH7750_TCR_CKEG_BOTH 0x018 /* Count/capture on both rising and | ||
600 | - falling edges */ | ||
601 | -#define SH7750_TCR_TPSC 0x007 /* Timer prescaler */ | ||
602 | -#define SH7750_TCR_TPSC_DIV4 0x000 /* Counts on peripheral clock/4 */ | ||
603 | -#define SH7750_TCR_TPSC_DIV16 0x001 /* Counts on peripheral clock/16 */ | ||
604 | -#define SH7750_TCR_TPSC_DIV64 0x002 /* Counts on peripheral clock/64 */ | ||
605 | -#define SH7750_TCR_TPSC_DIV256 0x003 /* Counts on peripheral clock/256 */ | ||
606 | -#define SH7750_TCR_TPSC_DIV1024 0x004 /* Counts on peripheral clock/1024 */ | ||
607 | -#define SH7750_TCR_TPSC_RTC 0x006 /* Counts on on-chip RTC output clk */ | ||
608 | -#define SH7750_TCR_TPSC_EXT 0x007 /* Counts on external clock */ | ||
609 | - | ||
610 | -/* Input Capture Register (read-only) - TCPR2 */ | ||
611 | -#define SH7750_TCPR2_REGOFS 0xD8002C /* offset */ | ||
612 | -#define SH7750_TCPR2 SH7750_P4_REG32(SH7750_TCPR2_REGOFS) | ||
613 | -#define SH7750_TCPR2_A7 SH7750_A7_REG32(SH7750_TCPR2_REGOFS) | ||
614 | - | ||
615 | /* | 527 | /* |
616 | * Bus State Controller - BSC | 528 | * Bus State Controller - BSC |
617 | */ | 529 | */ |
hw/sh_timer.c
0 โ 100644
1 | +/* | ||
2 | + * SuperH Timer modules. | ||
3 | + * | ||
4 | + * Copyright (c) 2007 Magnus Damm | ||
5 | + * Based on arm_timer.c by Paul Brook | ||
6 | + * Copyright (c) 2005-2006 CodeSourcery. | ||
7 | + * | ||
8 | + * This code is licenced under the GPL. | ||
9 | + */ | ||
10 | + | ||
11 | +#include "vl.h" | ||
12 | + | ||
13 | +//#define DEBUG_TIMER | ||
14 | + | ||
15 | +#define TIMER_TCR_TPSC (7 << 0) | ||
16 | +#define TIMER_TCR_CKEG (3 << 3) | ||
17 | +#define TIMER_TCR_UNIE (1 << 5) | ||
18 | +#define TIMER_TCR_ICPE (3 << 6) | ||
19 | +#define TIMER_TCR_UNF (1 << 8) | ||
20 | +#define TIMER_TCR_ICPF (1 << 9) | ||
21 | +#define TIMER_TCR_RESERVED (0x3f << 10) | ||
22 | + | ||
23 | +#define TIMER_FEAT_CAPT (1 << 0) | ||
24 | +#define TIMER_FEAT_EXTCLK (1 << 1) | ||
25 | + | ||
26 | +typedef struct { | ||
27 | + ptimer_state *timer; | ||
28 | + uint32_t tcnt; | ||
29 | + uint32_t tcor; | ||
30 | + uint32_t tcr; | ||
31 | + uint32_t tcpr; | ||
32 | + int freq; | ||
33 | + int int_level; | ||
34 | + int feat; | ||
35 | + int enabled; | ||
36 | + qemu_irq irq; | ||
37 | +} sh_timer_state; | ||
38 | + | ||
39 | +/* Check all active timers, and schedule the next timer interrupt. */ | ||
40 | + | ||
41 | +static void sh_timer_update(sh_timer_state *s) | ||
42 | +{ | ||
43 | +#if 0 /* not yet */ | ||
44 | + /* Update interrupts. */ | ||
45 | + if (s->int_level && (s->tcr & TIMER_TCR_UNIE)) { | ||
46 | + qemu_irq_raise(s->irq); | ||
47 | + } else { | ||
48 | + qemu_irq_lower(s->irq); | ||
49 | + } | ||
50 | +#endif | ||
51 | +} | ||
52 | + | ||
53 | +uint32_t sh_timer_read(void *opaque, target_phys_addr_t offset) | ||
54 | +{ | ||
55 | + sh_timer_state *s = (sh_timer_state *)opaque; | ||
56 | + | ||
57 | + switch (offset >> 2) { | ||
58 | + case 0: | ||
59 | + return s->tcor; | ||
60 | + case 1: | ||
61 | + return ptimer_get_count(s->timer); | ||
62 | + case 2: | ||
63 | + return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); | ||
64 | + case 3: | ||
65 | + if (s->feat & TIMER_FEAT_CAPT) | ||
66 | + return s->tcpr; | ||
67 | + default: | ||
68 | + cpu_abort (cpu_single_env, "sh_timer_read: Bad offset %x\n", | ||
69 | + (int)offset); | ||
70 | + return 0; | ||
71 | + } | ||
72 | +} | ||
73 | + | ||
74 | +static void sh_timer_write(void *opaque, target_phys_addr_t offset, | ||
75 | + uint32_t value) | ||
76 | +{ | ||
77 | + sh_timer_state *s = (sh_timer_state *)opaque; | ||
78 | + int freq; | ||
79 | + | ||
80 | + switch (offset >> 2) { | ||
81 | + case 0: | ||
82 | + s->tcor = value; | ||
83 | + ptimer_set_limit(s->timer, s->tcor, 0); | ||
84 | + break; | ||
85 | + case 1: | ||
86 | + s->tcnt = value; | ||
87 | + ptimer_set_count(s->timer, s->tcnt); | ||
88 | + break; | ||
89 | + case 2: | ||
90 | + if (s->enabled) { | ||
91 | + /* Pause the timer if it is running. This may cause some | ||
92 | + inaccuracy dure to rounding, but avoids a whole lot of other | ||
93 | + messyness. */ | ||
94 | + ptimer_stop(s->timer); | ||
95 | + } | ||
96 | + freq = s->freq; | ||
97 | + /* ??? Need to recalculate expiry time after changing divisor. */ | ||
98 | + switch (value & TIMER_TCR_TPSC) { | ||
99 | + case 0: freq >>= 2; break; | ||
100 | + case 1: freq >>= 4; break; | ||
101 | + case 2: freq >>= 6; break; | ||
102 | + case 3: freq >>= 8; break; | ||
103 | + case 4: freq >>= 10; break; | ||
104 | + case 6: | ||
105 | + case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; | ||
106 | + default: cpu_abort (cpu_single_env, | ||
107 | + "sh_timer_write: Reserved TPSC value\n"); break; | ||
108 | + } | ||
109 | + switch ((value & TIMER_TCR_CKEG) >> 3) { | ||
110 | + case 0: break; | ||
111 | + case 1: | ||
112 | + case 2: | ||
113 | + case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; | ||
114 | + default: cpu_abort (cpu_single_env, | ||
115 | + "sh_timer_write: Reserved CKEG value\n"); break; | ||
116 | + } | ||
117 | + switch ((value & TIMER_TCR_ICPE) >> 6) { | ||
118 | + case 0: break; | ||
119 | + case 2: | ||
120 | + case 3: if (s->feat & TIMER_FEAT_CAPT) break; | ||
121 | + default: cpu_abort (cpu_single_env, | ||
122 | + "sh_timer_write: Reserved ICPE value\n"); break; | ||
123 | + } | ||
124 | + if ((value & TIMER_TCR_UNF) == 0) | ||
125 | + s->int_level = 0; | ||
126 | + | ||
127 | + value &= ~TIMER_TCR_UNF; | ||
128 | + | ||
129 | + if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) | ||
130 | + cpu_abort (cpu_single_env, | ||
131 | + "sh_timer_write: Reserved ICPF value\n"); | ||
132 | + | ||
133 | + value &= ~TIMER_TCR_ICPF; /* capture not supported */ | ||
134 | + | ||
135 | + if (value & TIMER_TCR_RESERVED) | ||
136 | + cpu_abort (cpu_single_env, | ||
137 | + "sh_timer_write: Reserved TCR bits set\n"); | ||
138 | + s->tcr = value; | ||
139 | + ptimer_set_limit(s->timer, s->tcor, 0); | ||
140 | + ptimer_set_freq(s->timer, freq); | ||
141 | + if (s->enabled) { | ||
142 | + /* Restart the timer if still enabled. */ | ||
143 | + ptimer_run(s->timer, 0); | ||
144 | + } | ||
145 | + break; | ||
146 | + case 3: | ||
147 | + if (s->feat & TIMER_FEAT_CAPT) { | ||
148 | + s->tcpr = value; | ||
149 | + break; | ||
150 | + } | ||
151 | + default: | ||
152 | + cpu_abort (cpu_single_env, "sh_timer_write: Bad offset %x\n", | ||
153 | + (int)offset); | ||
154 | + } | ||
155 | + sh_timer_update(s); | ||
156 | +} | ||
157 | + | ||
158 | +static void sh_timer_start_stop(void *opaque, int enable) | ||
159 | +{ | ||
160 | + sh_timer_state *s = (sh_timer_state *)opaque; | ||
161 | + | ||
162 | +#ifdef DEBUG_TIMER | ||
163 | + printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); | ||
164 | +#endif | ||
165 | + | ||
166 | + if (s->enabled && !enable) { | ||
167 | + ptimer_stop(s->timer); | ||
168 | + } | ||
169 | + if (!s->enabled && enable) { | ||
170 | + ptimer_run(s->timer, 0); | ||
171 | + } | ||
172 | + s->enabled = !!enable; | ||
173 | + | ||
174 | +#ifdef DEBUG_TIMER | ||
175 | + printf("sh_timer_start_stop done %d\n", s->enabled); | ||
176 | +#endif | ||
177 | +} | ||
178 | + | ||
179 | +static void sh_timer_tick(void *opaque) | ||
180 | +{ | ||
181 | + sh_timer_state *s = (sh_timer_state *)opaque; | ||
182 | + s->int_level = s->enabled; | ||
183 | + sh_timer_update(s); | ||
184 | +} | ||
185 | + | ||
186 | +static void *sh_timer_init(uint32_t freq, int feat) | ||
187 | +{ | ||
188 | + sh_timer_state *s; | ||
189 | + QEMUBH *bh; | ||
190 | + | ||
191 | + s = (sh_timer_state *)qemu_mallocz(sizeof(sh_timer_state)); | ||
192 | + s->freq = freq; | ||
193 | + s->feat = feat; | ||
194 | + s->tcor = 0xffffffff; | ||
195 | + s->tcnt = 0xffffffff; | ||
196 | + s->tcpr = 0xdeadbeef; | ||
197 | + s->tcor = 0; | ||
198 | + s->enabled = 0; | ||
199 | + | ||
200 | + bh = qemu_bh_new(sh_timer_tick, s); | ||
201 | + s->timer = ptimer_init(bh); | ||
202 | + /* ??? Save/restore. */ | ||
203 | + return s; | ||
204 | +} | ||
205 | + | ||
206 | +typedef struct { | ||
207 | + void *timer[3]; | ||
208 | + int level[3]; | ||
209 | + uint32_t tocr; | ||
210 | + uint32_t tstr; | ||
211 | + target_phys_addr_t base; | ||
212 | + int feat; | ||
213 | +} tmu012_state; | ||
214 | + | ||
215 | +static uint32_t tmu012_read(void *opaque, target_phys_addr_t offset) | ||
216 | +{ | ||
217 | + tmu012_state *s = (tmu012_state *)opaque; | ||
218 | + | ||
219 | +#ifdef DEBUG_TIMER | ||
220 | + printf("tmu012_read 0x%lx\n", (unsigned long) offset); | ||
221 | +#endif | ||
222 | + offset -= s->base; | ||
223 | + | ||
224 | + if (offset >= 0x20) { | ||
225 | + if (!(s->feat & TMU012_FEAT_3CHAN)) | ||
226 | + cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n", | ||
227 | + (int)offset); | ||
228 | + return sh_timer_read(s->timer[2], offset - 0x20); | ||
229 | + } | ||
230 | + | ||
231 | + if (offset >= 0x14) | ||
232 | + return sh_timer_read(s->timer[1], offset - 0x14); | ||
233 | + | ||
234 | + if (offset >= 0x08) | ||
235 | + return sh_timer_read(s->timer[0], offset - 0x08); | ||
236 | + | ||
237 | + if (offset == 4) | ||
238 | + return s->tstr; | ||
239 | + | ||
240 | + if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) | ||
241 | + return s->tocr; | ||
242 | + | ||
243 | + cpu_abort (cpu_single_env, "tmu012_write: Bad offset %x\n", | ||
244 | + (int)offset); | ||
245 | + return 0; | ||
246 | +} | ||
247 | + | ||
248 | +static void tmu012_write(void *opaque, target_phys_addr_t offset, | ||
249 | + uint32_t value) | ||
250 | +{ | ||
251 | + tmu012_state *s = (tmu012_state *)opaque; | ||
252 | + | ||
253 | +#ifdef DEBUG_TIMER | ||
254 | + printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); | ||
255 | +#endif | ||
256 | + offset -= s->base; | ||
257 | + | ||
258 | + if (offset >= 0x20) { | ||
259 | + if (!(s->feat & TMU012_FEAT_3CHAN)) | ||
260 | + cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n", | ||
261 | + (int)offset); | ||
262 | + sh_timer_write(s->timer[2], offset - 0x20, value); | ||
263 | + return; | ||
264 | + } | ||
265 | + | ||
266 | + if (offset >= 0x14) { | ||
267 | + sh_timer_write(s->timer[1], offset - 0x14, value); | ||
268 | + return; | ||
269 | + } | ||
270 | + | ||
271 | + if (offset >= 0x08) { | ||
272 | + sh_timer_write(s->timer[0], offset - 0x08, value); | ||
273 | + return; | ||
274 | + } | ||
275 | + | ||
276 | + if (offset == 4) { | ||
277 | + sh_timer_start_stop(s->timer[0], value & (1 << 0)); | ||
278 | + sh_timer_start_stop(s->timer[1], value & (1 << 1)); | ||
279 | + if (s->feat & TMU012_FEAT_3CHAN) | ||
280 | + sh_timer_start_stop(s->timer[2], value & (1 << 2)); | ||
281 | + else | ||
282 | + if (value & (1 << 2)) | ||
283 | + cpu_abort (cpu_single_env, "tmu012_write: Bad channel\n"); | ||
284 | + | ||
285 | + s->tstr = value; | ||
286 | + return; | ||
287 | + } | ||
288 | + | ||
289 | + if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { | ||
290 | + s->tocr = value & (1 << 0); | ||
291 | + } | ||
292 | +} | ||
293 | + | ||
294 | +static CPUReadMemoryFunc *tmu012_readfn[] = { | ||
295 | + tmu012_read, | ||
296 | + tmu012_read, | ||
297 | + tmu012_read | ||
298 | +}; | ||
299 | + | ||
300 | +static CPUWriteMemoryFunc *tmu012_writefn[] = { | ||
301 | + tmu012_write, | ||
302 | + tmu012_write, | ||
303 | + tmu012_write | ||
304 | +}; | ||
305 | + | ||
306 | +void tmu012_init(uint32_t base, int feat, uint32_t freq) | ||
307 | +{ | ||
308 | + int iomemtype; | ||
309 | + tmu012_state *s; | ||
310 | + int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; | ||
311 | + | ||
312 | + s = (tmu012_state *)qemu_mallocz(sizeof(tmu012_state)); | ||
313 | + s->base = base; | ||
314 | + s->feat = feat; | ||
315 | + s->timer[0] = sh_timer_init(freq, timer_feat); | ||
316 | + s->timer[1] = sh_timer_init(freq, timer_feat); | ||
317 | + if (feat & TMU012_FEAT_3CHAN) | ||
318 | + s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT); | ||
319 | + iomemtype = cpu_register_io_memory(0, tmu012_readfn, | ||
320 | + tmu012_writefn, s); | ||
321 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | ||
322 | + /* ??? Save/restore. */ | ||
323 | +} |
vl.h
@@ -1517,6 +1517,12 @@ typedef struct { | @@ -1517,6 +1517,12 @@ typedef struct { | ||
1517 | 1517 | ||
1518 | int sh7750_register_io_device(struct SH7750State *s, | 1518 | int sh7750_register_io_device(struct SH7750State *s, |
1519 | sh7750_io_device * device); | 1519 | sh7750_io_device * device); |
1520 | +/* sh_timer.c */ | ||
1521 | +#define TMU012_FEAT_TOCR (1 << 0) | ||
1522 | +#define TMU012_FEAT_3CHAN (1 << 1) | ||
1523 | +#define TMU012_FEAT_EXTCLK (1 << 2) | ||
1524 | +void tmu012_init(uint32_t base, int feat, uint32_t freq); | ||
1525 | + | ||
1520 | /* tc58128.c */ | 1526 | /* tc58128.c */ |
1521 | int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); | 1527 | int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); |
1522 | 1528 |