filename | src/sh4/timer.c |
changeset | 929:fd8cb0c82f5f |
prev | 859:b941c703ccd6 |
next | 975:007bf7eb944f |
author | nkeynes |
date | Sat Dec 27 02:59:35 2008 +0000 (15 years ago) |
branch | lxdream-mem |
permissions | -rw-r--r-- |
last change | Replace fpscr_mask/fpscr flags in xlat_cache_block with a single xlat_sh4_mode, which tracks the field of the same name in sh4r - actually a little faster this way. Now depends on SR.MD, FPSCR.PR and FPSCR.SZ (although it doesn't benefit from the SR flag yet). Also fixed the failure to check the flags in the common case (code address returned by previous block) which took away the performance benefits, but oh well. |
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 MMIO_REGION_READ_FN( CPG, reg )
48 {
49 return MMIO_READ( CPG, reg&0xFFF );
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 MMIO_REGION_WRITE_FN( CPG, reg, val )
58 {
59 uint32_t div;
60 uint32_t primary_clock = sh4_input_freq;
61 reg &= 0xFFF;
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 MMIO_REGION_READ_FN( RTC, reg )
102 {
103 return MMIO_READ( RTC, reg &0xFFF );
104 }
106 MMIO_REGION_WRITE_FN( RTC, reg, val )
107 {
108 MMIO_WRITE( RTC, reg &0xFFF, 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 void TMU_set_timer_control( int timer, int tcr )
144 {
145 uint32_t period = 1;
146 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
148 if( (oldtcr & TCR_UNF) == 0 ) {
149 tcr = tcr & (~TCR_UNF);
150 } else {
151 if( ((oldtcr & TCR_UNIE) == 0) &&
152 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
153 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
154 } else if( (oldtcr & TCR_UNIE) != 0 &&
155 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
156 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
157 }
158 }
160 switch( tcr & 0x07 ) {
161 case 0:
162 period = sh4_peripheral_period << 2 ;
163 break;
164 case 1:
165 period = sh4_peripheral_period << 4;
166 break;
167 case 2:
168 period = sh4_peripheral_period << 6;
169 break;
170 case 3:
171 period = sh4_peripheral_period << 8;
172 break;
173 case 4:
174 period = sh4_peripheral_period << 10;
175 break;
176 case 5:
177 /* Illegal value. */
178 ERROR( "TMU %d period set to illegal value (5)", timer );
179 period = sh4_peripheral_period << 12; /* for something to do */
180 break;
181 case 6:
182 period = rtc_output_period;
183 break;
184 case 7:
185 /* External clock... Hrm? */
186 period = sh4_peripheral_period; /* I dunno... */
187 break;
188 }
189 TMU_timers[timer].timer_period = period;
191 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
192 }
194 void TMU_schedule_timer( int timer )
195 {
196 uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) *
197 (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
198 event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000),
199 (uint32_t)(duration % 1000000000) );
200 }
202 void TMU_start( int timer )
203 {
204 TMU_timers[timer].timer_run = sh4r.slice_cycle;
205 TMU_timers[timer].timer_remainder = 0;
206 TMU_schedule_timer( timer );
207 }
209 /**
210 * Stop the given timer. Run it up to the current time and leave it there.
211 */
212 void TMU_stop( int timer )
213 {
214 TMU_count( timer, sh4r.slice_cycle );
215 event_cancel( EVENT_TMU0+timer );
216 }
218 /**
219 * Count the specified timer for a given number of nanoseconds.
220 */
221 uint32_t TMU_count( int timer, uint32_t nanosecs )
222 {
223 uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
224 TMU_timers[timer].timer_run;
225 TMU_timers[timer].timer_remainder =
226 run_ns % TMU_timers[timer].timer_period;
227 TMU_timers[timer].timer_run = nanosecs;
228 uint32_t count = run_ns / TMU_timers[timer].timer_period;
229 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
230 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
231 if( count > value ) {
232 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
233 tcr |= TCR_UNF;
234 count -= value;
235 value = reset - (count % reset) + 1;
236 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
237 if( tcr & TCR_UNIE )
238 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
239 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
240 TMU_schedule_timer(timer);
241 } else {
242 value -= count;
243 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
244 }
245 return value;
246 }
248 MMIO_REGION_READ_FN( TMU, reg )
249 {
250 reg &= 0xFFF;
251 switch( reg ) {
252 case TCNT0:
253 TMU_count( 0, sh4r.slice_cycle );
254 break;
255 case TCNT1:
256 TMU_count( 1, sh4r.slice_cycle );
257 break;
258 case TCNT2:
259 TMU_count( 2, sh4r.slice_cycle );
260 break;
261 }
262 return MMIO_READ( TMU, reg );
263 }
265 MMIO_REGION_WRITE_FN( TMU, reg, val )
266 {
267 uint32_t oldval;
268 int i;
269 reg &= 0xFFF;
270 switch( reg ) {
271 case TSTR:
272 oldval = MMIO_READ( TMU, TSTR );
273 for( i=0; i<3; i++ ) {
274 uint32_t tmp = 1<<i;
275 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
276 TMU_stop(i);
277 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
278 TMU_start(i);
279 }
280 break;
281 case TCR0:
282 TMU_set_timer_control( 0, val );
283 return;
284 case TCR1:
285 TMU_set_timer_control( 1, val );
286 return;
287 case TCR2:
288 TMU_set_timer_control( 2, val );
289 return;
290 case TCNT0:
291 MMIO_WRITE( TMU, reg, val );
292 if( TMU_IS_RUNNING(0) ) { // reschedule
293 TMU_timers[0].timer_run = sh4r.slice_cycle;
294 TMU_schedule_timer( 0 );
295 }
296 return;
297 case TCNT1:
298 MMIO_WRITE( TMU, reg, val );
299 if( TMU_IS_RUNNING(1) ) { // reschedule
300 TMU_timers[1].timer_run = sh4r.slice_cycle;
301 TMU_schedule_timer( 1 );
302 }
303 return;
304 case TCNT2:
305 MMIO_WRITE( TMU, reg, val );
306 if( TMU_IS_RUNNING(2) ) { // reschedule
307 TMU_timers[2].timer_run = sh4r.slice_cycle;
308 TMU_schedule_timer( 2 );
309 }
310 return;
311 }
312 MMIO_WRITE( TMU, reg, val );
313 }
315 void TMU_count_all( uint32_t nanosecs )
316 {
317 int tcr = MMIO_READ( TMU, TSTR );
318 if( tcr & 0x01 ) {
319 TMU_count( 0, nanosecs );
320 }
321 if( tcr & 0x02 ) {
322 TMU_count( 1, nanosecs );
323 }
324 if( tcr & 0x04 ) {
325 TMU_count( 2, nanosecs );
326 }
327 }
329 void TMU_run_slice( uint32_t nanosecs )
330 {
331 TMU_count_all( nanosecs );
332 TMU_timers[0].timer_run = 0;
333 TMU_timers[1].timer_run = 0;
334 TMU_timers[2].timer_run = 0;
335 }
337 void TMU_update_clocks()
338 {
339 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
340 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
341 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
342 }
344 void TMU_reset( )
345 {
346 TMU_timers[0].timer_remainder = 0;
347 TMU_timers[0].timer_run = 0;
348 TMU_timers[1].timer_remainder = 0;
349 TMU_timers[1].timer_run = 0;
350 TMU_timers[2].timer_remainder = 0;
351 TMU_timers[2].timer_run = 0;
352 TMU_update_clocks();
353 }
355 void TMU_save_state( FILE *f ) {
356 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
357 }
359 int TMU_load_state( FILE *f )
360 {
361 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
362 return 0;
363 }
.