Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 260:c82e26ec0cac
prev115:699aa8916803
next264:e3b8a3ab32b8
author nkeynes
date Wed Jan 03 09:00:17 2007 +0000 (17 years ago)
permissions -rw-r--r--
last change Adjust timers when they're read rather than waiting until the next time
slice. Also temporarily cut the CPU time by 4.
Initialize the FRQCR register to 0x0E0A for convenience
view annotate diff log raw
     1 /**
     2  * $Id: timer.c,v 1.5 2007-01-03 09:00:17 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_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 )
    40 {
    41     return MMIO_READ( CPG, reg );
    42 }
    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 )
    50 {
    51     uint32_t div;
    52     switch( reg ) {
    53     case FRQCR: /* Frequency control */
    54 	div = ifc_divider[(val >> 6) & 0x07];
    55 	sh4_cpu_freq = sh4_input_freq / div;
    56 	sh4_cpu_period = 4000 * 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();
    66 	break;
    67     case WTCSR: /* Watchdog timer */
    68 	break;
    69     }
    71     MMIO_WRITE( CPG, reg, val );
    72 }
    74 /**
    75  * We don't really know what the default reset value is as it's determined
    76  * by the mode select pins. This is the standard value that the BIOS sets,
    77  * however, so it works for now.
    78  */
    79 void CPG_reset( )
    80 {
    81     mmio_region_CPG_write( FRQCR, 0x0E0A );
    82 }
    85 /********************************** RTC *************************************/
    87 uint32_t rtc_output_period;
    89 int32_t mmio_region_RTC_read( uint32_t reg )
    90 {
    91     return MMIO_READ( RTC, reg );
    92 }
    94 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
    95 {
    96     MMIO_WRITE( RTC, reg, val );
    97 }
    99 /********************************** TMU *************************************/
   101 uint32_t TMU_count( int timer, uint32_t nanosecs );
   104 #define TCR_ICPF 0x0200
   105 #define TCR_UNF  0x0100
   106 #define TCR_UNIE 0x0020
   108 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
   110 struct TMU_timer {
   111     uint32_t timer_period;
   112     uint32_t timer_remainder; /* left-over cycles from last count */
   113     uint32_t timer_run; /* cycles already run from this slice */
   114 };
   116 struct TMU_timer TMU_timers[3];
   118 int32_t mmio_region_TMU_read( uint32_t reg )
   119 {
   120     switch( reg ) {
   121     case TCNT0:
   122 	TMU_count( 0, sh4r.slice_cycle );
   123 	TMU_timers[0].timer_run = sh4r.slice_cycle;
   124 	break;
   125     case TCNT1:
   126 	TMU_count( 1, sh4r.slice_cycle );
   127 	TMU_timers[1].timer_run = sh4r.slice_cycle;
   128 	break;
   129     case TCNT2:
   130 	TMU_count( 2, sh4r.slice_cycle );
   131 	TMU_timers[2].timer_run = sh4r.slice_cycle;
   132 	break;
   133     }
   134     return MMIO_READ( TMU, reg );
   135 }
   137 void TMU_set_timer_control( int timer,  int tcr )
   138 {
   139     uint32_t period = 1;
   140     uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
   142     if( (oldtcr & TCR_UNF) == 0 ) {
   143 	tcr = tcr & (~TCR_UNF);
   144     } else {
   145 	if( (oldtcr & TCR_UNIE == 0) && 
   146 	    (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
   147 	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   148 	} else if( (oldtcr & TCR_UNIE) != 0 && 
   149 		   (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
   150 	    intc_clear_interrupt( INT_TMU_TUNI0 + timer );
   151 	}
   152     }
   154     switch( tcr & 0x07 ) {
   155     case 0:
   156 	period = sh4_peripheral_period << 2 ;
   157 	break;
   158     case 1: 
   159 	period = sh4_peripheral_period << 4;
   160 	break;
   161     case 2:
   162 	period = sh4_peripheral_period << 6;
   163 	break;
   164     case 3: 
   165 	period = sh4_peripheral_period << 8;
   166 	break;
   167     case 4:
   168 	period = sh4_peripheral_period << 10;
   169 	break;
   170     case 5:
   171 	/* Illegal value. */
   172 	ERROR( "TMU %d period set to illegal value (5)", timer );
   173 	period = sh4_peripheral_period << 12; /* for something to do */
   174 	break;
   175     case 6:
   176 	period = rtc_output_period;
   177 	break;
   178     case 7:
   179 	/* External clock... Hrm? */
   180 	period = sh4_peripheral_period; /* I dunno... */
   181 	break;
   182     }
   183     TMU_timers[timer].timer_period = period;
   185     MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
   186 }
   188 void TMU_start( int timer )
   189 {
   190     TMU_timers[timer].timer_run = sh4r.slice_cycle;
   191     TMU_timers[timer].timer_remainder = 0;
   192 }
   194 void TMU_stop( int timer )
   195 {
   197 }
   199 /**
   200  * Count the specified timer for a given number of nanoseconds.
   201  */
   202 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   203 {
   204     nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
   205 	TMU_timers[timer].timer_run;
   206     TMU_timers[timer].timer_remainder = 
   207 	nanosecs % TMU_timers[timer].timer_period;
   208     uint32_t count = nanosecs / TMU_timers[timer].timer_period;
   209     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   210     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   211     if( count > value ) {
   212 	uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   213 	tcr |= TCR_UNF;
   214 	count -= value;
   215         value = reset - (count % reset);
   216 	MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   217 	if( tcr & TCR_UNIE ) 
   218 	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   219     } else {
   220 	value -= count;
   221     }
   222     MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   223     return value;
   224 }
   226 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
   227 {
   228     uint32_t oldval;
   229     int i;
   230     switch( reg ) {
   231     case TSTR:
   232 	oldval = MMIO_READ( TMU, TSTR );
   233 	for( i=0; i<3; i++ ) {
   234 	    uint32_t tmp = 1<<i;
   235 	    if( (oldval & tmp) == 1 && (val&tmp) == 0  )
   236 		TMU_stop(i);
   237 	    else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
   238 		TMU_start(i);
   239 	}
   240 	break;
   241     case TCR0:
   242 	TMU_set_timer_control( 0, val );
   243 	return;
   244     case TCR1:
   245 	TMU_set_timer_control( 1, val );
   246 	return;
   247     case TCR2:
   248 	TMU_set_timer_control( 2, val );
   249 	return;
   250     }
   251     MMIO_WRITE( TMU, reg, val );
   252 }
   254 void TMU_run_slice( uint32_t nanosecs )
   255 {
   256     int tcr = MMIO_READ( TMU, TSTR );
   257     if( tcr & 0x01 ) {
   258 	TMU_count( 0, nanosecs );
   259 	TMU_timers[0].timer_run = 0;
   260     }
   261     if( tcr & 0x02 ) {
   262 	TMU_count( 1, nanosecs );
   263 	TMU_timers[1].timer_run = 0;
   264     }
   265     if( tcr & 0x04 ) {
   266 	TMU_count( 2, nanosecs );
   267 	TMU_timers[2].timer_run = 0;
   268     }
   269 }
   271 void TMU_update_clocks()
   272 {
   273     TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
   274     TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
   275     TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
   276 }
   278 void TMU_reset( )
   279 {
   280     TMU_timers[0].timer_remainder = 0;
   281     TMU_timers[0].timer_run = 0;
   282     TMU_timers[1].timer_remainder = 0;
   283     TMU_timers[1].timer_run = 0;
   284     TMU_timers[2].timer_remainder = 0;
   285     TMU_timers[2].timer_run = 0;
   286     TMU_update_clocks();
   287 }
   289 void TMU_save_state( FILE *f ) {
   290     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   291 }
   293 int TMU_load_state( FILE *f ) 
   294 {
   295     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   296     return 0;
   297 }
.