273 lines
6.7 KiB
C
273 lines
6.7 KiB
C
#include <clock.h>
|
|
#include <timer.h>
|
|
#include <modules/timer.h>
|
|
#include <rtc.h>
|
|
#include <stddef.h>
|
|
#include <mpu.h>
|
|
#include <stdint.h>
|
|
|
|
static clock_config_t conf = {
|
|
.FLL = -1, .PLL = -1,
|
|
.Bphi_div1 = -1, .Iphi_div1 = -1, .Pphi_div1 = -1,
|
|
.CKIO_f = -1,
|
|
.Bphi_f = -1, .Iphi_f = -1, .Pphi_f = -1
|
|
};
|
|
|
|
/*
|
|
clock_setting()
|
|
Returns the P_phi / 4 timer setting that will last for the given time.
|
|
Several units can be used. Be aware that the result is approximate, and
|
|
very high frequencies or very short delays will yield important errors.
|
|
*/
|
|
uint32_t clock_setting(int duration, enum ClockUnit unit)
|
|
{
|
|
if(conf.Pphi_f <= 0) return 0xffffffff;
|
|
uint64_t f = conf.Pphi_f >> 2;
|
|
uint64_t result;
|
|
|
|
switch(unit)
|
|
{
|
|
case Clock_us:
|
|
result = (duration * f) / 1000000;
|
|
break;
|
|
case Clock_ms:
|
|
result = (duration * f) / 1000;
|
|
break;
|
|
case Clock_s:
|
|
result = (duration * f);
|
|
break;
|
|
|
|
case Clock_Hz:
|
|
result = f / duration;
|
|
break;
|
|
case Clock_kHz:
|
|
result = f / (duration * 1000);
|
|
break;
|
|
case Clock_MHz:
|
|
result = f / (duration * 1000000);
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return (result > 0xffffffff) ? (0xffffffff) : (result);
|
|
}
|
|
|
|
/*
|
|
clock_config()
|
|
Returns a copy of the clock configuration.
|
|
*/
|
|
clock_config_t clock_config(void)
|
|
{
|
|
return conf;
|
|
}
|
|
|
|
/*
|
|
sleep()
|
|
Sleeps until an interrupt is accepted.
|
|
*/
|
|
void sleep(void)
|
|
{
|
|
__asm__(
|
|
"sleep"
|
|
);
|
|
}
|
|
|
|
static void sleep_callback(void *arg)
|
|
{
|
|
int *flag = arg;
|
|
*flag = 1;
|
|
}
|
|
|
|
/*
|
|
sleep_ms()
|
|
Sleeps for the given number of milliseconds using a virtual timer.
|
|
*/
|
|
void sleep_ms(int ms_delay)
|
|
{
|
|
volatile int sleep_done = 0;
|
|
|
|
timer_t *timer = timer_create(ms_delay, 1);
|
|
timer_attach(timer, sleep_callback, (void *)&sleep_done);
|
|
timer_start(timer);
|
|
|
|
while(!sleep_done) sleep();
|
|
}
|
|
|
|
/*
|
|
sleep_us()
|
|
Sleeps for the given number of microseconds using the hardware timer
|
|
timer_user.
|
|
*/
|
|
void sleep_us(int us_delay)
|
|
{
|
|
volatile int sleep_done = 0;
|
|
const uint32_t constant = clock_setting(us_delay, Clock_us);
|
|
|
|
timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1);
|
|
timer_attach(timer, sleep_callback, (void *)&sleep_done);
|
|
timer_start(timer);
|
|
|
|
while(!sleep_done) sleep();
|
|
}
|
|
|
|
|
|
|
|
//---
|
|
// Clock frequency measurements -- Public API.
|
|
//---
|
|
|
|
// Indicates whether the measurements are finished.
|
|
static volatile int clock_measure_done = 0;
|
|
// Once again SH7705 and SH7305 need different methods...
|
|
static timer_t *htimer_7705 = NULL;
|
|
static int cb_id_7705 = -1;
|
|
static void clock_measure_7705(void);
|
|
static void clock_compute_7305(void);
|
|
|
|
/*
|
|
clock_measure()
|
|
Begins the frequency measurements. The measurements will end
|
|
automatically. While doing measurements, do not use the RTC interrupt
|
|
or the user timer.
|
|
Call clock_measure_end() to wait until the measurements are finished.
|
|
It is possible to execute code during the measurements, so that less
|
|
time is spent.
|
|
*/
|
|
void clock_measure(void)
|
|
{
|
|
// On SH7705 we cannot have the value of CKIO simply, so we measure
|
|
// P_phi using a timer/RTC combination, and we deduce CKIO.
|
|
if(isSH3())
|
|
{
|
|
htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4,
|
|
1);
|
|
cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0);
|
|
}
|
|
|
|
// On SH7305, assuming clock mode 3, we can compute the clock
|
|
// frequencies because we know that RTC_CLK oscillates at 32768 Hz.
|
|
else
|
|
{
|
|
clock_compute_7305();
|
|
clock_measure_done = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
clock_measure_end()
|
|
Waits until the measurements are finished. This may be immediate.
|
|
*/
|
|
void clock_measure_end(void)
|
|
{
|
|
while(!clock_measure_done) sleep();
|
|
}
|
|
|
|
//---
|
|
// Clock frequency measurements -- SH7305.
|
|
//---
|
|
|
|
/*
|
|
clock_compute_7305()
|
|
Computes the clock frequencies according to the CPG parameters.
|
|
*/
|
|
static void clock_compute_7305(void)
|
|
{
|
|
volatile unsigned int *FRQCRA = (void *)0xa4150000;
|
|
volatile unsigned int *PLLCR = (void *)0xa4150024;
|
|
volatile unsigned int *FLLFRQ = (void *)0xa4150050;
|
|
|
|
// Surely the documentation of SH7724 does not meet the specification
|
|
// of SH7305 for the PLL setting, because the register accepts other
|
|
// values than the ones specified for SH7724. The relation given by
|
|
// Sentaro21 (thanks again!) yields good results.
|
|
int pll = (*FRQCRA >> 24) & 0x3f; // Raw setting
|
|
pll = pll + 1; // Resulting multiplier
|
|
conf.PLL = pll;
|
|
|
|
// This one is simpler. The FLL ratio is actually the setting value.
|
|
int fll = *FLLFRQ & 0x7ff; // Raw setting = multiplier
|
|
if(*FLLFRQ & (1 << 14)) fll >>= 1; // Halve-output flag
|
|
conf.FLL = fll;
|
|
|
|
// The divider1 ratios are NOT those of SH7724. The relation between
|
|
// the values below and the divider ratios is given by Sentaro21
|
|
// (thanks to him!) and satisfies ratio = 1 / (2 ** (setting + 1)).
|
|
int div1_bphi = (*FRQCRA >> 8) & 0xf;
|
|
int div1_iphi = (*FRQCRA >> 20) & 0xf;
|
|
int div1_pphi = (*FRQCRA ) & 0xf;
|
|
|
|
conf.Bphi_div1 = 1 << (div1_bphi + 1);
|
|
conf.Iphi_div1 = 1 << (div1_iphi + 1);
|
|
conf.Pphi_div1 = 1 << (div1_pphi + 1);
|
|
|
|
// Computing the frequency of the signal, which is input to divider 1.
|
|
int base = 32768;
|
|
if(*PLLCR & (1 << 12)) base *= fll;
|
|
if(*PLLCR & (1 << 14)) base *= pll;
|
|
|
|
conf.RTCCLK_f = 32768;
|
|
conf.Bphi_f = base >> (div1_bphi + 1);
|
|
conf.Iphi_f = base >> (div1_iphi + 1);
|
|
conf.Pphi_f = base >> (div1_pphi + 1);
|
|
}
|
|
|
|
//---
|
|
// Clock frequency measurements -- SH7705.
|
|
//---
|
|
|
|
/*
|
|
clock_measure_7705_finalize()
|
|
Given the number of P_phi / 4 timer ticks elapsed between two RTC
|
|
256 Hz interrupts, determines the clock configuration.
|
|
*/
|
|
static void clock_measure_7705_finalize(uint32_t elapsed)
|
|
{
|
|
volatile unsigned int *FRQCR = (void *)0xffffff80;
|
|
|
|
conf.Pphi_f = elapsed * 4 * 256;
|
|
if(conf.Pphi_f <= 0) return;
|
|
|
|
conf.PLL1 = ((*FRQCR >> 8) & 0x03) + 1;
|
|
conf.PLL2 = -1;
|
|
|
|
conf.Bphi_div1 = 0;
|
|
conf.Iphi_div1 = ((*FRQCR >> 4) & 0x03) + 1;
|
|
conf.Pphi_div1 = ((*FRQCR ) & 0x03) + 1;
|
|
|
|
conf.CKIO_f = (conf.Pphi_f * conf.Pphi_div1) / conf.PLL1;
|
|
conf.Bphi_f = conf.CKIO_f;
|
|
conf.Iphi_f = (conf.CKIO_f * conf.PLL1) / conf.Iphi_div1;
|
|
}
|
|
|
|
/*
|
|
clock_measure_7705_callback()
|
|
Starts measurements. Measurements will end automatically. Do not use
|
|
RTC interrupt or the user timer will doing measurements.
|
|
Call clock_measure_end() when you need to use those, to ensure
|
|
measurements are finished.
|
|
*/
|
|
static void clock_measure_7705_callback(void)
|
|
{
|
|
timer_stop(htimer_7705);
|
|
rtc_cb_end(cb_id_7705);
|
|
|
|
uint32_t elapsed = 0xffffffff - TMU.timers[timer_user]->TCNT;
|
|
clock_measure_7705_finalize(elapsed);
|
|
clock_measure_done = 1;
|
|
}
|
|
|
|
/*
|
|
clock_measure_7705()
|
|
Programs the clock measurements. We need to have the user timer and the
|
|
RTC synchronized for this operation, so we wait for an RTC interrupt
|
|
and we prepare the timer beforehand to avoid losing processor time in
|
|
configuring the registers.
|
|
*/
|
|
static void clock_measure_7705(void)
|
|
{
|
|
timer_start(htimer_7705);
|
|
rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback);
|
|
}
|