Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 561:533f6b478071
prev422:61a0598e07ff
next619:0800a0137472
author nkeynes
date Tue Jan 01 05:08:38 2008 +0000 (12 years ago)
branchlxdream-mmu
permissions -rw-r--r--
last change Enable Id keyword on all source files
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 "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 }
.