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
file annotate diff log raw
nkeynes@23
     1
/**
nkeynes@260
     2
 * $Id: timer.c,v 1.5 2007-01-03 09:00:17 nkeynes Exp $
nkeynes@23
     3
 * 
nkeynes@23
     4
 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
nkeynes@23
     5
 * keep things simple (they intertwine a bit).
nkeynes@23
     6
 *
nkeynes@23
     7
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@23
     8
 *
nkeynes@23
     9
 * This program is free software; you can redistribute it and/or modify
nkeynes@23
    10
 * it under the terms of the GNU General Public License as published by
nkeynes@23
    11
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@23
    12
 * (at your option) any later version.
nkeynes@23
    13
 *
nkeynes@23
    14
 * This program is distributed in the hope that it will be useful,
nkeynes@23
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@23
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@23
    17
 * GNU General Public License for more details.
nkeynes@23
    18
 */
nkeynes@23
    19
nkeynes@23
    20
#include "dream.h"
nkeynes@23
    21
#include "mem.h"
nkeynes@23
    22
#include "clock.h"
nkeynes@23
    23
#include "sh4core.h"
nkeynes@23
    24
#include "sh4mmio.h"
nkeynes@53
    25
#include "intc.h"
nkeynes@23
    26
nkeynes@23
    27
/********************************* CPG *************************************/
nkeynes@53
    28
/* This is the base clock from which all other clocks are derived */
nkeynes@53
    29
uint32_t sh4_input_freq = SH4_BASE_RATE;
nkeynes@53
    30
nkeynes@53
    31
uint32_t sh4_cpu_freq = SH4_BASE_RATE;
nkeynes@53
    32
uint32_t sh4_bus_freq = SH4_BASE_RATE;
nkeynes@53
    33
uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
nkeynes@53
    34
nkeynes@53
    35
uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
nkeynes@53
    36
uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
nkeynes@53
    37
uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
nkeynes@23
    38
nkeynes@23
    39
int32_t mmio_region_CPG_read( uint32_t reg )
nkeynes@23
    40
{
nkeynes@23
    41
    return MMIO_READ( CPG, reg );
nkeynes@23
    42
}
nkeynes@23
    43
nkeynes@53
    44
/* CPU + bus dividers (note officially only the first 6 values are valid) */
nkeynes@53
    45
int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
nkeynes@53
    46
/* Peripheral clock dividers (only first 5 are officially valid) */
nkeynes@53
    47
int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
nkeynes@53
    48
nkeynes@23
    49
void mmio_region_CPG_write( uint32_t reg, uint32_t val )
nkeynes@23
    50
{
nkeynes@53
    51
    uint32_t div;
nkeynes@53
    52
    switch( reg ) {
nkeynes@53
    53
    case FRQCR: /* Frequency control */
nkeynes@53
    54
	div = ifc_divider[(val >> 6) & 0x07];
nkeynes@53
    55
	sh4_cpu_freq = sh4_input_freq / div;
nkeynes@260
    56
	sh4_cpu_period = 4000 * div / sh4_input_freq;
nkeynes@53
    57
	div = ifc_divider[(val >> 3) & 0x07];
nkeynes@53
    58
	sh4_bus_freq = sh4_input_freq / div;
nkeynes@53
    59
	sh4_bus_period = 1000 * div / sh4_input_freq;
nkeynes@53
    60
	div = pfc_divider[val & 0x07];
nkeynes@53
    61
	sh4_peripheral_freq = sh4_input_freq / div;
nkeynes@53
    62
	sh4_peripheral_period = 1000 * div / sh4_input_freq;
nkeynes@53
    63
nkeynes@53
    64
	/* Update everything that depends on the peripheral frequency */
nkeynes@53
    65
	SCIF_update_line_speed();
nkeynes@53
    66
	break;
nkeynes@53
    67
    case WTCSR: /* Watchdog timer */
nkeynes@53
    68
	break;
nkeynes@53
    69
    }
nkeynes@53
    70
	
nkeynes@23
    71
    MMIO_WRITE( CPG, reg, val );
nkeynes@23
    72
}
nkeynes@23
    73
