2 * $Id: timer.c,v 1.3 2005-12-29 12:52:29 nkeynes Exp $
4 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
5 * keep things simple (they intertwine a bit).
7 * Copyright (c) 2005 Nathan Keynes.
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.
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.
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 )
41 return MMIO_READ( CPG, reg );
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 )
53 case FRQCR: /* Frequency control */
54 div = ifc_divider[(val >> 6) & 0x07];
55 sh4_cpu_freq = sh4_input_freq / div;
56 sh4_cpu_period = 1000 * 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();
67 case WTCSR: /* Watchdog timer */
71 MMIO_WRITE( CPG, reg, val );
74 /********************************** RTC *************************************/
76 uint32_t rtc_output_period;
78 int32_t mmio_region_RTC_read( uint32_t reg )
80 return MMIO_READ( RTC, reg );
83 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
85 MMIO_WRITE( RTC, reg, val );
88 /********************************** TMU *************************************/
90 #define TCR_ICPF 0x0200
91 #define TCR_UNF 0x0100
92 #define TCR_UNIE 0x0020
95 uint32_t timer_period;
96 uint32_t timer_remainder; /* left-over cycles from last count */
97 uint32_t timer_run; /* cycles already run from this slice */
100 struct TMU_timer TMU_timers[3];
102 int32_t mmio_region_TMU_read( uint32_t reg )
104 return MMIO_READ( TMU, reg );
107 void TMU_set_timer_period( int timer, int tcr )
110 switch( tcr & 0x07 ) {
112 period = sh4_peripheral_period << 2 ;
115 period = sh4_peripheral_period << 4;
118 period = sh4_peripheral_period << 6;
121 period = sh4_peripheral_period << 8;
124 period = sh4_peripheral_period << 10;
128 ERROR( "TMU %d period set to illegal value (5)", timer );
129 period = sh4_peripheral_period << 12; /* for something to do */
132 period = rtc_output_period;
135 /* External clock... Hrm? */
136 period = sh4_peripheral_period; /* I dunno... */
139 TMU_timers[timer].timer_period = period;
142 void TMU_start( int timer )
144 TMU_timers[timer].timer_run = 0;
145 TMU_timers[timer].timer_remainder = 0;
148 void TMU_stop( int timer )
154 * Count the specified timer for a given number of nanoseconds.
156 uint32_t TMU_count( int timer, uint32_t nanosecs )
158 nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
159 TMU_timers[timer].timer_run;
160 TMU_timers[timer].timer_remainder =
161 nanosecs % TMU_timers[timer].timer_period;
162 uint32_t count = nanosecs / TMU_timers[timer].timer_period;
163 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
164 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
165 if( count > value ) {
166 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
169 value = reset - (count % reset);
170 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
172 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
176 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
180 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
186 oldval = MMIO_READ( TMU, TSTR );
187 for( i=0; i<3; i++ ) {
189 if( (oldval & tmp) == 1 && (val&tmp) == 0 )
191 else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
196 TMU_set_timer_period( 0, val );
199 TMU_set_timer_period( 1, val );
202 TMU_set_timer_period( 2, val );
205 MMIO_WRITE( TMU, reg, val );
208 void TMU_run_slice( uint32_t nanosecs )
210 int tcr = MMIO_READ( TMU, TSTR );
212 TMU_count( 0, nanosecs );
213 TMU_timers[0].timer_run = 0;
216 TMU_count( 1, nanosecs );
217 TMU_timers[1].timer_run = 0;
220 TMU_count( 2, nanosecs );
221 TMU_timers[2].timer_run = 0;
225 void TMU_update_clocks()
227 TMU_set_timer_period( 0, MMIO_READ( TMU, TCR0 ) );
228 TMU_set_timer_period( 1, MMIO_READ( TMU, TCR1 ) );
229 TMU_set_timer_period( 2, MMIO_READ( TMU, TCR2 ) );
234 TMU_timers[0].timer_remainder = 0;
235 TMU_timers[0].timer_run = 0;
236 TMU_timers[1].timer_remainder = 0;
237 TMU_timers[1].timer_run = 0;
238 TMU_timers[2].timer_remainder = 0;
239 TMU_timers[2].timer_run = 0;
243 void TMU_save_state( FILE *f ) {
244 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
247 int TMU_load_state( FILE *f )
249 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
.