2018-08-01 20:41:36 +02:00
|
|
|
//---
|
|
|
|
// gint:tmu - Timer operation
|
|
|
|
//---
|
|
|
|
|
|
|
|
#include <gint/timer.h>
|
|
|
|
#include <gint/drivers.h>
|
2021-04-23 18:50:20 +02:00
|
|
|
#include <gint/drivers/states.h>
|
2018-08-19 17:11:37 +02:00
|
|
|
#include <gint/clock.h>
|
2020-06-20 17:18:51 +02:00
|
|
|
#include <gint/intc.h>
|
2021-04-27 14:29:38 +02:00
|
|
|
#include <gint/cpu.h>
|
2019-07-16 21:32:20 +02:00
|
|
|
#include <gint/mpu/tmu.h>
|
2020-06-20 22:45:46 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Callbacks for all timers */
|
|
|
|
gint_call_t tmu_callbacks[9];
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2020-05-10 14:03:41 +02:00
|
|
|
/* Arrays of standard and extra timers */
|
2020-07-10 16:36:05 +02:00
|
|
|
static tmu_t *TMU = SH7305_TMU.TMU;
|
|
|
|
static etmu_t *ETMU = SH7305_ETMU;
|
2021-04-27 14:29:38 +02:00
|
|
|
|
2018-08-01 20:41:36 +02:00
|
|
|
/* TSTR register for standard timers */
|
2020-07-10 16:36:05 +02:00
|
|
|
static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Shortcut to set registers that are slow to update */
|
|
|
|
#define set(lval, rval) do(lval = rval); while(rval != lval)
|
|
|
|
|
2018-08-01 20:41:36 +02:00
|
|
|
//---
|
2020-06-20 22:45:46 +02:00
|
|
|
// Local functions
|
2018-08-01 20:41:36 +02:00
|
|
|
//---
|
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
/* conf(): Configure a fixed timer */
|
2021-04-27 14:29:38 +02:00
|
|
|
static void conf(int id, uint32_t delay, int clock, gint_call_t call)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2019-07-18 21:18:36 +02:00
|
|
|
if(id < 3)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
|
|
|
/* Refuse to setup timers that are already in use */
|
2020-05-10 14:03:41 +02:00
|
|
|
tmu_t *T = &TMU[id];
|
2020-06-20 22:45:46 +02:00
|
|
|
if(T->TCR.UNIE || *TSTR & (1 << id)) return;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2020-05-10 14:03:41 +02:00
|
|
|
/* Configure the counter, clear interrupt flag*/
|
|
|
|
T->TCOR = delay;
|
|
|
|
T->TCNT = delay;
|
|
|
|
T->TCR.TPSC = clock;
|
2021-04-27 14:29:38 +02:00
|
|
|
set(T->TCR.UNF, 0);
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-18 21:18:36 +02:00
|
|
|
/* Enable interrupt and count on rising edge (SH7705) */
|
2020-05-10 14:03:41 +02:00
|
|
|
T->TCR.UNIE = 1;
|
|
|
|
T->TCR.CKEG = 0;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-10 14:03:41 +02:00
|
|
|
etmu_t *T = &ETMU[id-3];
|
2020-06-20 22:45:46 +02:00
|
|
|
if(T->TCR.UNIE) return;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* No clock input and clock edge here */
|
|
|
|
set(T->TCR.UNF, 0);
|
|
|
|
set(T->TCOR, delay);
|
|
|
|
set(T->TCNT, delay);
|
2020-05-10 14:03:41 +02:00
|
|
|
T->TCR.UNIE = 1;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
tmu_callbacks[id] = call;
|
2020-06-20 22:45:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* matches(): Check if a timer matches the provided specification and delay */
|
|
|
|
static int matches(int id, int spec, uint32_t delay)
|
|
|
|
{
|
|
|
|
/* A specific idea only matches the exact timer */
|
|
|
|
if(spec >= 0) return id == (spec & 0xf);
|
|
|
|
/* TIMER_ANY always matches ETMU only for delays at least 100 µs */
|
|
|
|
if(spec == TIMER_ANY) return (id < 3 || delay >= 100);
|
|
|
|
/* TIMER_TMU and TIMER_ETMU match as you'd expect */
|
|
|
|
if(spec == TIMER_TMU) return (id < 3);
|
|
|
|
if(spec == TIMER_ETMU) return (id >= 3);
|
|
|
|
/* Default is not matching */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* available(): Check if a timer is available (UNIE cleared, not running) */
|
|
|
|
static int available(int id)
|
|
|
|
{
|
2021-04-27 14:29:38 +02:00
|
|
|
if(id >= timer_count()) return 0;
|
2020-06-20 22:45:46 +02:00
|
|
|
|
|
|
|
if(id < 3)
|
|
|
|
{
|
|
|
|
tmu_t *T = &TMU[id];
|
|
|
|
return !T->TCR.UNIE && !(*TSTR & (1 << id));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
etmu_t *T = &ETMU[id-3];
|
|
|
|
return !T->TCR.UNIE && !T->TSTR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* stop_callback(): Empty callback that stops the timer */
|
|
|
|
static int stop_callback(void)
|
|
|
|
{
|
|
|
|
return TIMER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---
|
|
|
|
// Timer API
|
|
|
|
//---
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
int timer_configure(int spec, uint64_t delay, gint_call_t call)
|
2020-06-20 22:45:46 +02:00
|
|
|
{
|
|
|
|
int clock = 0;
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Default behavior for the callback */
|
|
|
|
if(!call.function) call = GINT_CALL(stop_callback);
|
2020-06-20 22:45:46 +02:00
|
|
|
|
|
|
|
/* Find a matching timer, starting from the slowest timers with the
|
|
|
|
smallest interrupt priorities all the way up to TMU0 */
|
|
|
|
for(int id = timer_count() - 1; id >= 0; id--)
|
|
|
|
{
|
|
|
|
if(!matches(id, spec, delay) || !available(id)) continue;
|
|
|
|
|
|
|
|
/* If ID is a TMU, choose a timer prescaler. Assuming the worst
|
|
|
|
running Pphi of ~48 MHz, select the finest resolution that
|
|
|
|
allows the requested delay to be represented. */
|
|
|
|
if(id < 3 && spec >= 0)
|
|
|
|
{
|
|
|
|
/* Explicit timers with clock in the specification */
|
|
|
|
clock = (spec >> 4) & 0xf;
|
|
|
|
}
|
|
|
|
else if(id < 3)
|
|
|
|
{
|
|
|
|
uint64_t sec = 1000000;
|
|
|
|
|
|
|
|
/* Pphi/4 until 350 seconds */
|
|
|
|
if(delay <= 350 * sec) clock = TIMER_Pphi_4;
|
|
|
|
/* Pphi/16 until 1430 seconds */
|
|
|
|
else if(delay <= 1430 * sec) clock = TIMER_Pphi_16;
|
|
|
|
/* Pphi/64 until 5720 seconds */
|
|
|
|
else if(delay <= 5720 * sec) clock = TIMER_Pphi_64;
|
|
|
|
/* Pphi/256 otherwise */
|
|
|
|
else clock = TIMER_Pphi_256;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the delay constant for that timer and clock */
|
|
|
|
if(spec < 0) delay = timer_delay(id, delay, clock);
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
conf(id, delay, clock, call);
|
2020-06-20 22:45:46 +02:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* timer_delay() - compute a delay constant from a duration in seconds */
|
2020-06-20 22:45:46 +02:00
|
|
|
uint32_t timer_delay(int id, uint64_t delay_us, int clock)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2020-06-20 22:45:46 +02:00
|
|
|
uint64_t freq;
|
|
|
|
|
|
|
|
if(id < 3)
|
|
|
|
{
|
|
|
|
const clock_frequency_t *cpg = clock_freq();
|
|
|
|
freq = cpg->Pphi_f;
|
|
|
|
if(clock == TIMER_Pphi_4) freq >>= 2;
|
|
|
|
if(clock == TIMER_Pphi_16) freq >>= 4;
|
|
|
|
if(clock == TIMER_Pphi_64) freq >>= 6;
|
|
|
|
if(clock == TIMER_Pphi_256) freq >>= 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ETMU all run on TCLK at 32768 Hz */
|
|
|
|
freq = 32768;
|
|
|
|
}
|
2018-08-19 17:11:37 +02:00
|
|
|
|
2019-02-21 20:58:38 +01:00
|
|
|
uint64_t product = freq * delay_us;
|
2018-08-01 20:41:36 +02:00
|
|
|
return product / 1000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* timer_control() - start or stop a timer
|
2019-07-18 21:18:36 +02:00
|
|
|
@id Timer ID to configure
|
2018-08-01 20:41:36 +02:00
|
|
|
@state 0 to start the timer, 1 to stop it (nothing else!) */
|
2019-07-18 21:18:36 +02:00
|
|
|
static void timer_control(int id, int state)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2019-07-18 21:18:36 +02:00
|
|
|
if(id < 3) *TSTR = (*TSTR | (1 << id)) ^ (state << id);
|
2020-05-10 14:03:41 +02:00
|
|
|
else ETMU[id-3].TSTR = state ^ 1;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* timer_start() - start a configured timer */
|
2019-07-18 21:18:36 +02:00
|
|
|
void timer_start(int id)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2019-07-18 21:18:36 +02:00
|
|
|
timer_control(id, 0);
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
2018-08-19 17:11:37 +02:00
|
|
|
/* timer_reload() - change a timer's delay constant for next interrupts */
|
2019-07-18 21:18:36 +02:00
|
|
|
void timer_reload(int id, uint32_t delay)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2020-05-10 14:03:41 +02:00
|
|
|
if(id < 3) TMU[id].TCOR = delay;
|
|
|
|
else ETMU[id-3].TCOR = delay;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* timer_pause() - stop a running timer */
|
2019-07-18 21:18:36 +02:00
|
|
|
void timer_pause(int id)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2019-07-18 21:18:36 +02:00
|
|
|
timer_control(id, 1);
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
2020-05-10 14:03:41 +02:00
|
|
|
/* timer_stop() - stop and free a timer */
|
kernel: dynamic loading of GMAPPED functions to user RAM
This commit introduces a large architectural change. Unlike previous
models of the fx-9860G series, the G-III models have a new user RAM
address different from 8801c000. The purpose of this change is to
dynamically load GMAPPED functions to this address by querying the TLB,
and call them through a function pointer whose address is determined
when loading.
Because of the overhead of using a function pointer in both assembly and
C code, changes have been made to avoid GMAPPED functions altogether.
Current, only cpu_setVBR() and gint_inth_callback() are left, the second
being used specifically to enable TLB misses when needed.
* Add a .gint.mappedrel section for the function pointers holding
addresses to GMAPPED functions; add function pointers for
cpu_setVBR() and gint_inth_callback()
* Move rram to address 0 instead of the hardcoded 0x8801c000
* Load GMAPPED functions at their linked address + the physical address
user RAM is mapped, to and compute their function pointers
* Remove the GMAPPED macro since no user function needs it anymore
* Add section flags "ax" (code) or "aw" (data) to every custom .section
in assembler code, as they default to unpredictable values that can
cause the section to be marked NOLOAD by the linker
* Update the main kernel, TMU, ETMU and RTC interrupt handlers to use
the new indirect calling method
This is made possible by new MMU functions giving direct access to the
physical area behind any virtualized page.
* Add an mmu_translate() function to query the TLB
* Add an mmu_uram() function to access user RAM from P1
The exception catching mechanism has been modified to avoid the use of
GMAPPED functions altogether.
* Set SR.BL=0 and SR.IMASK=15 before calling exception catchers
* Move gint_exc_skip() to normal text ROM
* Also fix registers not being popped off the stack before a panic
The timer drivers have also been modified to avoid GMAPPED functions.
* Invoke timer_stop() through gint_inth_callback() and move it to ROM
* Move and expand the ETMU driver to span 3 blocks at 0xd00 (ETMU4)
* Remove the timer_clear() function by inlining it into the ETMU handler
(TCR is provided within the storage block of each timer)
* Also split src/timer/inth.s into src/timer/inth-{tmu,etmu}.s
Additionally, VBR addresses are now determined at runtime to further
reduce hardcoded memory layout addresses in the linker script.
* Determine fx-9860G VBR addresses dynamically from mmu_uram()
* Determine fx-CG 50 VBR addresses dynamically from mmu_uram()
* Remove linker symbols for VBR addresses
Comments and documentation have been updated throughout the code to
reflect the changes.
2020-09-17 14:48:54 +02:00
|
|
|
void timer_stop(int id)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
|
|
|
/* Stop the timer and disable UNIE to indicate that it's free */
|
2019-07-18 21:18:36 +02:00
|
|
|
timer_pause(id);
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-18 21:18:36 +02:00
|
|
|
if(id < 3)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2020-05-10 14:03:41 +02:00
|
|
|
TMU[id].TCR.UNIE = 0;
|
2021-11-15 06:42:11 +01:00
|
|
|
TMU[id].TCR.UNF = 0;
|
2020-05-10 14:03:41 +02:00
|
|
|
TMU[id].TCOR = 0xffffffff;
|
|
|
|
TMU[id].TCNT = 0xffffffff;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Extra timers generate interrupts when TCNT=0 even if TSTR=0.
|
|
|
|
We always keep TCOR/TCNT to non-zero values when idle. */
|
2020-05-10 14:03:41 +02:00
|
|
|
etmu_t *T = &ETMU[id-3];
|
|
|
|
T->TCR.UNIE = 0;
|
2021-04-27 14:29:38 +02:00
|
|
|
set(T->TCOR, 0xffffffff);
|
|
|
|
set(T->TCNT, 0xffffffff);
|
|
|
|
set(T->TCR.UNF, 0);
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-21 14:49:34 +02:00
|
|
|
/* timer_wait(): Wait for a timer to stop */
|
2020-06-17 11:36:05 +02:00
|
|
|
void timer_wait(int id)
|
|
|
|
{
|
|
|
|
if(id < 3)
|
|
|
|
{
|
|
|
|
tmu_t *T = &TMU[id];
|
2020-10-21 14:49:34 +02:00
|
|
|
/* Sleep only if an interrupt will be there to wake us up */
|
2020-06-17 11:36:05 +02:00
|
|
|
while(*TSTR & (1 << id)) if(T->TCR.UNIE) sleep();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-06-18 20:20:55 +02:00
|
|
|
etmu_t *T = &ETMU[id-3];
|
2020-06-17 11:36:05 +02:00
|
|
|
while(T->TSTR) if(T->TCR.UNIE) sleep();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 06:42:11 +01:00
|
|
|
/* timer_spinwait(): Start a timer and actively wait for UNF */
|
2020-10-21 14:49:34 +02:00
|
|
|
void timer_spinwait(int id)
|
|
|
|
{
|
|
|
|
if(id < 3)
|
|
|
|
{
|
|
|
|
tmu_t *T = &TMU[id];
|
2021-11-15 06:42:11 +01:00
|
|
|
T->TCR.UNIE = 0;
|
|
|
|
timer_start(id);
|
2020-10-21 14:49:34 +02:00
|
|
|
while(!T->TCR.UNF) {}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
etmu_t *T = &ETMU[id-3];
|
2021-11-15 06:42:11 +01:00
|
|
|
set(T->TCR.UNIE, 0);
|
|
|
|
timer_start(id);
|
2020-10-21 14:49:34 +02:00
|
|
|
while(!T->TCR.UNF) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-15 20:16:03 +02:00
|
|
|
//---
|
|
|
|
// Overclock adjustment
|
|
|
|
//---
|
|
|
|
|
|
|
|
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi_0)
|
|
|
|
{
|
|
|
|
uint64_t new_Pphi = new_Pphi_0;
|
|
|
|
|
|
|
|
for(int id = 0; id < 3; id++)
|
|
|
|
{
|
|
|
|
tmu_t *T = &TMU[id];
|
|
|
|
/* Skip timers that are not running */
|
|
|
|
if(T->TCNT == 0xffffffff && T->TCOR == 0xffffffff)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* For libprof: keep timers with max TCOR as they are */
|
|
|
|
if(T->TCOR != 0xffffffff) {
|
|
|
|
T->TCOR = ((uint64_t)T->TCOR * new_Pphi) / old_Pphi;
|
|
|
|
}
|
|
|
|
T->TCNT = ((uint64_t)T->TCNT * new_Pphi) / old_Pphi;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 20:58:38 +01:00
|
|
|
//---
|
2021-04-27 14:29:38 +02:00
|
|
|
// Deprecated API
|
2019-02-21 20:58:38 +01:00
|
|
|
//---
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
#undef timer_setup
|
|
|
|
int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
va_start(va, function);
|
|
|
|
uint32_t arg = va_arg(va, uint32_t);
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
return timer_configure(spec, delay, GINT_CALL(function.v, arg));
|
|
|
|
}
|
|
|
|
|
2020-06-20 22:45:46 +02:00
|
|
|
int timer_timeout(void volatile *arg)
|
2019-02-21 20:58:38 +01:00
|
|
|
{
|
2020-06-20 22:45:46 +02:00
|
|
|
int volatile *x = arg;
|
|
|
|
if(x) (*x)++;
|
|
|
|
return TIMER_STOP;
|
2019-07-16 21:32:20 +02:00
|
|
|
}
|
|
|
|
|
2018-08-01 20:41:36 +02:00
|
|
|
//---
|
|
|
|
// Driver initialization
|
|
|
|
//---
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Interrupt handlers for standard timers (3 gates) */
|
2019-07-18 21:18:36 +02:00
|
|
|
extern void inth_tmu(void);
|
|
|
|
/* Interrupt handlers for extra timers */
|
kernel: dynamic loading of GMAPPED functions to user RAM
This commit introduces a large architectural change. Unlike previous
models of the fx-9860G series, the G-III models have a new user RAM
address different from 8801c000. The purpose of this change is to
dynamically load GMAPPED functions to this address by querying the TLB,
and call them through a function pointer whose address is determined
when loading.
Because of the overhead of using a function pointer in both assembly and
C code, changes have been made to avoid GMAPPED functions altogether.
Current, only cpu_setVBR() and gint_inth_callback() are left, the second
being used specifically to enable TLB misses when needed.
* Add a .gint.mappedrel section for the function pointers holding
addresses to GMAPPED functions; add function pointers for
cpu_setVBR() and gint_inth_callback()
* Move rram to address 0 instead of the hardcoded 0x8801c000
* Load GMAPPED functions at their linked address + the physical address
user RAM is mapped, to and compute their function pointers
* Remove the GMAPPED macro since no user function needs it anymore
* Add section flags "ax" (code) or "aw" (data) to every custom .section
in assembler code, as they default to unpredictable values that can
cause the section to be marked NOLOAD by the linker
* Update the main kernel, TMU, ETMU and RTC interrupt handlers to use
the new indirect calling method
This is made possible by new MMU functions giving direct access to the
physical area behind any virtualized page.
* Add an mmu_translate() function to query the TLB
* Add an mmu_uram() function to access user RAM from P1
The exception catching mechanism has been modified to avoid the use of
GMAPPED functions altogether.
* Set SR.BL=0 and SR.IMASK=15 before calling exception catchers
* Move gint_exc_skip() to normal text ROM
* Also fix registers not being popped off the stack before a panic
The timer drivers have also been modified to avoid GMAPPED functions.
* Invoke timer_stop() through gint_inth_callback() and move it to ROM
* Move and expand the ETMU driver to span 3 blocks at 0xd00 (ETMU4)
* Remove the timer_clear() function by inlining it into the ETMU handler
(TCR is provided within the storage block of each timer)
* Also split src/timer/inth.s into src/timer/inth-{tmu,etmu}.s
Additionally, VBR addresses are now determined at runtime to further
reduce hardcoded memory layout addresses in the linker script.
* Determine fx-9860G VBR addresses dynamically from mmu_uram()
* Determine fx-CG 50 VBR addresses dynamically from mmu_uram()
* Remove linker symbols for VBR addresses
Comments and documentation have been updated throughout the code to
reflect the changes.
2020-09-17 14:48:54 +02:00
|
|
|
extern void inth_etmu4(void);
|
2019-07-16 21:32:20 +02:00
|
|
|
extern void inth_etmux(void);
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
static void constructor(void)
|
2018-08-19 17:11:37 +02:00
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
if(isSH3())
|
|
|
|
{
|
|
|
|
TMU = SH7705_TMU.TMU;
|
|
|
|
ETMU = SH7705_ETMU;
|
|
|
|
TSTR = &SH7705_TMU.TSTR;
|
|
|
|
}
|
2018-08-19 17:11:37 +02:00
|
|
|
}
|
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
static void configure(void)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2020-05-10 14:03:41 +02:00
|
|
|
uint16_t etmu_event[6] = { 0x9e0, 0xc20, 0xc40, 0x900, 0xd00, 0xfa0 };
|
|
|
|
*TSTR = 0;
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Install the TMU handlers and adjust the TCR0 value on SH3 */
|
|
|
|
void *h = intc_handler(0x400, inth_tmu, 96);
|
|
|
|
if(isSH3()) *(void volatile **)(h + 88) = &TMU[0].TCR;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Clear all timers */
|
|
|
|
for(int i = 0; i < 3; i++)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-27 14:29:38 +02:00
|
|
|
set(TMU[i].TCR.word, 0);
|
|
|
|
TMU[i].TCOR = 0xffffffff;
|
|
|
|
TMU[i].TCNT = 0xffffffff;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
2021-04-27 14:29:38 +02:00
|
|
|
for(int i = 3; i < timer_count(); i++)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-27 14:29:38 +02:00
|
|
|
etmu_t *T = &ETMU[i-3];
|
2020-05-10 14:03:41 +02:00
|
|
|
T->TSTR = 0;
|
2021-04-27 14:29:38 +02:00
|
|
|
set(T->TCOR, 0xffffffff);
|
|
|
|
set(T->TCNT, 0xffffffff);
|
|
|
|
set(T->TCR.byte, 0);
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Install the ETMU4 handler, which contains the logic for ETMUs */
|
|
|
|
void *h4 = intc_handler(etmu_event[4], inth_etmu4, 96);
|
|
|
|
|
|
|
|
/* Install the other ETMU handlers, and set their parameters */
|
kernel: dynamic loading of GMAPPED functions to user RAM
This commit introduces a large architectural change. Unlike previous
models of the fx-9860G series, the G-III models have a new user RAM
address different from 8801c000. The purpose of this change is to
dynamically load GMAPPED functions to this address by querying the TLB,
and call them through a function pointer whose address is determined
when loading.
Because of the overhead of using a function pointer in both assembly and
C code, changes have been made to avoid GMAPPED functions altogether.
Current, only cpu_setVBR() and gint_inth_callback() are left, the second
being used specifically to enable TLB misses when needed.
* Add a .gint.mappedrel section for the function pointers holding
addresses to GMAPPED functions; add function pointers for
cpu_setVBR() and gint_inth_callback()
* Move rram to address 0 instead of the hardcoded 0x8801c000
* Load GMAPPED functions at their linked address + the physical address
user RAM is mapped, to and compute their function pointers
* Remove the GMAPPED macro since no user function needs it anymore
* Add section flags "ax" (code) or "aw" (data) to every custom .section
in assembler code, as they default to unpredictable values that can
cause the section to be marked NOLOAD by the linker
* Update the main kernel, TMU, ETMU and RTC interrupt handlers to use
the new indirect calling method
This is made possible by new MMU functions giving direct access to the
physical area behind any virtualized page.
* Add an mmu_translate() function to query the TLB
* Add an mmu_uram() function to access user RAM from P1
The exception catching mechanism has been modified to avoid the use of
GMAPPED functions altogether.
* Set SR.BL=0 and SR.IMASK=15 before calling exception catchers
* Move gint_exc_skip() to normal text ROM
* Also fix registers not being popped off the stack before a panic
The timer drivers have also been modified to avoid GMAPPED functions.
* Invoke timer_stop() through gint_inth_callback() and move it to ROM
* Move and expand the ETMU driver to span 3 blocks at 0xd00 (ETMU4)
* Remove the timer_clear() function by inlining it into the ETMU handler
(TCR is provided within the storage block of each timer)
* Also split src/timer/inth.s into src/timer/inth-{tmu,etmu}.s
Additionally, VBR addresses are now determined at runtime to further
reduce hardcoded memory layout addresses in the linker script.
* Determine fx-9860G VBR addresses dynamically from mmu_uram()
* Determine fx-CG 50 VBR addresses dynamically from mmu_uram()
* Remove linker symbols for VBR addresses
Comments and documentation have been updated throughout the code to
reflect the changes.
2020-09-17 14:48:54 +02:00
|
|
|
for(int i = 3; i < timer_count(); i++) if(i != 7)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
void *h = intc_handler(etmu_event[i-3], inth_etmux, 32);
|
2019-07-16 21:32:20 +02:00
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
/* Distance from VBR handler to ETMU4, used to jump */
|
|
|
|
*(uint32_t *)(h + 20) += (uint32_t)h4 - cpu_getVBR();
|
|
|
|
/* Timer ID, used for timer_stop() after the callback */
|
|
|
|
*(uint16_t *)(h + 18) = i;
|
|
|
|
/* Pointer to the callback */
|
|
|
|
*(gint_call_t **)(h + 24) += i;
|
|
|
|
/* TCR address to acknowledge the interrupt */
|
|
|
|
*(void volatile **)(h + 28) = &ETMU[i-3].TCR;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
2020-06-20 17:18:51 +02:00
|
|
|
intc_priority(INTC_TMU_TUNI0, 13);
|
|
|
|
intc_priority(INTC_TMU_TUNI1, 11);
|
|
|
|
intc_priority(INTC_TMU_TUNI2, 9);
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/* Enable the extra TMUs at level 7 */
|
2020-06-20 17:18:51 +02:00
|
|
|
intc_priority(INTC_ETMU_TUNI0, 7);
|
|
|
|
if(isSH4())
|
2018-08-19 17:11:37 +02:00
|
|
|
{
|
2020-06-20 17:18:51 +02:00
|
|
|
intc_priority(INTC_ETMU_TUNI1, 7);
|
|
|
|
intc_priority(INTC_ETMU_TUNI2, 7);
|
|
|
|
intc_priority(INTC_ETMU_TUNI3, 7);
|
|
|
|
intc_priority(INTC_ETMU_TUNI4, 7);
|
|
|
|
intc_priority(INTC_ETMU_TUNI5, 7);
|
2018-08-19 17:11:37 +02:00
|
|
|
}
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---
|
2021-04-23 18:50:20 +02:00
|
|
|
// State and driver metadata
|
2018-08-01 20:41:36 +02:00
|
|
|
//---
|
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
static void hsave(tmu_state_t *s)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
s->TSTR = *TSTR;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < 3; i++)
|
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
s->t[i].TCOR = TMU[i].TCOR;
|
|
|
|
s->t[i].TCNT = TMU[i].TCNT;
|
|
|
|
s->t[i].TCR = TMU[i].TCR.word;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
2020-07-10 16:05:11 +02:00
|
|
|
for(int i = 3; i < timer_count(); i++)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
struct tmu_state_stored_timer *c = &s->t[i];
|
2020-07-10 16:05:11 +02:00
|
|
|
etmu_t *T = &ETMU[i-3];
|
2020-05-10 14:03:41 +02:00
|
|
|
|
|
|
|
/* Don't snapshot an interrupt state, because the timer state
|
|
|
|
is sometimes garbage protected by a masked interrupt. */
|
|
|
|
c->TCOR = T->TCOR ? T->TCOR : 0xffffffff;
|
|
|
|
c->TCNT = T->TCNT ? T->TCNT : c->TCOR;
|
2020-07-10 16:05:11 +02:00
|
|
|
c->TCR = T->TCR.byte & 0xd;
|
2020-05-10 14:03:41 +02:00
|
|
|
c->TSTR = T->TSTR;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
static void hrestore(tmu_state_t const *s)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2020-07-08 19:49:09 +02:00
|
|
|
*TSTR = 0;
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < 3; i++)
|
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
TMU[i].TCOR = s->t[i].TCOR;
|
|
|
|
TMU[i].TCNT = s->t[i].TCNT;
|
|
|
|
TMU[i].TCR.word = s->t[i].TCR;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
2020-07-10 16:05:11 +02:00
|
|
|
for(int i = 3; i < timer_count(); i++)
|
2018-08-01 20:41:36 +02:00
|
|
|
{
|
2021-04-23 18:50:20 +02:00
|
|
|
struct tmu_state_stored_timer const *c = &s->t[i];
|
2020-07-10 16:05:11 +02:00
|
|
|
etmu_t *T = &ETMU[i-3];
|
2020-05-10 14:03:41 +02:00
|
|
|
|
2021-04-27 14:29:38 +02:00
|
|
|
set(T->TCOR, c->TCOR);
|
2020-05-10 14:03:41 +02:00
|
|
|
T->TSTR = c->TSTR;
|
2021-04-27 14:29:38 +02:00
|
|
|
set(T->TCNT, c->TCNT);
|
|
|
|
set(T->TCR.byte, c->TCR);
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
2020-07-08 19:49:09 +02:00
|
|
|
|
2021-04-23 18:50:20 +02:00
|
|
|
*TSTR = s->TSTR;
|
2018-08-01 20:41:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gint_driver_t drv_tmu = {
|
2020-05-10 16:36:21 +02:00
|
|
|
.name = "TMU",
|
2021-04-23 18:50:20 +02:00
|
|
|
.constructor = constructor,
|
|
|
|
.configure = configure,
|
|
|
|
.hsave = (void *)hsave,
|
|
|
|
.hrestore = (void *)hrestore,
|
|
|
|
.state_size = sizeof(tmu_state_t),
|
2018-08-01 20:41:36 +02:00
|
|
|
};
|
2021-04-23 18:50:20 +02:00
|
|
|
GINT_DECLARE_DRIVER(13, drv_tmu);
|