nkeynes@260
    74
/**
nkeynes@260
    75
 * We don't really know what the default reset value is as it's determined
nkeynes@260
    76
 * by the mode select pins. This is the standard value that the BIOS sets,
nkeynes@260
    77
 * however, so it works for now.
nkeynes@260
    78
 */
nkeynes@260
    79
void CPG_reset( )
nkeynes@260
    80
{
nkeynes@260
    81
    mmio_region_CPG_write( FRQCR, 0x0E0A );
nkeynes@260
    82
}
nkeynes@260
    83
nkeynes@260
    84
nkeynes@23
    85
/********************************** RTC *************************************/
nkeynes@23
    86
nkeynes@53
    87
uint32_t rtc_output_period;
nkeynes@53
    88
nkeynes@23
    89
int32_t mmio_region_RTC_read( uint32_t reg )
nkeynes@23
    90
{
nkeynes@23
    91
    return MMIO_READ( RTC, reg );
nkeynes@23
    92
}
nkeynes@23
    93
nkeynes@23
    94
void mmio_region_RTC_write( uint32_t reg, uint32_t val )
nkeynes@23
    95
{
nkeynes@23
    96
    MMIO_WRITE( RTC, reg, val );
nkeynes@23
    97
}
nkeynes@23
    98
nkeynes@23
    99
/********************************** TMU *************************************/
nkeynes@23
   100
nkeynes@260
   101
uint32_t TMU_count( int timer, uint32_t nanosecs );
nkeynes@260
   102
nkeynes@260
   103
nkeynes@53
   104
#define TCR_ICPF 0x0200
nkeynes@53
   105
#define TCR_UNF  0x0100
nkeynes@53
   106
#define TCR_UNIE 0x0020
nkeynes@53
   107
nkeynes@115
   108
#define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
nkeynes@115
   109
nkeynes@53
   110
struct TMU_timer {
nkeynes@53
   111
    uint32_t timer_period;
nkeynes@53
   112
    uint32_t timer_remainder; /* left-over cycles from last count */
nkeynes@53
   113
    uint32_t timer_run; /* cycles already run from this slice */
nkeynes@53
   114
};
nkeynes@53
   115
nkeynes@53
   116
struct TMU_timer TMU_timers[3];
nkeynes@23
   117
nkeynes@23
   118
int32_t mmio_region_TMU_read( uint32_t reg )
nkeynes@23
   119
{
nkeynes@260
   120
    switch( reg ) {
nkeynes@260
   121
    case TCNT0:
nkeynes@260
   122
	TMU_count( 0, sh4r.slice_cycle );
nkeynes@260
   123
	TMU_timers[0].timer_run = sh4r.slice_cycle;
nkeynes@260
   124
	break;
nkeynes@260
   125
    case TCNT1:
nkeynes@260
   126
	TMU_count( 1, sh4r.slice_cycle );
nkeynes@260
   127
	TMU_timers[1].timer_run = sh4r.slice_cycle;
nkeynes@260
   128
	break;
nkeynes@260
   129
    case TCNT2:
nkeynes@260
   130
	TMU_count( 2, sh4r.slice_cycle );
nkeynes@260
   131
	TMU_timers[2].timer_run = sh4r.slice_cycle;
nkeynes@260
   132
	break;
nkeynes@260
   133
    }
nkeynes@23
   134
    return MMIO_READ( TMU, reg );
nkeynes@23
   135
}
nkeynes@23
   136
nkeynes@115
   137
