A simple OutRun on Casio Graph 90+E
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
OutRun/src/clock.cc

327 lines
9.0 KiB

#include "clock.h"
#include <gint/hardware.h>
#include <gint/defs/types.h>
#include <gint/cpu.h>
#include <gint/mpu/bsc.h>
#include <gint/mpu/cpg.h>
#define FLLFRQ_default 0x00004384
#define FRQCR_default 0x0F011112
#define CS0BCR_default 0x36DA0400 // CG50
#define CS2BCR_default 0x36DA3400 // CG50
#define CS3BCR_default 0x36DB4400 // CG50
#define CS4BCR_default 0x36DB0400
#define CS5aBCR_default 0x17DF0400
#define CS5bBCR_default 0x17DF0400
#define CS6aBCR_default 0x34D30200
#define CS6bBCR_default 0x34D30200
#define CS0WCR_default 0x000003C0 // CG50
#define CS2WCR_default 0x000003C0 // CG50
#define CS3WCR_default 0x000024D1 // CG50
#define CS4WCR_default 0x00000540
#define CS5aWCR_default 0x000203C1
#define CS5bWCR_default 0x000203C1
#define CS6aWCR_default 0x000302C0
#define CS6bWCR_default 0x000302C0
#define SDCR_default 0x00000A08
#define PLL_32x 0b011111 //
#define PLL_26x 0b011001 //
#define PLL_16x 0b001111 // default
#define DIV_2 0b0000 // 1/2
#define DIV_4 0b0001 // 1/4
#define DIV_8 0b0010 // 1/8
#define DIV16 0b0011 // 1/16
#define WAIT18 0b1011
#define CPG SH7305_CPG
#define BSC SH7305_BSC
#define SDMR3_CL2 *(volatile uint8_t *)0xFEC15040 // SDMR2 Address
#define SDMR3_CL3 *(volatile uint8_t *)0xFEC15060 // SDMR2 Address
#include <gint/mpu/tmu.h>
#include <gint/timer.h>
/* Arrays of standard and extra timers */
static tmu_t *TMU = SH7305_TMU.TMU;
static etmu_t *ETMU = SH7305_ETMU;
/* TSTR register for standard timers */
static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
bool runningTimers[3]; // 9 timers : 3 TMUs + 6 ETMUs
uint32_t initTimersTCNT[3];
uint32_t initTimersTCOR[3];
uint32_t newTimersTCNT[3];
uint32_t newTimersTCOR[3];
int initPphi;
int newPphi;
static int getPphi_sh7305(void)
{
/* The meaning of the PLL setting on SH7305 differs from the
documentation of SH7224; the value must not be doubled. */
int pll = CPG.FRQCR.STC + 1;
/* The FLL ratio is the value of the setting, halved if SELXM=1 */
int fll = CPG.FLLFRQ.FLF;
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but on
the SH7305 it is 1 / (2^setting + 1). */
int divb = CPG.FRQCR.BFC;
int divi = CPG.FRQCR.IFC;
int divp = CPG.FRQCR.P1FC;
/* Deduce the input frequency of divider 1 */
int base = 32768;
if(CPG.PLLCR.FLLE) base *= fll;
if(CPG.PLLCR.PLLE) base *= pll;
return (base >> (divp + 1));
}
//We list all running timers and store this in a table (true/false)
void listTimerStatus( void )
{
for(int k=0;k<3; k++)
{
if(k < 3)
{
tmu_t *T = &TMU[k];
runningTimers[k]= (!T->TCR.UNIE && !(*TSTR & (1 << k)));
}
// else
// {
// etmu_t *T = &ETMU[k-3];
// runningTimers[k]= (!T->TCR.UNIE && !T->TSTR);
// }
}
}
// We get all TCNT and TCOR of currently used timers
// And store these value into the
void getInitialTimersParameters( void )
{
for(int k=0;k<3; k++)
{
if (runningTimers[k]==true)
{
if(k < 3)
{
tmu_t *T = &TMU[k];
initTimersTCNT[k]= T->TCNT;
initTimersTCOR[k]= T->TCOR;
}
// else
// {
// etmu_t *T = &ETMU[k-3];
// initTimersTCNT[k]= T->TCNT;
// initTimersTCOR[k]= T->TCOR;
// }
}
}
}
static int callback(void)
{
return TIMER_CONTINUE;
}
//We update the timers with the new TCNT and new TCOR
void updateNewTimersParameters( void )
{
for(int k=0;k<3; k++)
{
if (runningTimers[k]==true)
{
timer_stop( k );
if(k < 3)
{
tmu_t *T = &TMU[k];
T->TCNT = newTimersTCNT[k];
T->TCOR = newTimersTCOR[k];
}
// else
// {
// etmu_t *T = &ETMU[k-3];
// T->TCNT = newTimersTCNT[k];
// T->TCOR = newTimersTCOR[k];
// }
timer_start(k);
}
}
}
//We compute the new TCNT and new TCOR
void computeNewTimersParameters( int initPphi_f, int newPphi_f )
{
for(int k=0;k<3; k++)
{
if (runningTimers[k]==true)
{
newTimersTCNT[k] = (uint32_t) ((uint64_t) initTimersTCNT[k] * (uint64_t) newPphi_f / (uint64_t) initPphi_f);
newTimersTCOR[k] = (uint32_t) ((uint64_t) initTimersTCOR[k] * (uint64_t) newPphi_f / (uint64_t) initPphi_f);
}
}
}
static overclock_level current_clock_state = OC_Default;
bool overclock_config_changed = false;
void SetOCDefault( void )
{
BSC.CS0WCR.WR = WAIT18;
CPG.FLLFRQ.lword = FLLFRQ_default;
CPG.FRQCR.lword = FRQCR_default;
CPG.FRQCR.KICK = 1 ;
while((CPG.LSTATS & 1)!=0)
BSC.CS0BCR.lword = CS0BCR_default;
BSC.CS0WCR.lword = CS0WCR_default;
BSC.CS2BCR.lword = CS2BCR_default;
BSC.CS2WCR.lword = CS2WCR_default;
BSC.CS3BCR.lword = CS3BCR_default;
BSC.CS3WCR.lword = CS3WCR_default;
if ( BSC.CS3WCR.A3CL == 1 ) SDMR3_CL2 = 0; else SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = CS5aBCR_default;
BSC.CS5AWCR.lword = CS5aWCR_default;
}
void SetOCPtuneF2( void )
{
BSC.CS0WCR.WR = WAIT18;
CPG.FLLFRQ.lword = 0x00004000+900;
CPG.FRQCR.lword = (PLL_16x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8;
CPG.FRQCR.KICK = 1 ;
while((CPG.LSTATS & 1)!=0)
BSC.CS0BCR.lword = 0x24920400;
BSC.CS0WCR.lword = 0x00000340;
BSC.CS2BCR.lword = 0x24923400;
BSC.CS2WCR.lword = CS2WCR_default;
BSC.CS3BCR.lword = 0x24924400;
BSC.CS3WCR.lword = CS3WCR_default;
if ( BSC.CS3WCR.A3CL == 1 ) SDMR3_CL2 = 0; else SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = CS5aBCR_default;
BSC.CS5AWCR.lword = CS5aWCR_default;
}
void SetOCPtuneF3( void )
{
BSC.CS0WCR.WR = WAIT18;
CPG.FLLFRQ.lword = 0x00004000+900;
CPG.FRQCR.lword = (PLL_26x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8;
CPG.FRQCR.KICK = 1 ;
while((CPG.LSTATS & 1)!=0)
BSC.CS0BCR.lword = 0x24920400;
BSC.CS0WCR.lword = 0x00000240;
BSC.CS2BCR.lword = 0x24923400;
BSC.CS2WCR.lword = CS2WCR_default;
BSC.CS3BCR.lword = 0x24924400;
BSC.CS3WCR.lword = CS3WCR_default;
if ( BSC.CS3WCR.A3CL == 1 ) SDMR3_CL2 = 0; else SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = CS5aBCR_default;
BSC.CS5AWCR.lword = CS5aWCR_default;
}
void SetOCPtuneF4( void )
{
BSC.CS0WCR.WR = WAIT18;
CPG.FLLFRQ.lword = 0x00004000+900;
CPG.FRQCR.lword = (PLL_32x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_8<<8)+DIV16;
CPG.FRQCR.KICK = 1 ;
while((CPG.LSTATS & 1)!=0)
BSC.CS0BCR.lword = 0x24920400;
BSC.CS0WCR.lword = 0x000002C0;
BSC.CS2BCR.lword = 0x24923400;
BSC.CS2WCR.lword = CS2WCR_default;
BSC.CS3BCR.lword = 0x24924400;
BSC.CS3WCR.lword = CS3WCR_default;
BSC.CS5ABCR.lword = CS5aBCR_default;
BSC.CS5AWCR.lword = CS5aWCR_default;
}
void SetOCPtuneF5( void )
{
BSC.CS0WCR.WR = WAIT18;
CPG.FLLFRQ.lword = 0x00004000+900;
CPG.FRQCR.lword = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_8;
CPG.FRQCR.KICK = 1 ;
while((CPG.LSTATS & 1)!=0)
BSC.CS0BCR.lword = 0x24920400;
BSC.CS0WCR.lword = 0x00000440;
BSC.CS2BCR.lword = 0x24923400;
BSC.CS2WCR.lword = CS2WCR_default;
BSC.CS3BCR.lword = 0x24924400;
BSC.CS3WCR.lword = CS3WCR_default;
if ( BSC.CS3WCR.A3CL == 1 ) SDMR3_CL2 = 0; else SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = CS5aBCR_default;
BSC.CS5AWCR.lword = CS5aWCR_default;
}
// return 0 if no need to change
// return 1 if successful change
// return -1 on error
int clock_overclock( overclock_level level )
{
uint32_t count=0;
if(gint[HWCALC] == HWCALC_FXCG50 && current_clock_state!=level)
{
cpu_atomic_start();
listTimerStatus(); // we list the running timers
initPphi = getPphi_sh7305(); // we get the current P_Phi_f
getInitialTimersParameters(); // we collect the current TCNT and TCOR
if (level == OC_Default && current_clock_state!=OC_Default) SetOCDefault(), current_clock_state= OC_Default, overclock_config_changed=true;
if (level == OC_PtuneF2 && current_clock_state!=OC_PtuneF2) SetOCPtuneF2(), current_clock_state= OC_PtuneF2, overclock_config_changed=true;
if (level == OC_PtuneF3 && current_clock_state!=OC_PtuneF3) SetOCPtuneF3(), current_clock_state= OC_PtuneF3, overclock_config_changed=true;
if (level == OC_PtuneF4 && current_clock_state!=OC_PtuneF4) SetOCPtuneF4(), current_clock_state= OC_PtuneF4, overclock_config_changed=true;
if (level == OC_PtuneF5 && current_clock_state!=OC_PtuneF5) SetOCPtuneF5(), current_clock_state= OC_PtuneF5, overclock_config_changed=true;
newPphi = getPphi_sh7305(); // we get the new P_Phi_f after OC
computeNewTimersParameters( initPphi, newPphi ); // we compute the new TCNT and TCOR as per the new frequency
updateNewTimersParameters(); // we adjust the timers accordingly
cpu_atomic_end();
return 1;
}
else return 0;
}