filename | src/sh4/timer.c |
changeset | 859:b941c703ccd6 |
prev | 736:a02d1475ccfd |
next | 929:fd8cb0c82f5f |
author | nkeynes |
date | Sat Oct 25 12:36:42 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Add --enable-optimized flag (on by default). When optimized, add -fomit-frame-pointer -fexceptions on non-Mac x86 (Unwinding doesn't seem to work correctly on Mac) |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
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 <assert.h>
21 #include "lxdream.h"
22 #include "mem.h"
23 #include "clock.h"
24 #include "eventq.h"
25 #include "sh4/sh4core.h"
26 #include "sh4/sh4mmio.h"
27 #include "sh4/intc.h"
29 /********************************* CPG *************************************/
30 /* This is the base clock from which all other clocks are derived.
31 * Note: The real clock runs at 33Mhz, which is multiplied by the PLL to
32 * run the instruction clock at 200Mhz. For sake of simplicity/precision,
33 * we instead use 200Mhz as the base rate and divide everything down instead.
34 **/
35 uint32_t sh4_input_freq = SH4_BASE_RATE;
37 uint32_t sh4_cpu_multiplier = 2000; /* = 0.5 * frequency */
39 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
40 uint32_t sh4_bus_freq = SH4_BASE_RATE / 2;
41 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 4;
43 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
44 uint32_t sh4_bus_period = 2* 1000 / SH4_BASE_RATE;
45 uint32_t sh4_peripheral_period = 4 * 2000 / SH4_BASE_RATE;
47 int32_t mmio_region_CPG_read( uint32_t reg )
48 {
49 return MMIO_READ( CPG, reg );
50 }
52 /* CPU + bus dividers (note officially only the first 6 values are valid) */
53 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
54 /* Peripheral clock dividers (only first 5 are officially valid) */
55 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
57 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
58 {
59 uint32_t div;
60 uint32_t primary_clock = sh4_input_freq;
62 switch( reg ) {
63 case FRQCR: /* Frequency control */
64 if( (val & FRQCR_PLL1EN) == 0 )
65 primary_clock /= 6;
66 div = ifc_divider[(val >> 6) & 0x07];
67 sh4_cpu_freq = primary_clock / div;
68 sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
69 div = ifc_divider[(val >> 3) & 0x07];
70 sh4_bus_freq = primary_clock / div;
71 sh4_bus_period = 1000 * div / sh4_input_freq;
72 div = pfc_divider[val & 0x07];
73 sh4_peripheral_freq = primary_clock / div;
74 sh4_peripheral_period = 1000 * div / sh4_input_freq;
76 /* Update everything that depends on the peripheral frequency */
77 SCIF_update_line_speed();
78 break;
79 case WTCSR: /* Watchdog timer */
80 break;
81 }
83 MMIO_WRITE( CPG, reg, val );
84 }
86 /**
87 * We don't really know what the default reset value is as it's determined
88 * by the mode select pins. This is the standard value that the BIOS sets,
89 * however, so it works for now.
90 */
91 void CPG_reset( )
92 {
93 mmio_region_CPG_write( FRQCR, 0x0E0A );
94 }
97 /********************************** RTC *************************************/
99 uint32_t rtc_output_period;
101 int32_t mmio_region_RTC_read( uint32_t reg )
102 {
103 return MMIO_READ( RTC, reg );
104 }
106 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
107 {
108 MMIO_WRITE( RTC, reg, val );
109 }
111 /********************************** TMU *************************************/
113 #define TMU_IS_RUNNING(timer) (MMIO_READ(TMU,TSTR) & (1<<timer))
115 uint32_t TMU_count( int timer, uint32_t nanosecs );
117 void TMU_event_callback( int eventid )
118 {
119 TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle );
120 }
122 void TMU_init(void)
123 {
124 register_event_callback( EVENT_TMU0, TMU_event_callback );
125 register_event_callback( EVENT_TMU1, TMU_event_callback );
126 register_event_callback( EVENT_TMU2, TMU_event_callback );
127 }
129 #define TCR_ICPF 0x0200
130 #define TCR_UNF 0x0100
131 #define TCR_UNIE 0x0020
133 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
135 struct TMU_timer {
136 uint32_t timer_period;
137 uint32_t timer_remainder; /* left-over cycles from last count */
138 uint32_t timer_run; /* cycles already run from this slice */
139 };
141 static struct TMU_timer TMU_timers[3];
143 int32_t mmio_region_TMU_read( uint32_t reg )
144 {
145 switch( reg ) {
146 case TCNT0:
147 TMU_count( 0, sh4r.slice_cycle );
148 break;
149 case TCNT1:
150 TMU_count( 1, sh4r.slice_cycle );
151 break;
152 case TCNT2:
153 TMU_count( 2, sh4r.slice_cycle );
154 break;
155 }
156 return MMIO_READ( TMU, reg );
157 }
159 void TMU_set_timer_control( int timer, int tcr )
160 {
161 uint32_t period = 1;
162 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
164 if( (oldtcr & TCR_UNF) == 0 ) {
165 tcr = tcr & (~TCR_UNF);
166 } else {
167 if( ((oldtcr & TCR_UNIE) == 0) &&
168 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
169 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
170 } else if( (oldtcr & TCR_UNIE) != 0 &&
171 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
172 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
173 }
174 }
176 switch( tcr & 0x07 ) {
177 case 0:
178 period = sh4_peripheral_period << 2 ;
179 break;
180 case 1:
181 period = sh4_peripheral_period << 4;
182 break;
183 case 2:
184 period = sh4_peripheral_period << 6;
185 break;
186 case 3:
187 period = sh4_peripheral_period << 8;
188 break;
189 case 4:
190 period = sh4_peripheral_period << 10;
191 break;
192 case 5:
193 /* Illegal value. */
194 ERROR( "TMU %d period set to illegal value (5)", timer );
195 period = sh4_peripheral_period << 12; /* for something to do */
196 break;
197 case 6:
198 period = rtc_output_period;
199 break;
200 case 7:
201 /* External clock... Hrm? */
202 period = sh4_peripheral_period; /* I dunno... */
203 break;
204 }
205 TMU_timers[timer].timer_period = period;
207 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
208 }
210 void TMU_schedule_timer( int timer )
211 {
212 uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) *
213 (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
214 event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000),
215 (uint32_t)(duration % 1000000000) );
216 }
218 void TMU_start( int timer )
219 {
220 TMU_timers[timer].timer_run = sh4r.slice_cycle;
221 TMU_timers[timer].timer_remainder = 0;
222 TMU_schedule_timer( timer );
223 }
225 /**
226 * Stop the given timer. Run it up to the current time and leave it there.
227 */
228 void TMU_stop( int timer )
229 {
230 TMU_count( timer, sh4r.slice_cycle );
231 event_cancel( EVENT_TMU0+timer );
232 }
234 /**
235 * Count the specified timer for a given number of nanoseconds.
236 */
237 uint32_t TMU_count( int timer, uint32_t nanosecs )
238 {
239 uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
240 TMU_timers[timer].timer_run;
241 TMU_timers[timer].timer_remainder =
242 run_ns % TMU_timers[timer].timer_period;
243 TMU_timers[timer].timer_run = nanosecs;
244 uint32_t count = run_ns / TMU_timers[timer].timer_period;
245 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
246 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
247 if( count > value ) {
248 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
249 tcr |= TCR_UNF;
250 count -= value;
251 value = reset - (count % reset) + 1;
252 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
253 if( tcr & TCR_UNIE )
254 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
255 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
256 TMU_schedule_timer(timer);
257 } else {
258 value -= count;
259 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
260 }
261 return value;
262 }
264 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
265 {
266 uint32_t oldval;
267 int i;
268 switch( reg ) {
269 case TSTR:
270 oldval = MMIO_READ( TMU, TSTR );
271 for( i=0; i<3; i++ ) {
272 uint32_t tmp = 1<<i;
273 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
274 TMU_stop(i);
275 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
276 TMU_start(i);
277 }
278 break;
279 case TCR0:
280 TMU_set_timer_control( 0, val );
281 return;
282 case TCR1:
283 TMU_set_timer_control( 1, val );
284 return;
285 case TCR2:
286 TMU_set_timer_control( 2, val );
287 return;
288 case TCNT0:
289 MMIO_WRITE( TMU, reg, val );
290 if( TMU_IS_RUNNING(0) ) { // reschedule
291 TMU_timers[0].timer_run = sh4r.slice_cycle;
292 TMU_schedule_timer( 0 );
293 }
294 return;
295 case TCNT1:
296 MMIO_WRITE( TMU, reg, val );
297 if( TMU_IS_RUNNING(1) ) { // reschedule
298 TMU_timers[1].timer_run = sh4r.slice_cycle;
299 TMU_schedule_timer( 1 );
300 }
301 return;
302 case TCNT2:
303 MMIO_WRITE( TMU, reg, val );
304 if( TMU_IS_RUNNING(2) ) { // reschedule
305 TMU_timers[2].timer_run = sh4r.slice_cycle;
306 TMU_schedule_timer( 2 );
307 }
308 return;
309 }
310 MMIO_WRITE( TMU, reg, val );
311 }
313 void TMU_count_all( uint32_t nanosecs )
314 {
315 int tcr = MMIO_READ( TMU, TSTR );
316 if( tcr & 0x01 ) {
317 TMU_count( 0, nanosecs );
318 }
319 if( tcr & 0x02 ) {
320 TMU_count( 1, nanosecs );
321 }
322 if( tcr & 0x04 ) {
323 TMU_count( 2, nanosecs );
324 }
325 }
327 void TMU_run_slice( uint32_t nanosecs )
328 {
329 TMU_count_all( nanosecs );
330 TMU_timers[0].timer_run = 0;
331 TMU_timers[1].timer_run = 0;
332 TMU_timers[2].timer_run = 0;
333 }
335 void TMU_update_clocks()
336 {
337 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
338 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
339 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
340 }
342 void TMU_reset( )
343 {
344 TMU_timers[0].timer_remainder = 0;
345 TMU_timers[0].timer_run = 0;
346 TMU_timers[1].timer_remainder = 0;
347 TMU_timers[1].timer_run = 0;
348 TMU_timers[2].timer_remainder = 0;
349 TMU_timers[2].timer_run = 0;
350 TMU_update_clocks();
351 }
353 void TMU_save_state( FILE *f ) {
354 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
355 }
357 int TMU_load_state( FILE *f )
358 {
359 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
360 return 0;
361 }
.