void TMU_set_timer_control( int timer,  int tcr )
nkeynes@53
   138
{
nkeynes@53
   139
    uint32_t period = 1;
nkeynes@115
   140
    uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
nkeynes@115
   141
nkeynes@115
   142
    if( (oldtcr & TCR_UNF) == 0 ) {
nkeynes@115
   143
	tcr = tcr & (~TCR_UNF);
nkeynes@115
   144
    } else {
nkeynes@115
   145
	if( (oldtcr & TCR_UNIE == 0) && 
nkeynes@115
   146
	    (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
nkeynes@115
   147
	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
nkeynes@115
   148
	} else if( (oldtcr & TCR_UNIE) != 0 && 
nkeynes@115
   149
		   (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
nkeynes@115
   150
	    intc_clear_interrupt( INT_TMU_TUNI0 + timer );
nkeynes@115
   151
	}
nkeynes@115
   152
    }
nkeynes@115
   153
nkeynes@53
   154
    switch( tcr & 0x07 ) {
nkeynes@53
   155
    case 0:
nkeynes@53
   156
	period = sh4_peripheral_period << 2 ;
nkeynes@53
   157
	break;
nkeynes@53
   158
    case 1: 
nkeynes@53
   159
	period = sh4_peripheral_period << 4;
nkeynes@53
   160
	break;
nkeynes@53
   161
    case 2:
nkeynes@53
   162
	period = sh4_peripheral_period << 6;
nkeynes@53
   163
	break;
nkeynes@53
   164
    case 3: 
nkeynes@53
   165
	period = sh4_peripheral_period << 8;
nkeynes@53
   166
	break;
nkeynes@53
   167
    case 4:
nkeynes@53
   168
	period = sh4_peripheral_period << 10;
nkeynes@53
   169
	break;
nkeynes@53
   170
    case 5:
nkeynes@53
   171
	/* Illegal value. */
nkeynes@53
   172
	ERROR( "TMU %d period set to illegal value (5)", timer );
nkeynes@53
   173
	period = sh4_peripheral_period << 12; /* for something to do */
nkeynes@53
   174
	break;
nkeynes@53
   175
    case 6:
nkeynes@53
   176
	period = rtc_output_period;
nkeynes@53
   177
	break;
nkeynes@53
   178
    case 7:
nkeynes@53
   179
	/* External clock... Hrm? */
nkeynes@53
   180
	period = sh4_peripheral_period; /* I dunno... */
nkeynes@53
   181
	break;
nkeynes@53
   182
    }
nkeynes@53
   183
    TMU_timers[timer].timer_period = period;
nkeynes@115
   184
nkeynes@115
   185
    MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
nkeynes@53
   186
}
nkeynes@23
   187
nkeynes@53
   188
void TMU_start( int timer )
nkeynes@23
   189
{
nkeynes@260
   190
    TMU_timers[timer].timer_run = sh4r.slice_cycle;
nkeynes@53
   191
    TMU_timers[timer].timer_remainder = 0;
nkeynes@53
   192
}
nkeynes@53
   193
nkeynes@53
   194
void TMU_stop( int timer )
nkeynes@53
   195
{
nkeynes@53
   196
nkeynes@53
   197
}
nkeynes@53
   198
nkeynes@53
   199
/**
nkeynes@53
   200
 * Count the specified timer for a given number of nanoseconds.
nkeynes@53
   201
 */
nkeynes@53
   202
uint32_t TMU_count( int timer, uint32_t nanosecs ) 
nkeynes@53
   203
{
nkeynes@53
   204
    nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
nkeynes@53
   205
	TMU_timers[timer].timer_run;
nkeynes@53
   206
    TMU_timers[timer].timer_remainder = 
nkeynes@53
   207
	nanosecs % TMU_timers[timer].timer_period;
nkeynes@53
   208
    uint32_t count = nanosecs / TMU_timers[timer].timer_period;
nkeynes@53
   209
    uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
nkeynes@53
   210
    uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
nkeynes@53
   211
    if( count > value ) {
nkeynes@53
   212
	uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
nkeynes@53
   213
	tcr |= TCR_UNF;
nkeynes@53
   214
	count -= value;
nkeynes@53
   215
        value = reset - (count % reset);
nkeynes@53
   216
	MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
nkeynes@53
   217
	if( tcr & TCR_UNIE ) 
nkeynes@53
   218
	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
nkeynes@53
   219
    } else {
nkeynes@53
   220
	value -= count;
nkeynes@23
   221
    }
nkeynes@53
   222
    MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
nkeynes@53
   223
    return value;
nkeynes@23
   224
}
nkeynes@23
   225
nkeynes@23
   226
void mmio_region_TMU_write( uint32_t reg, uint32_t val )
nkeynes@23
   227
{
nkeynes@53
   228
    uint32_t oldval;
nkeynes@53
   229
    int i;
nkeynes@23
   230
    switch( reg ) {
nkeynes@53
   231
    case TSTR:
nkeynes@53
   232
	oldval = MMIO_READ( TMU, TSTR );
nkeynes@53
   233
	for( i=0; i<3; i++ ) {
nkeynes@53
   234
	    uint32_t tmp = 1<<i;
nkeynes@53
   235
	    if( (oldval & tmp) == 1 && (val&tmp) == 0  )
nkeynes@53
   236
		TMU_stop(i);
nkeynes@53
   237
	    else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
nkeynes@53
   238
		TMU_start(i);
nkeynes@53
   239
	}
nkeynes@53
   240
	break;
nkeynes@53
   241
    case TCR0:
nkeynes@115
   242
	TMU_set_timer_control( 0, val );
nkeynes@115
   243
	return;
nkeynes@53
   244
    case TCR1:
nkeynes@115
   245
	TMU_set_timer_control( 1, val );
nkeynes@115
   246
	return;
nkeynes@53
   247
    case TCR2:
nkeynes@115
   248
	TMU_set_timer_control( 2, val );
nkeynes@115
   249
	return;
nkeynes@23
   250
    }
nkeynes@23
   251
    MMIO_WRITE( TMU, reg, val );
nkeynes@23
   252
}
nkeynes@23
   253
nkeynes@30
   254
void TMU_run_slice( uint32_t nanosecs )
nkeynes@23
   255
{
nkeynes@23
   256
    int tcr = MMIO_READ( TMU, TSTR );
nkeynes@23
   257
    if( tcr & 0x01 ) {
nkeynes@53
   258
	TMU_count( 0, nanosecs );
nkeynes@53
   259
	TMU_timers[0].timer_run = 0;
nkeynes@23
   260
    }
nkeynes@23
   261
    if( tcr & 0x02 ) {
nkeynes@53
   262
	TMU_count( 1, nanosecs );
nkeynes@53
   263
	TMU_timers[1].timer_run = 0;
nkeynes@23
   264
    }
nkeynes@23
   265
    if( tcr & 0x04 ) {
nkeynes@53
   266
	TMU_count( 2, nanosecs );
nkeynes@53
   267
	TMU_timers[2].timer_run = 0;
nkeynes@23
   268
    }
nkeynes@23
   269
}
nkeynes@53
   270
nkeynes@53
   271
void TMU_update_clocks()
nkeynes@53
   272
{
nkeynes@115
   273
    TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
nkeynes@115
   274
    TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
nkeynes@115
   275
    TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
nkeynes@53
   276
}
nkeynes@53
   277
nkeynes@53
   278
void TMU_reset( )
nkeynes@53
   279
{
nkeynes@53
   280
    TMU_timers[0].timer_remainder = 0;
nkeynes@53
   281
    TMU_timers[0].timer_run = 0;
nkeynes@53
   282
    TMU_timers[1].timer_remainder = 0;
nkeynes@53
   283
    TMU_timers[1].timer_run = 0;
nkeynes@53
   284
    TMU_timers[2].timer_remainder = 0;
nkeynes@53
   285
    TMU_timers[2].timer_run = 0;
nkeynes@53
   286
    TMU_update_clocks();
nkeynes@53
   287
}
nkeynes@53
   288
nkeynes@53
   289
void TMU_save_state( FILE *f ) {
nkeynes@53
   290
    fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
nkeynes@53
   291
}
nkeynes@53
   292
nkeynes@53
   293
int TMU_load_state( FILE *f ) 
nkeynes@53
   294
{
nkeynes@53
   295
    fread( &TMU_timers, sizeof(TMU_timers), 1, f );
nkeynes@53
   296
    return 0;
nkeynes@53
   297
}
.