From 8ff7d89d33d4c55e6b6ac988bcdd81fd6806a4b1 Mon Sep 17 00:00:00 2001 From: Lephe Date: Wed, 21 Oct 2020 14:49:34 +0200 Subject: [PATCH] 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. --- include/gint/clock.h | 5 +++++ include/gint/timer.h | 10 ++++++++-- src/cpg/cpg.c | 8 +++++--- src/tmu/sleep.c | 18 +++++++++++++++--- src/tmu/tmu.c | 19 +++++++++++++++++-- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/include/gint/clock.h b/include/gint/clock.h index fa75c9b..c6fc0c9 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -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) diff --git a/include/gint/timer.h b/include/gint/timer.h index 7c4de42..954ce0b 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -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 //--- diff --git a/src/cpg/cpg.c b/src/cpg/cpg.c index 9ed33cf..e942182 100644 --- a/src/cpg/cpg.c +++ b/src/cpg/cpg.c @@ -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; diff --git a/src/tmu/sleep.c b/src/tmu/sleep.c index 6b8effc..c24670b 100644 --- a/src/tmu/sleep.c +++ b/src/tmu/sleep.c @@ -5,8 +5,7 @@ #include #include -/* 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); } diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index cb893a8..0b710ea 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -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 //---