Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 115:699aa8916803
prev53:f2981805b929
next260:c82e26ec0cac
author nkeynes
date Fri Mar 17 12:13:12 2006 +0000 (18 years ago)
permissions -rw-r--r--
last change Implement timer interrupt clearing (kinda useful)
view annotate diff log raw
     1 /**
     2  * $Id: timer.c,v 1.4 2006-03-17 12:13:12 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 = 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();
    66 	break;
    67     case WTCSR: /* Watchdog timer */
    68 	break;
    69     }
    71     MMIO_WRITE( CPG, reg, val );
    72 }
    74 /********************************** RTC *************************************/
    76 uint32_t rtc_output_period;
    78 int32_t mmio_region_RTC_read( uint32_t reg )
    79 {
    80     return MMIO_READ( RTC, reg );
    81 }
    83 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
    84 {
    85     MMIO_WRITE( RTC, reg, val );
    86 }
    88 /********************************** TMU *************************************/
    90 #define TCR_ICPF 0x0200
    91 #define TCR_UNF  0x0100
    92 #define TCR_UNIE 0x0020
    94 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
    96 struct TMU_timer {
    97     uint32_t timer_period;
    98     uint32_t timer_remainder; /* left-over cycles from last count */
    99     uint32_t timer_run; /* cycles already run from this slice */
   100 };
   102 struct TMU_timer TMU_timers[3];
   104 int32_t mmio_region_TMU_read( uint32_t reg )
   105 {
   106     return MMIO_READ( TMU, reg );
   107 }
   109 void TMU_set_timer_control( int timer,  int tcr )
   110 {
   111     uint32_t period = 1;
   112     uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
   114     if( (oldtcr & TCR_UNF) == 0 ) {
   115 	tcr = tcr & (~TCR_UNF);
   116     } else {
   117 	if( (oldtcr & TCR_UNIE == 0) && 
   118 	    (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
   119 	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   120 	} else if( (oldtcr & TCR_UNIE) != 0 && 
   121 		   (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
   122 	    intc_clear_interrupt( INT_TMU_TUNI0 + timer );
   123 	}
   124     }
   126     switch( tcr & 0x07 ) {
   127     case 0:
   128 	period = sh4_peripheral_period << 2 ;
   129 	break;
   130     case 1: 
   131 	period = sh4_peripheral_period << 4;
   132 	break;
   133     case 2:
   134 	period = sh4_peripheral_period << 6;
   135 	break;
   136     case 3: 
   137 	period = sh4_peripheral_period << 8;
   138 	break;
   139     case 4:
   140 	period = sh4_peripheral_period << 10;
   141 	break;
   142     case 5:
   143 	/* Illegal value. */
   144 	ERROR( "TMU %d period set to illegal value (5)", timer );
   145 	period = sh4_peripheral_period << 12; /* for something to do */
   146 	break;
   147     case 6:
   148 	period = rtc_output_period;
   149 	break;
   150     case 7:
   151 	/* External clock... Hrm? */
   152 	period = sh4_peripheral_period; /* I dunno... */
   153 	break;
   154     }
   155     TMU_timers[timer].timer_period = period;
   157     MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
   158 }
   160 void TMU_start( int timer )
   161 {
   162     TMU_timers[timer].timer_run = 0;
   163     TMU_timers[timer].timer_remainder = 0;
   164 }
   166 void TMU_stop( int timer )
   167 {
   169 }
   171 /**
   172  * Count the specified timer for a given number of nanoseconds.
   173  */
   174 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   175 {
   176     nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
   177 	TMU_timers[timer].timer_run;
   178     TMU_timers[timer].timer_remainder = 
   179 	nanosecs % TMU_timers[timer].timer_period;
   180     uint32_t count = nanosecs / TMU_timers[timer].timer_period;
   181     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   182     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   183     if( count > value ) {
   184 	uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   185 	tcr |= TCR_UNF;
   186 	count -= value;
   187         value = reset - (count % reset);
   188 	MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   189 	if( tcr & TCR_UNIE ) 
   190 	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   191     } else {
   192 	value -= count;
   193     }
   194     MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   195     return value;
   196 }
   198 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
   199 {
   200     uint32_t oldval;
   201     int i;
   202     switch( reg ) {
   203     case TSTR:
   204 	oldval = MMIO_READ( TMU, TSTR );
   205 	for( i=0; i<3; i++ ) {
   206 	    uint32_t tmp = 1<<i;
   207 	    if( (oldval & tmp) == 1 && (val&tmp) == 0  )
   208 		TMU_stop(i);
   209 	    else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
   210 		TMU_start(i);
   211 	}
   212 	break;
   213     case TCR0:
   214 	TMU_set_timer_control( 0, val );
   215 	return;
   216     case TCR1:
   217 	TMU_set_timer_control( 1, val );
   218 	return;
   219     case TCR2:
   220 	TMU_set_timer_control( 2, val );
   221 	return;
   222     }
   223     MMIO_WRITE( TMU, reg, val );
   224 }
   226 void TMU_run_slice( uint32_t nanosecs )
   227 {
   228     int tcr = MMIO_READ( TMU, TSTR );
   229     if( tcr & 0x01 ) {
   230 	TMU_count( 0, nanosecs );
   231 	TMU_timers[0].timer_run = 0;
   232     }
   233     if( tcr & 0x02 ) {
   234 	TMU_count( 1, nanosecs );
   235 	TMU_timers[1].timer_run = 0;
   236     }
   237     if( tcr & 0x04 ) {
   238 	TMU_count( 2, nanosecs );
   239 	TMU_timers[2].timer_run = 0;
   240     }
   241 }
   243 void TMU_update_clocks()
   244 {
   245     TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
   246     TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
   247     TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
   248 }
   250 void TMU_reset( )
   251 {
   252     TMU_timers[0].timer_remainder = 0;
   253     TMU_timers[0].timer_run = 0;
   254     TMU_timers[1].timer_remainder = 0;
   255     TMU_timers[1].timer_run = 0;
   256     TMU_timers[2].timer_remainder = 0;
   257     TMU_timers[2].timer_run = 0;
   258     TMU_update_clocks();
   259 }
   261 void TMU_save_state( FILE *f ) {
   262     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   263 }
   265 int TMU_load_state( FILE *f ) 
   266 {
   267     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   268     return 0;
   269 }
.