filename | src/sh4/timer.c |
changeset | 422:61a0598e07ff |
prev | 414:fd8e96bc513e |
next | 561:533f6b478071 |
author | nkeynes |
date | Sun Oct 28 07:23:46 2007 +0000 (16 years ago) |
permissions | -rw-r--r-- |
last change | Fix CDI with multiple tracks/session Fix file being closed too early |
view | annotate | diff | log | raw |
1 /**
2 * $Id: timer.c,v 1.9 2007-10-06 09:03:24 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_multiplier = 2000; /* = 0.5 * frequency */
33 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
34 uint32_t sh4_bus_freq = SH4_BASE_RATE;
35 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
37 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
38 uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
39 uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
41 int32_t mmio_region_CPG_read( uint32_t reg )
42 {
43 return MMIO_READ( CPG, reg );
44 }
46 /* CPU + bus dividers (note officially only the first 6 values are valid) */
47 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
48 /* Peripheral clock dividers (only first 5 are officially valid) */
49 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
51 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
52 {
53 uint32_t div;
54 switch( reg ) {
55 case FRQCR: /* Frequency control */
56 div = ifc_divider[(val >> 6) & 0x07];
57 sh4_cpu_freq = sh4_input_freq / div;
58 sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
59 div = ifc_divider[(val >> 3) & 0x07];
60 sh4_bus_freq = sh4_input_freq / div;
61 sh4_bus_period = 1000 * div / sh4_input_freq;
62 div = pfc_divider[val & 0x07];
63 sh4_peripheral_freq = sh4_input_freq / div;
64 sh4_peripheral_period = 1000 * div / sh4_input_freq;
66 /* Update everything that depends on the peripheral frequency */
67 SCIF_update_line_speed();
68 break;
69 case WTCSR: /* Watchdog timer */
70 break;
71 }
73 MMIO_WRITE( CPG, reg, val );
74 }
76 /**
77 * We don't really know what the default reset value is as it's determined
78 * by the mode select pins. This is the standard value that the BIOS sets,
79 * however, so it works for now.
80 */
81 void CPG_reset( )
82 {
83 mmio_region_CPG_write( FRQCR, 0x0E0A );
84 }
87 /********************************** RTC *************************************/
89 uint32_t rtc_output_period;
91 int32_t mmio_region_RTC_read( uint32_t reg )
92 {
93 return MMIO_READ( RTC, reg );
94 }
96 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
97 {
98 MMIO_WRITE( RTC, reg, val );
99 }
101 /********************************** TMU *************************************/
103 uint32_t TMU_count( int timer, uint32_t nanosecs );
106 #define TCR_ICPF 0x0200
107 #define TCR_UNF 0x0100
108 #define TCR_UNIE 0x0020
110 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
112 struct TMU_timer {
113 uint32_t timer_period;
114 uint32_t timer_remainder; /* left-over cycles from last count */
115 uint32_t timer_run; /* cycles already run from this slice */
116 };
118 struct TMU_timer TMU_timers[3];
120 int32_t mmio_region_TMU_read( uint32_t reg )
121 {
122 switch( reg ) {
123 case TCNT0:
124 TMU_count( 0, sh4r.slice_cycle );
125 TMU_timers[0].timer_run = sh4r.slice_cycle;
126 break;
127 case TCNT1:
128 TMU_count( 1, sh4r.slice_cycle );
129 TMU_timers[1].timer_run = sh4r.slice_cycle;
130 break;
131 case TCNT2:
132 TMU_count( 2, sh4r.slice_cycle );
133 TMU_timers[2].timer_run = sh4r.slice_cycle;
134 break;
135 }
136 return MMIO_READ( TMU, reg );
137 }
139 void TMU_set_timer_control( int timer, int tcr )
140 {
141 uint32_t period = 1;
142 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
144 if( (oldtcr & TCR_UNF) == 0 ) {
145 tcr = tcr & (~TCR_UNF);
146 } else {
147 if( ((oldtcr & TCR_UNIE) == 0) &&
148 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
149 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
150 } else if( (oldtcr & TCR_UNIE) != 0 &&
151 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
152 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
153 }
154 }
156 switch( tcr & 0x07 ) {
157 case 0:
158 period = sh4_peripheral_period << 2 ;
159 break;
160 case 1:
161 period = sh4_peripheral_period << 4;
162 break;
163 case 2:
164 period = sh4_peripheral_period << 6;
165 break;
166 case 3:
167 period = sh4_peripheral_period << 8;
168 break;
169 case 4:
170 period = sh4_peripheral_period << 10;
171 break;
172 case 5:
173 /* Illegal value. */
174 ERROR( "TMU %d period set to illegal value (5)", timer );
175 period = sh4_peripheral_period << 12; /* for something to do */
176 break;
177 case 6:
178 period = rtc_output_period;
179 break;
180 case 7:
181 /* External clock... Hrm? */
182 period = sh4_peripheral_period; /* I dunno... */
183 break;
184 }
185 TMU_timers[timer].timer_period = period;
187 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
188 }
190 void TMU_start( int timer )
191 {
192 TMU_timers[timer].timer_run = sh4r.slice_cycle;
193 TMU_timers[timer].timer_remainder = 0;
194 }
196 /**
197 * Stop the given timer. Run it up to the current time and leave it there.
198 */
199 void TMU_stop( int timer )
200 {
201 TMU_count( timer, sh4r.slice_cycle );
202 TMU_timers[timer].timer_run = sh4r.slice_cycle;
203 }
205 /**
206 * Count the specified timer for a given number of nanoseconds.
207 */
208 uint32_t TMU_count( int timer, uint32_t nanosecs )
209 {
210 nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
211 TMU_timers[timer].timer_run;
212 TMU_timers[timer].timer_remainder =
213 nanosecs % TMU_timers[timer].timer_period;
214 uint32_t count = nanosecs / TMU_timers[timer].timer_period;
215 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
216 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
217 if( count > value ) {
218 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
219 tcr |= TCR_UNF;
220 count -= value;
221 value = reset - (count % reset);
222 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
223 if( tcr & TCR_UNIE )
224 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
225 } else {
226 value -= count;
227 }
228 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
229 return value;
230 }
232 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
233 {
234 uint32_t oldval;
235 int i;
236 switch( reg ) {
237 case TSTR:
238 oldval = MMIO_READ( TMU, TSTR );
239 for( i=0; i<3; i++ ) {
240 uint32_t tmp = 1<<i;
241 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
242 TMU_stop(i);
243 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
244 TMU_start(i);
245 }
246 break;
247 case TCR0:
248 TMU_set_timer_control( 0, val );
249 return;
250 case TCR1:
251 TMU_set_timer_control( 1, val );
252 return;
253 case TCR2:
254 TMU_set_timer_control( 2, val );
255 return;
256 }
257 MMIO_WRITE( TMU, reg, val );
258 }
260 void TMU_run_slice( uint32_t nanosecs )
261 {
262 int tcr = MMIO_READ( TMU, TSTR );
263 if( tcr & 0x01 ) {
264 TMU_count( 0, nanosecs );
265 TMU_timers[0].timer_run = 0;
266 }
267 if( tcr & 0x02 ) {
268 TMU_count( 1, nanosecs );
269 TMU_timers[1].timer_run = 0;
270 }
271 if( tcr & 0x04 ) {
272 TMU_count( 2, nanosecs );
273 TMU_timers[2].timer_run = 0;
274 }
275 }
277 void TMU_update_clocks()
278 {
279 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
280 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
281 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
282 }
284 void TMU_reset( )
285 {
286 TMU_timers[0].timer_remainder = 0;
287 TMU_timers[0].timer_run = 0;
288 TMU_timers[1].timer_remainder = 0;
289 TMU_timers[1].timer_run = 0;
290 TMU_timers[2].timer_remainder = 0;
291 TMU_timers[2].timer_run = 0;
292 TMU_update_clocks();
293 }
295 void TMU_save_state( FILE *f ) {
296 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
297 }
299 int TMU_load_state( FILE *f )
300 {
301 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
302 return 0;
303 }
.