Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 929:fd8cb0c82f5f
prev859:b941c703ccd6
next975:007bf7eb944f
author nkeynes
date Mon Jan 26 03:05:54 2009 +0000 (15 years ago)
permissions -rw-r--r--
last change Fix TLB access to SH4 peripheral control regions
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  * Note: The real clock runs at 33Mhz, which is multiplied by the PLL to
    32  * run the instruction clock at 200Mhz. For sake of simplicity/precision,
    33  * we instead use 200Mhz as the base rate and divide everything down instead.
    34  **/
    35 uint32_t sh4_input_freq = SH4_BASE_RATE;
    37 uint32_t sh4_cpu_multiplier = 2000; /* = 0.5 * frequency */
    39 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
    40 uint32_t sh4_bus_freq = SH4_BASE_RATE / 2;
    41 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 4;
    43 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
    44 uint32_t sh4_bus_period = 2* 1000 / SH4_BASE_RATE;
    45 uint32_t sh4_peripheral_period = 4 * 2000 / SH4_BASE_RATE;
    47 MMIO_REGION_READ_FN( CPG, reg )
    48 {
    49     return MMIO_READ( CPG, reg&0xFFF );
    50 }
    52 /* CPU + bus dividers (note officially only the first 6 values are valid) */
    53 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
    54 /* Peripheral clock dividers (only first 5 are officially valid) */
    55 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
    57 MMIO_REGION_WRITE_FN( CPG, reg, val )
    58 {
    59     uint32_t div;
    60     uint32_t primary_clock = sh4_input_freq;
    61     reg &= 0xFFF;
    62     switch( reg ) {
    63     case FRQCR: /* Frequency control */
    64         if( (val & FRQCR_PLL1EN) == 0 )
    65             primary_clock /= 6;
    66         div = ifc_divider[(val >> 6) & 0x07];
    67         sh4_cpu_freq = primary_clock / div;
    68         sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
    69         div = ifc_divider[(val >> 3) & 0x07];
    70         sh4_bus_freq = primary_clock / div;
    71         sh4_bus_period = 1000 * div / sh4_input_freq;
    72         div = pfc_divider[val & 0x07];
    73         sh4_peripheral_freq = primary_clock / div;
    74         sh4_peripheral_period = 1000 * div / sh4_input_freq;
    76         /* Update everything that depends on the peripheral frequency */
    77         SCIF_update_line_speed();
    78         break;
    79     case WTCSR: /* Watchdog timer */
    80         break;
    81     }
    83     MMIO_WRITE( CPG, reg, val );
    84 }
    86 /**
    87  * We don't really know what the default reset value is as it's determined
    88  * by the mode select pins. This is the standard value that the BIOS sets,
    89  * however, so it works for now.
    90  */
    91 void CPG_reset( )
    92 {
    93     mmio_region_CPG_write( FRQCR, 0x0E0A );
    94 }
    97 /********************************** RTC *************************************/
    99 uint32_t rtc_output_period;
   101 MMIO_REGION_READ_FN( RTC, reg )
   102 {
   103     return MMIO_READ( RTC, reg &0xFFF );
   104 }
   106 MMIO_REGION_WRITE_FN( RTC, reg, val )
   107 {
   108     MMIO_WRITE( RTC, reg &0xFFF, val );
   109 }
   111 /********************************** TMU *************************************/
   113 #define TMU_IS_RUNNING(timer)  (MMIO_READ(TMU,TSTR) & (1<<timer))
   115 uint32_t TMU_count( int timer, uint32_t nanosecs );
   117 void TMU_event_callback( int eventid )
   118 {
   119     TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle );
   120 }
   122 void TMU_init(void)
   123 {
   124     register_event_callback( EVENT_TMU0, TMU_event_callback );
   125     register_event_callback( EVENT_TMU1, TMU_event_callback );
   126     register_event_callback( EVENT_TMU2, TMU_event_callback );
   127 }    
   129 #define TCR_ICPF 0x0200
   130 #define TCR_UNF  0x0100
   131 #define TCR_UNIE 0x0020
   133 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
   135 struct TMU_timer {
   136     uint32_t timer_period;
   137     uint32_t timer_remainder; /* left-over cycles from last count */
   138     uint32_t timer_run; /* cycles already run from this slice */
   139 };
   141 static struct TMU_timer TMU_timers[3];
   143 void TMU_set_timer_control( int timer,  int tcr )
   144 {
   145     uint32_t period = 1;
   146     uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
   148     if( (oldtcr & TCR_UNF) == 0 ) {
   149         tcr = tcr & (~TCR_UNF);
   150     } else {
   151         if( ((oldtcr & TCR_UNIE) == 0) && 
   152                 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
   153             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   154         } else if( (oldtcr & TCR_UNIE) != 0 && 
   155                 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
   156             intc_clear_interrupt( INT_TMU_TUNI0 + timer );
   157         }
   158     }
   160     switch( tcr & 0x07 ) {
   161     case 0:
   162         period = sh4_peripheral_period << 2 ;
   163         break;
   164     case 1: 
   165         period = sh4_peripheral_period << 4;
   166         break;
   167     case 2:
   168         period = sh4_peripheral_period << 6;
   169         break;
   170     case 3: 
   171         period = sh4_peripheral_period << 8;
   172         break;
   173     case 4:
   174         period = sh4_peripheral_period << 10;
   175         break;
   176     case 5:
   177         /* Illegal value. */
   178         ERROR( "TMU %d period set to illegal value (5)", timer );
   179         period = sh4_peripheral_period << 12; /* for something to do */
   180         break;
   181     case 6:
   182         period = rtc_output_period;
   183         break;
   184     case 7:
   185         /* External clock... Hrm? */
   186         period = sh4_peripheral_period; /* I dunno... */
   187         break;
   188     }
   189     TMU_timers[timer].timer_period = period;
   191     MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
   192 }
   194 void TMU_schedule_timer( int timer )
   195 {
   196     uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) * 
   197     (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
   198     event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000), 
   199                          (uint32_t)(duration % 1000000000) );
   200 }
   202 void TMU_start( int timer )
   203 {
   204     TMU_timers[timer].timer_run = sh4r.slice_cycle;
   205     TMU_timers[timer].timer_remainder = 0;
   206     TMU_schedule_timer( timer );
   207 }
   209 /**
   210  * Stop the given timer. Run it up to the current time and leave it there.
   211  */
   212 void TMU_stop( int timer )
   213 {
   214     TMU_count( timer, sh4r.slice_cycle );
   215     event_cancel( EVENT_TMU0+timer );
   216 }
   218 /**
   219  * Count the specified timer for a given number of nanoseconds.
   220  */
   221 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   222 {
   223     uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
   224     TMU_timers[timer].timer_run;
   225     TMU_timers[timer].timer_remainder = 
   226         run_ns % TMU_timers[timer].timer_period;
   227     TMU_timers[timer].timer_run = nanosecs;
   228     uint32_t count = run_ns / TMU_timers[timer].timer_period;
   229     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   230     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   231     if( count > value ) {
   232         uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   233         tcr |= TCR_UNF;
   234         count -= value;
   235         value = reset - (count % reset) + 1;
   236         MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   237         if( tcr & TCR_UNIE ) 
   238             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   239         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   240         TMU_schedule_timer(timer);
   241     } else {
   242         value -= count;
   243         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   244     }
   245     return value;
   246 }
   248 MMIO_REGION_READ_FN( TMU, reg )
   249 {
   250     reg &= 0xFFF;
   251     switch( reg ) {
   252     case TCNT0:
   253         TMU_count( 0, sh4r.slice_cycle );
   254         break;
   255     case TCNT1:
   256         TMU_count( 1, sh4r.slice_cycle );
   257         break;
   258     case TCNT2:
   259         TMU_count( 2, sh4r.slice_cycle );
   260         break;
   261     }
   262     return MMIO_READ( TMU, reg );
   263 }
   265 MMIO_REGION_WRITE_FN( TMU, reg, val )
   266 {
   267     uint32_t oldval;
   268     int i;
   269     reg &= 0xFFF;
   270     switch( reg ) {
   271     case TSTR:
   272         oldval = MMIO_READ( TMU, TSTR );
   273         for( i=0; i<3; i++ ) {
   274             uint32_t tmp = 1<<i;
   275             if( (oldval & tmp) != 0 && (val&tmp) == 0  )
   276                 TMU_stop(i);
   277             else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
   278                 TMU_start(i);
   279         }
   280         break;
   281     case TCR0:
   282         TMU_set_timer_control( 0, val );
   283         return;
   284     case TCR1:
   285         TMU_set_timer_control( 1, val );
   286         return;
   287     case TCR2:
   288         TMU_set_timer_control( 2, val );
   289         return;
   290     case TCNT0:
   291         MMIO_WRITE( TMU, reg, val );
   292         if( TMU_IS_RUNNING(0) ) { // reschedule
   293             TMU_timers[0].timer_run = sh4r.slice_cycle;
   294             TMU_schedule_timer( 0 );
   295         }
   296         return;
   297     case TCNT1:
   298         MMIO_WRITE( TMU, reg, val );
   299         if( TMU_IS_RUNNING(1) ) { // reschedule
   300             TMU_timers[1].timer_run = sh4r.slice_cycle;
   301             TMU_schedule_timer( 1 );
   302         }
   303         return;
   304     case TCNT2:
   305         MMIO_WRITE( TMU, reg, val );
   306         if( TMU_IS_RUNNING(2) ) { // reschedule
   307             TMU_timers[2].timer_run = sh4r.slice_cycle;
   308             TMU_schedule_timer( 2 );
   309         }
   310         return;
   311     }
   312     MMIO_WRITE( TMU, reg, val );
   313 }
   315 void TMU_count_all( uint32_t nanosecs )
   316 {
   317     int tcr = MMIO_READ( TMU, TSTR );
   318     if( tcr & 0x01 ) {
   319         TMU_count( 0, nanosecs );
   320     }
   321     if( tcr & 0x02 ) {
   322         TMU_count( 1, nanosecs );
   323     }
   324     if( tcr & 0x04 ) {
   325         TMU_count( 2, nanosecs );
   326     }
   327 }
   329 void TMU_run_slice( uint32_t nanosecs )
   330 {
   331     TMU_count_all( nanosecs );
   332     TMU_timers[0].timer_run = 0;
   333     TMU_timers[1].timer_run = 0;
   334     TMU_timers[2].timer_run = 0;
   335 }
   337 void TMU_update_clocks()
   338 {
   339     TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
   340     TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
   341     TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
   342 }
   344 void TMU_reset( )
   345 {
   346     TMU_timers[0].timer_remainder = 0;
   347     TMU_timers[0].timer_run = 0;
   348     TMU_timers[1].timer_remainder = 0;
   349     TMU_timers[1].timer_run = 0;
   350     TMU_timers[2].timer_remainder = 0;
   351     TMU_timers[2].timer_run = 0;
   352     TMU_update_clocks();
   353 }
   355 void TMU_save_state( FILE *f ) {
   356     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   357 }
   359 int TMU_load_state( FILE *f ) 
   360 {
   361     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   362     return 0;
   363 }
.