filename | src/sh4/timer.c |
changeset | 619:0800a0137472 |
prev | 561:533f6b478071 |
next | 736:a02d1475ccfd |
author | nkeynes |
date | Mon May 12 10:00:13 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Cleanup most of the -Wall warnings (getting a bit sloppy...) Convert FP code to use fixed banks rather than indirect pointer (3-4% faster this way now) |
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 uint32_t sh4_input_freq = SH4_BASE_RATE;
33 uint32_t sh4_cpu_multiplier = 2000; /* = 0.5 * frequency */
35 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
36 uint32_t sh4_bus_freq = SH4_BASE_RATE;
37 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
39 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
40 uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
41 uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
43 int32_t mmio_region_CPG_read( uint32_t reg )
44 {
45 return MMIO_READ( CPG, reg );
46 }
48 /* CPU + bus dividers (note officially only the first 6 values are valid) */
49 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
50 /* Peripheral clock dividers (only first 5 are officially valid) */
51 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
53 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
54 {
55 uint32_t div;
56 switch( reg ) {
57 case FRQCR: /* Frequency control */
58 div = ifc_divider[(val >> 6) & 0x07];
59 sh4_cpu_freq = sh4_input_freq / div;
60 sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
61 div = ifc_divider[(val >> 3) & 0x07];
62 sh4_bus_freq = sh4_input_freq / div;
63 sh4_bus_period = 1000 * div / sh4_input_freq;
64 div = pfc_divider[val & 0x07];
65 sh4_peripheral_freq = sh4_input_freq / div;
66 sh4_peripheral_period = 1000 * div / sh4_input_freq;
68 /* Update everything that depends on the peripheral frequency */
69 SCIF_update_line_speed();
70 break;
71 case WTCSR: /* Watchdog timer */
72 break;
73 }
75 MMIO_WRITE( CPG, reg, val );
76 }
78 /**
79 * We don't really know what the default reset value is as it's determined
80 * by the mode select pins. This is the standard value that the BIOS sets,
81 * however, so it works for now.
82 */
83 void CPG_reset( )
84 {
85 mmio_region_CPG_write( FRQCR, 0x0E0A );
86 }
89 /********************************** RTC *************************************/
91 uint32_t rtc_output_period;
93 int32_t mmio_region_RTC_read( uint32_t reg )
94 {
95 return MMIO_READ( RTC, reg );
96 }
98 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
99 {
100 MMIO_WRITE( RTC, reg, val );
101 }
103 /********************************** TMU *************************************/
105 #define TMU_IS_RUNNING(timer) (MMIO_READ(TMU,TSTR) & (1<<timer))
107 uint32_t TMU_count( int timer, uint32_t nanosecs );
109 void TMU_event_callback( int eventid )
110 {
111 TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle );
112 }
114 void TMU_init(void)
115 {
116 register_event_callback( EVENT_TMU0, TMU_event_callback );
117 register_event_callback( EVENT_TMU1, TMU_event_callback );
118 register_event_callback( EVENT_TMU2, TMU_event_callback );
119 }
121 #define TCR_ICPF 0x0200
122 #define TCR_UNF 0x0100
123 #define TCR_UNIE 0x0020
125 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
127 struct TMU_timer {
128 uint32_t timer_period;
129 uint32_t timer_remainder; /* left-over cycles from last count */
130 uint32_t timer_run; /* cycles already run from this slice */
131 };
133 static struct TMU_timer TMU_timers[3];
135 int32_t mmio_region_TMU_read( uint32_t reg )
136 {
137 switch( reg ) {
138 case TCNT0:
139 TMU_count( 0, sh4r.slice_cycle );
140 break;
141 case TCNT1:
142 TMU_count( 1, sh4r.slice_cycle );
143 break;
144 case TCNT2:
145 TMU_count( 2, sh4r.slice_cycle );
146 break;
147 }
148 return MMIO_READ( TMU, reg );
149 }
151 void TMU_set_timer_control( int timer, int tcr )
152 {
153 uint32_t period = 1;
154 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
156 if( (oldtcr & TCR_UNF) == 0 ) {
157 tcr = tcr & (~TCR_UNF);
158 } else {
159 if( ((oldtcr & TCR_UNIE) == 0) &&
160 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
161 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
162 } else if( (oldtcr & TCR_UNIE) != 0 &&
163 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
164 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
165 }
166 }
168 switch( tcr & 0x07 ) {
169 case 0:
170 period = sh4_peripheral_period << 2 ;
171 break;
172 case 1:
173 period = sh4_peripheral_period << 4;
174 break;
175 case 2:
176 period = sh4_peripheral_period << 6;
177 break;
178 case 3:
179 period = sh4_peripheral_period << 8;
180 break;
181 case 4:
182 period = sh4_peripheral_period << 10;
183 break;
184 case 5:
185 /* Illegal value. */
186 ERROR( "TMU %d period set to illegal value (5)", timer );
187 period = sh4_peripheral_period << 12; /* for something to do */
188 break;
189 case 6:
190 period = rtc_output_period;
191 break;
192 case 7:
193 /* External clock... Hrm? */
194 period = sh4_peripheral_period; /* I dunno... */
195 break;
196 }
197 TMU_timers[timer].timer_period = period;
199 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
200 }
202 void TMU_schedule_timer( int timer )
203 {
204 uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) *
205 (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
206 event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000),
207 (uint32_t)(duration % 1000000000) );
208 }
210 void TMU_start( int timer )
211 {
212 TMU_timers[timer].timer_run = sh4r.slice_cycle;
213 TMU_timers[timer].timer_remainder = 0;
214 TMU_schedule_timer( timer );
215 }
217 /**
218 * Stop the given timer. Run it up to the current time and leave it there.
219 */
220 void TMU_stop( int timer )
221 {
222 TMU_count( timer, sh4r.slice_cycle );
223 event_cancel( EVENT_TMU0+timer );
224 }
226 /**
227 * Count the specified timer for a given number of nanoseconds.
228 */
229 uint32_t TMU_count( int timer, uint32_t nanosecs )
230 {
231 uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
232 TMU_timers[timer].timer_run;
233 TMU_timers[timer].timer_remainder =
234 run_ns % TMU_timers[timer].timer_period;
235 TMU_timers[timer].timer_run = nanosecs;
236 uint32_t count = run_ns / TMU_timers[timer].timer_period;
237 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
238 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
239 if( count > value ) {
240 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
241 tcr |= TCR_UNF;
242 count -= value;
243 value = reset - (count % reset) + 1;
244 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
245 if( tcr & TCR_UNIE )
246 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
247 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
248 TMU_schedule_timer(timer);
249 } else {
250 value -= count;
251 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
252 }
253 return value;
254 }
256 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
257 {
258 uint32_t oldval;
259 int i;
260 switch( reg ) {
261 case TSTR:
262 oldval = MMIO_READ( TMU, TSTR );
263 for( i=0; i<3; i++ ) {
264 uint32_t tmp = 1<<i;
265 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
266 TMU_stop(i);
267 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
268 TMU_start(i);
269 }
270 break;
271 case TCR0:
272 TMU_set_timer_control( 0, val );
273 return;
274 case TCR1:
275 TMU_set_timer_control( 1, val );
276 return;
277 case TCR2:
278 TMU_set_timer_control( 2, val );
279 return;
280 case TCNT0:
281 MMIO_WRITE( TMU, reg, val );
282 if( TMU_IS_RUNNING(0) ) { // reschedule
283 TMU_timers[0].timer_run = sh4r.slice_cycle;
284 TMU_schedule_timer( 0 );
285 }
286 return;
287 case TCNT1:
288 MMIO_WRITE( TMU, reg, val );
289 if( TMU_IS_RUNNING(1) ) { // reschedule
290 TMU_timers[1].timer_run = sh4r.slice_cycle;
291 TMU_schedule_timer( 1 );
292 }
293 return;
294 case TCNT2:
295 MMIO_WRITE( TMU, reg, val );
296 if( TMU_IS_RUNNING(2) ) { // reschedule
297 TMU_timers[2].timer_run = sh4r.slice_cycle;
298 TMU_schedule_timer( 2 );
299 }
300 return;
301 }
302 MMIO_WRITE( TMU, reg, val );
303 }
305 void TMU_count_all( uint32_t nanosecs )
306 {
307 int tcr = MMIO_READ( TMU, TSTR );
308 if( tcr & 0x01 ) {
309 TMU_count( 0, nanosecs );
310 }
311 if( tcr & 0x02 ) {
312 TMU_count( 1, nanosecs );
313 }
314 if( tcr & 0x04 ) {
315 TMU_count( 2, nanosecs );
316 }
317 }
319 void TMU_run_slice( uint32_t nanosecs )
320 {
321 TMU_count_all( nanosecs );
322 TMU_timers[0].timer_run = 0;
323 TMU_timers[1].timer_run = 0;
324 TMU_timers[2].timer_run = 0;
325 }
327 void TMU_update_clocks()
328 {
329 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
330 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
331 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
332 }
334 void TMU_reset( )
335 {
336 TMU_timers[0].timer_remainder = 0;
337 TMU_timers[0].timer_run = 0;
338 TMU_timers[1].timer_remainder = 0;
339 TMU_timers[1].timer_run = 0;
340 TMU_timers[2].timer_remainder = 0;
341 TMU_timers[2].timer_run = 0;
342 TMU_update_clocks();
343 }
345 void TMU_save_state( FILE *f ) {
346 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
347 }
349 int TMU_load_state( FILE *f )
350 {
351 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
352 return 0;
353 }
.