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 | 476 | endif |
477 | 477 | ifeq ($(TARGET_BASE_ARCH), sh4) |
478 | 478 | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o |
479 | +VL_OBJS+= sh_timer.o ptimer.o | |
479 | 480 | endif |
480 | 481 | ifeq ($(TARGET_BASE_ARCH), m68k) |
481 | 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 | 64 | uint8_t scbrr2; |
65 | 65 | fifo serial2_receive_fifo; |
66 | 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 | 67 | /* IO ports */ |
75 | 68 | uint16_t gpioic; |
76 | 69 | uint32_t pctra; |
... | ... | @@ -88,83 +81,8 @@ typedef struct SH7750State { |
88 | 81 | sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ |
89 | 82 | /* Cache */ |
90 | 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 | 88 | First serial port |
... | ... | @@ -581,8 +499,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) |
581 | 499 | fprintf(stderr, |
582 | 500 | "Read access to refresh count register, incrementing\n"); |
583 | 501 | return s->rfcr++; |
584 | - case SH7750_TCR0_A7: | |
585 | - return s->tcr0; | |
586 | 502 | case SH7750_SCLSR2_A7: |
587 | 503 | /* Read and clear overflow bit */ |
588 | 504 | r = s->sclsr2; |
... | ... | @@ -649,10 +565,6 @@ static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr, |
649 | 565 | case SH7750_SCBRR2_A7: |
650 | 566 | s->scbrr2 = mem_value; |
651 | 567 | return; |
652 | - case SH7750_TSTR_A7: | |
653 | - s->tstr = mem_value; | |
654 | - timer_start_changed(s); | |
655 | - return; | |
656 | 568 | case SH7750_SCSCR1_A7: |
657 | 569 | s->scscr1 = mem_value; |
658 | 570 | scscr1_changed(s); |
... | ... | @@ -721,9 +633,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, |
721 | 633 | case SH7750_SCSMR2_A7: |
722 | 634 | s->scsmr2 = mem_value; |
723 | 635 | return; |
724 | - case SH7750_TCR0_A7: | |
725 | - s->tcr0 = mem_value; | |
726 | - return; | |
727 | 636 | case SH7750_GPIOIC_A7: |
728 | 637 | s->gpioic = mem_value; |
729 | 638 | if (mem_value != 0) { |
... | ... | @@ -768,9 +677,6 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, |
768 | 677 | s->portpullupb = portpullup(mem_value); |
769 | 678 | portb_changed(s, temp); |
770 | 679 | return; |
771 | - case SH7750_TCNT0_A7: | |
772 | - s->tcnt0 = mem_value & 0xf; | |
773 | - return; | |
774 | 680 | case SH7750_MMUCR_A7: |
775 | 681 | s->cpu->mmucr = mem_value; |
776 | 682 | return; |
... | ... | @@ -828,7 +734,11 @@ SH7750State *sh7750_init(CPUSH4State * cpu) |
828 | 734 | sh7750_mem_read, |
829 | 735 | sh7750_mem_write, s); |
830 | 736 | cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); |
831 | - init_timers(s); | |
832 | 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 | 743 | return s; |
834 | 744 | } | ... | ... |
hw/sh7750_regnames.c
... | ... | @@ -42,18 +42,6 @@ static regname_t regnames[] = { |
42 | 42 | REGNAME(SH7750_RMONAR_A7) |
43 | 43 | REGNAME(SH7750_RCR1_A7) |
44 | 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 | 45 | REGNAME(SH7750_BCR1_A7) |
58 | 46 | REGNAME(SH7750_BCR2_A7) |
59 | 47 | REGNAME(SH7750_WCR1_A7) | ... | ... |
hw/sh7750_regs.h
... | ... | @@ -524,94 +524,6 @@ |
524 | 524 | year counters are stopped |
525 | 525 | 1 - sec, min, hr, day-of-week, month, |
526 | 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 | 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 | 1517 | |
1518 | 1518 | int sh7750_register_io_device(struct SH7750State *s, |
1519 | 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 | 1526 | /* tc58128.c */ |
1521 | 1527 | int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); |
1522 | 1528 | ... | ... |