Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 619:0800a0137472
prev561:533f6b478071
next736:a02d1475ccfd
author nkeynes
date Fri Mar 28 12:32:25 2008 +0000 (16 years ago)
permissions -rw-r--r--
last change Merge lxdream-render branch (643:670) to trunk
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 }
.