filename | src/sh4/timer.c |
changeset | 326:0c107e0d0fe0 |
prev | 264:e3b8a3ab32b8 |
next | 414:fd8e96bc513e |
author | nkeynes |
date | Fri Sep 28 07:27:20 2007 +0000 (16 years ago) |
permissions | -rw-r--r-- |
last change | Change block signature to return pointer to next block (if known) Rewrite block-exit code |
view | annotate | diff | log | raw |
1 /**
2 * $Id: timer.c,v 1.7 2007-01-25 10:18:42 nkeynes Exp $
3 *
4 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
5 * keep things simple (they intertwine a bit).
6 *
7 * Copyright (c) 2005 Nathan Keynes.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
20 #include "dream.h"
21 #include "mem.h"
22 #include "clock.h"
23 #include "sh4core.h"
24 #include "sh4mmio.h"
25 #include "intc.h"
27 /********************************* CPG *************************************/
28 /* This is the base clock from which all other clocks are derived */
29 uint32_t sh4_input_freq = SH4_BASE_RATE;
31 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
32 uint32_t sh4_bus_freq = SH4_BASE_RATE;
33 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
35 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
36 uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
37 uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
39 int32_t mmio_region_CPG_read( uint32_t reg )
40 {
41 return MMIO_READ( CPG, reg );
42 }
44 /* CPU + bus dividers (note officially only the first 6 values are valid) */
45 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
46 /* Peripheral clock dividers (only first 5 are officially valid) */
47 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
49 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
50 {
51 uint32_t div;
52 switch( reg ) {
53 case FRQCR: /* Frequency control */
54 div = ifc_divider[(val >> 6) & 0x07];
55 sh4_cpu_freq = sh4_input_freq / div;
56 sh4_cpu_period = 2000 * div / sh4_input_freq;
57 div = ifc_divider[(val >> 3) & 0x07];
58 sh4_bus_freq = sh4_input_freq / div;
59 sh4_bus_period = 1000 * div / sh4_input_freq;
60 div = pfc_divider[val & 0x07];
61 sh4_peripheral_freq = sh4_input_freq / div;
62 sh4_peripheral_period = 1000 * div / sh4_input_freq;
64 /* Update everything that depends on the peripheral frequency */
65 SCIF_update_line_speed();
66 break;
67 case WTCSR: /* Watchdog timer */
68 break;
69 }
71 MMIO_WRITE( CPG, reg, val );
72 }
74 /**
75 * We don't really know what the default reset value is as it's determined
76 * by the mode select pins. This is the standard value that the BIOS sets,
77 * however, so it works for now.
78 */
79 void CPG_reset( )
80 {
81 mmio_region_CPG_write( FRQCR, 0x0E0A );
82 }
85 /********************************** RTC *************************************/
87 uint32_t rtc_output_period;
89 int32_t mmio_region_RTC_read( uint32_t reg )
90 {
91 return MMIO_READ( RTC, reg );
92 }
94 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
95 {
96 MMIO_WRITE( RTC, reg, val );
97 }
99 /********************************** TMU *************************************/
101 uint32_t TMU_count( int timer, uint32_t nanosecs );
104 #define TCR_ICPF 0x0200
105 #define TCR_UNF 0x0100
106 #define TCR_UNIE 0x0020
108 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
110 struct TMU_timer {
111 uint32_t timer_period;
112 uint32_t timer_remainder; /* left-over cycles from last count */
113 uint32_t timer_run; /* cycles already run from this slice */
114 };
116 struct TMU_timer TMU_timers[3];
118 int32_t mmio_region_TMU_read( uint32_t reg )
119 {
120 switch( reg ) {
121 case TCNT0:
122 TMU_count( 0, sh4r.slice_cycle );
123 TMU_timers[0].timer_run = sh4r.slice_cycle;
124 break;
125 case TCNT1:
126 TMU_count( 1, sh4r.slice_cycle );
127 TMU_timers[1].timer_run = sh4r.slice_cycle;
128 break;
129 case TCNT2:
130 TMU_count( 2, sh4r.slice_cycle );
131 TMU_timers[2].timer_run = sh4r.slice_cycle;
132 break;
133 }
134 return MMIO_READ( TMU, reg );
135 }
137 void TMU_set_timer_control( int timer, int tcr )
138 {
139 uint32_t period = 1;
140 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
142 if( (oldtcr & TCR_UNF) == 0 ) {
143 tcr = tcr & (~TCR_UNF);
144 } else {
145 if( (oldtcr & TCR_UNIE == 0) &&
146 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
147 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
148 } else if( (oldtcr & TCR_UNIE) != 0 &&
149 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
150 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
151 }
152 }
154 switch( tcr & 0x07 ) {
155 case 0:
156 period = sh4_peripheral_period << 2 ;
157 break;
158 case 1:
159 period = sh4_peripheral_period << 4;
160 break;
161 case 2:
162 period = sh4_peripheral_period << 6;
163 break;
164 case 3:
165 period = sh4_peripheral_period << 8;
166 break;
167 case 4:
168 period = sh4_peripheral_period << 10;
169 break;
170 case 5:
171 /* Illegal value. */
172 ERROR( "TMU %d period set to illegal value (5)", timer );
173 period = sh4_peripheral_period << 12; /* for something to do */
174 break;
175 case 6:
176 period = rtc_output_period;
177 break;
178 case 7:
179 /* External clock... Hrm? */
180 period = sh4_peripheral_period; /* I dunno... */
181 break;
182 }
183 TMU_timers[timer].timer_period = period;
185 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
186 }
188 void TMU_start( int timer )
189 {
190 TMU_timers[timer].timer_run = sh4r.slice_cycle;
191 TMU_timers[timer].timer_remainder = 0;
192 }
194 /**
195 * Stop the given timer. Run it up to the current time and leave it there.
196 */
197 void TMU_stop( int timer )
198 {
199 TMU_count( timer, sh4r.slice_cycle );
200 TMU_timers[timer].timer_run = sh4r.slice_cycle;
201 }
203 /**
204 * Count the specified timer for a given number of nanoseconds.
205 */
206 uint32_t TMU_count( int timer, uint32_t nanosecs )
207 {
208 nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
209 TMU_timers[timer].timer_run;
210 TMU_timers[timer].timer_remainder =
211 nanosecs % TMU_timers[timer].timer_period;
212 uint32_t count = nanosecs / TMU_timers[timer].timer_period;
213 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
214 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
215 if( count > value ) {
216 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
217 tcr |= TCR_UNF;
218 count -= value;
219 value = reset - (count % reset);
220 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
221 if( tcr & TCR_UNIE )
222 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
223 } else {
224 value -= count;
225 }
226 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
227 return value;
228 }
230 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
231 {
232 uint32_t oldval;
233 int i;
234 switch( reg ) {
235 case TSTR:
236 oldval = MMIO_READ( TMU, TSTR );
237 for( i=0; i<3; i++ ) {
238 uint32_t tmp = 1<<i;
239 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
240 TMU_stop(i);
241 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
242 TMU_start(i);
243 }
244 break;
245 case TCR0:
246 TMU_set_timer_control( 0, val );
247 return;
248 case TCR1:
249 TMU_set_timer_control( 1, val );
250 return;
251 case TCR2:
252 TMU_set_timer_control( 2, val );
253 return;
254 }
255 MMIO_WRITE( TMU, reg, val );
256 }
258 void TMU_run_slice( uint32_t nanosecs )
259 {
260 int tcr = MMIO_READ( TMU, TSTR );
261 if( tcr & 0x01 ) {
262 TMU_count( 0, nanosecs );
263 TMU_timers[0].timer_run = 0;
264 }
265 if( tcr & 0x02 ) {
266 TMU_count( 1, nanosecs );
267 TMU_timers[1].timer_run = 0;
268 }
269 if( tcr & 0x04 ) {
270 TMU_count( 2, nanosecs );
271 TMU_timers[2].timer_run = 0;
272 }
273 }
275 void TMU_update_clocks()
276 {
277 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
278 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
279 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
280 }
282 void TMU_reset( )
283 {
284 TMU_timers[0].timer_remainder = 0;
285 TMU_timers[0].timer_run = 0;
286 TMU_timers[1].timer_remainder = 0;
287 TMU_timers[1].timer_run = 0;
288 TMU_timers[2].timer_remainder = 0;
289 TMU_timers[2].timer_run = 0;
290 TMU_update_clocks();
291 }
293 void TMU_save_state( FILE *f ) {
294 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
295 }
297 int TMU_load_state( FILE *f )
298 {
299 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
300 return 0;
301 }
.