cpg, tmu: add spin waiting and spin delay functions for drivers

This change adds a new TMU function timer_spinwait() which waits for a
timer to raise its UNF flag. This makes it possible to wait even when
interrupts are disabled.

This is used by the new CPG function sleep_us_spin() which waits for a
given delay without using interrupts. This is currently used in SPU
initialization.
This commit is contained in:
Lephe 2020-10-21 14:49:34 +02:00
parent 858ec8aa12
commit 8ff7d89d33
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
5 changed files with 50 additions and 10 deletions

View file

@ -67,6 +67,11 @@ const clock_frequency_t *clock_freq(void);
function selects a timer with timer_setup() called with TIMER_ANY. */
void sleep_us(uint64_t delay_us);
/* sleep_us_spin(): Actively sleep for a fixed duration in microseconds
Like sleep_us(), but uses timer_spinwait() and does not rely on interrupts
being enabled. Useful in timer code running without interrupts. */
void sleep_us_spin(uint64_t delay_us);
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)

View file

@ -41,7 +41,7 @@
* Set a specific ID in timer_setup(), in which case the delay is no longer
interpreter as count of µs, but as a TCOR value.
* If this ID is a TMU, you can further add (with + or |) a prescaler
specification, one of TIMER_Po_{4,16,64,256}.
specification, one of TIMER_Pphi_{4,16,64,256}.
* Regardless of how the timer was obtained, you can use timer_reload() to
replace the value of TCOR.
* Also note that TMU0, TMU1, TMU2 and the ETMU have respective interrupt
@ -59,7 +59,7 @@
Standard TMU can count at different speeds. A fast speed offers more
precision but a slower speed offers longer delays. gint automatically
selects suitable speed by default.
selects suitable speeds by default.
If you want something very particular, you can add (with + or |) a prescaler
value to a chosen ID in timer_setup() to request that specific value. The
@ -186,6 +186,12 @@ void timer_stop(int timer);
it may have only paused. If the timer never stops, you're in trouble. */
void timer_wait(int timer);
/* timer_spinwait(): Actively wait for a timer to raise UNF
Waits until the timer raises UNF, without sleeping. This is useful for
delays in driver code that is run when interrupts are disabled. This relies
neither on the interrupt signal nor on the UNIE flag. */
void timer_spinwait(int timer);
//---
// Low-level functions
//---

View file

@ -56,7 +56,9 @@ static void sh7705_probe(void)
/* Deduce the frequency of the main clocks. This value is ckio/3 */
int ckio_3 = 9830400;
/* Exchange the setting values 2 and 3 */
/* Exchange the setting values 2 and 3 (corresponding to /3 and /4)
This means that /1, /2, /4 are now 0, 1, 2, which is perfect for a
quick bit shift */
idiv = idiv ^ (idiv >> 1);
pdiv = pdiv ^ (pdiv >> 1);
@ -87,8 +89,8 @@ static void sh7305_probe(void)
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
freq.FLL = fll;
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but
SH7305 behaves as 1 / (2^setting + 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.FRQCRA.BFC;
int divi = CPG.FRQCRA.IFC;

View file

@ -5,8 +5,7 @@
#include <gint/clock.h>
#include <gint/timer.h>
/* sleep_us(): Sleep for a fixed duration in microseconds */
void sleep_us(uint64_t delay_us)
static void do_sleep(uint64_t delay_us, int spin)
{
volatile int flag = 0;
@ -14,5 +13,18 @@ void sleep_us(uint64_t delay_us)
if(timer < 0) return;
timer_start(timer);
timer_wait(timer);
if(spin) timer_spinwait(timer);
else timer_wait(timer);
}
/* sleep_us(): Sleep for a fixed duration in microseconds */
void sleep_us(uint64_t delay_us)
{
do_sleep(delay_us, 0);
}
/* sleep_us_spin(): Actively sleep for a fixed duration in microseconds */
void sleep_us_spin(uint64_t delay_us)
{
do_sleep(delay_us, 1);
}

View file

@ -261,13 +261,13 @@ void timer_stop(int id)
}
}
/* timer_wait() - wait until a timer is stopped */
/* timer_wait(): Wait for a timer to stop */
void timer_wait(int id)
{
if(id < 3)
{
tmu_t *T = &TMU[id];
/* Sleep if an interruption will wake us up */
/* Sleep only if an interrupt will be there to wake us up */
while(*TSTR & (1 << id)) if(T->TCR.UNIE) sleep();
}
else
@ -277,6 +277,21 @@ void timer_wait(int id)
}
}
/* timer_spinwait(): Actively wait for a timer to raise UNF */
void timer_spinwait(int id)
{
if(id < 3)
{
tmu_t *T = &TMU[id];
while(!T->TCR.UNF) {}
}
else
{
etmu_t *T = &ETMU[id-3];
while(!T->TCR.UNF) {}
}
}
//---
// Predefined timer callbacks
//---