mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2025-07-11 22:57:34 +02:00
timer: final iteration on the API
This commit minimally changes the signature of timer_setup() to greatly simplify timer management, allowing to user to let the library choose available timers dynamically depending on the settings.
This commit is contained in:
parent
a91a0a483b
commit
dc83d5ee1f
7 changed files with 325 additions and 169 deletions
|
@ -5,6 +5,8 @@
|
||||||
#ifndef GINT_CLOCK
|
#ifndef GINT_CLOCK
|
||||||
#define GINT_CLOCK
|
#define GINT_CLOCK
|
||||||
|
|
||||||
|
#include <gint/defs/types.h>
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Clock signals
|
// Clock signals
|
||||||
//---
|
//---
|
||||||
|
@ -59,22 +61,13 @@ const clock_frequency_t *clock_freq(void);
|
||||||
add-in is idle, for instance while waiting for keyboard input. */
|
add-in is idle, for instance while waiting for keyboard input. */
|
||||||
#define sleep() __asm__("sleep")
|
#define sleep() __asm__("sleep")
|
||||||
|
|
||||||
/* sleep_us() - sleep for a definite duration in microseconds
|
/* sleep_us(): Sleep for a fixed duration in microseconds
|
||||||
|
Stops the processor until the specified delay in microseconds has elapsed.
|
||||||
|
(The processor will still wake up occasionally to handle interrupts.) This
|
||||||
|
function selects a timer with timer_setup() called with TIMER_ANY. */
|
||||||
|
void sleep_us(uint64_t delay_us);
|
||||||
|
|
||||||
Stops the processor until [delay_us] microseconds have elapsed. Interrupts
|
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
|
||||||
may occur during that time (especially timers firing), in which case the
|
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
|
||||||
events will be treated as usual. The processor will resume sleeping after
|
|
||||||
handling them.
|
|
||||||
|
|
||||||
The user may choose the timer used to time out the sleep. Remember that only
|
|
||||||
timers 0 to 2 have microsecond-level resolution; other timers count in units
|
|
||||||
of about 30 us.
|
|
||||||
|
|
||||||
@timer Which timer to use to time out the sleep
|
|
||||||
@us_delay How long to sleep (in microseconds) */
|
|
||||||
void sleep_us(int timer, int us_delay);
|
|
||||||
|
|
||||||
/* sleep_ms() - sleep for a definite duration in milliseconds */
|
|
||||||
#define sleep_ms(timer, ms_delay) sleep_us(timer, (ms_delay) * 1000)
|
|
||||||
|
|
||||||
#endif /* GINT_CLOCK */
|
#endif /* GINT_CLOCK */
|
||||||
|
|
|
@ -28,11 +28,12 @@
|
||||||
|
|
||||||
/* Aligned variables */
|
/* Aligned variables */
|
||||||
#define GALIGNED(x) __attribute__((aligned(x)))
|
#define GALIGNED(x) __attribute__((aligned(x)))
|
||||||
|
|
||||||
/* Packed structures. I require explicit alignment because if it's unspecified,
|
/* Packed structures. I require explicit alignment because if it's unspecified,
|
||||||
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
||||||
access sizes silently fail - honestly you don't want this to happen */
|
access sizes silently fail - honestly you don't want this to happen */
|
||||||
#define GPACKED(x) __attribute__((packed, aligned(x)))
|
#define GPACKED(x) __attribute__((packed, aligned(x)))
|
||||||
|
/* Transparent unions */
|
||||||
|
#define GTRANSPARENT __attribute__((transparent_union))
|
||||||
|
|
||||||
/* Weak symbols */
|
/* Weak symbols */
|
||||||
#define GWEAK __attribute__((weak))
|
#define GWEAK __attribute__((weak))
|
||||||
|
|
|
@ -9,114 +9,203 @@
|
||||||
#include <gint/mpu/tmu.h>
|
#include <gint/mpu/tmu.h>
|
||||||
#include <gint/hardware.h>
|
#include <gint/hardware.h>
|
||||||
|
|
||||||
/* Timer identifiers
|
/* Timer types and numbers
|
||||||
|
|
||||||
Hardware timers are numbered with integers starting from 0. You can freely
|
If you're new to timers, read this comment and then check timer_setup() and
|
||||||
access all the available timers by using their number once you have
|
timer_start(): most of the time you only need these.
|
||||||
configured them with timer_setup(). The number of timers depends on the MPU:
|
|
||||||
|
|
||||||
SH3-based: 4 timers, ids 0..3 [SH7355, SH7337]
|
There are two types of timers on the calculator: normal timers called TMU,
|
||||||
SH4-based: 9 timers, ids 0..8 [SH7305]
|
and extra timers added by Casio called ETMU. The main difference is that TMU
|
||||||
|
are very precise (about 4 MHz; the resolution is about 250 ns) while ETMU
|
||||||
|
are much less precise (32 kHz; the resolution is about 30 µs).
|
||||||
|
|
||||||
You should be aware that some of these timers are used by default by gint:
|
The number of available timers also depends on the platform:
|
||||||
- Timer 0 is used by the gray engine on fx9860g.
|
* SH3-based fx9860g have 3 TMU (ID 0,1,2) and 1 ETMU (ID 3)
|
||||||
- Timer 3/8 is used by the keyboard on SH3/SH4.
|
* SH4-based fx9860g and fxcg50 have 3 TMU (ID 0,1,2) and 6 ETMU (ID 3..8)
|
||||||
|
|
||||||
timer_setup() will fail if you try to use a timer that's already running.
|
You can request "a" timer with timer_setup(), and gint will choose an
|
||||||
Always check the return value of timer_setup()! Using a timer id that has
|
available timer depending on the precision you requested. Or, if you want
|
||||||
not been validated by timer_setup() will work, but do *something else* than
|
one specifically, you ask for an ID of your choice.
|
||||||
what you intended. */
|
|
||||||
|
|
||||||
/* timer_count() - tells how many timers are available on the platform */
|
gint uses 1 to 2 timers by default:
|
||||||
|
* One for the keyboard on all calculators, always an ETMU
|
||||||
|
* The gray engine on fx9860g uses TMU0 for interrupt priority reasons; TMU0
|
||||||
|
is normally available for it unless you've used all TMUs at the same time
|
||||||
|
libprof also uses a TMU.
|
||||||
|
|
||||||
|
Most of the time you can just use timer_setup() and specify TIMER_ANY. If
|
||||||
|
you want to be sure that you have a TMU or an ETMU, you can do so by
|
||||||
|
specifying TIMER_TMU or TIMER_ETMU. If you further want to have a specific
|
||||||
|
timer with specific settings, then you can:
|
||||||
|
|
||||||
|
* 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}.
|
||||||
|
* 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
|
||||||
|
priority levels of 13, 11, 9, and 7. The gray engine uses TMU0 to
|
||||||
|
guarantee maximum visual stability in the presence of interrupts.
|
||||||
|
|
||||||
|
In this module, timers are manipulated through their ID. timer_setup()
|
||||||
|
returns the ID of a timer which was allocated to you. You can check it to
|
||||||
|
determine whether your timer is a TMU (0,1,2) or an ETMU (3 or more). */
|
||||||
|
|
||||||
|
/* timer_count(): Number of timers available on the platform */
|
||||||
#define timer_count() (isSH3() ? 4 : 9)
|
#define timer_count() (isSH3() ? 4 : 9)
|
||||||
|
|
||||||
/* Clock input
|
/* Clock input
|
||||||
|
|
||||||
Timers count down when their input clock ticks, and fire when their counter
|
Standard TMU can count at different speeds. A fast speed offers more
|
||||||
reach 0. The choice of the input clock influences the resolution of the
|
precision but a slower speed offers longer delays. gint automatically
|
||||||
timer, but if the clock is too fast, the 32-bit counter might not be able to
|
selects suitable speed by default.
|
||||||
represent long delays.
|
|
||||||
|
|
||||||
Several input clocks are available. The peripheral clock (Po) can be divided
|
If you want something very particular, you can add (with + or |) a prescaler
|
||||||
by 4, 16, 64 or 256; as an alternative the external clock TCLK can be used
|
value to a chosen ID in timer_setup() to request that specific value. The
|
||||||
for counting. I suspect TCLK runs at a fixed frequency of 32768 Hz, but this
|
default prescaler if the ID is fixed is TIMER_Pphi_4. */
|
||||||
has yet to be verified.
|
enum {
|
||||||
|
TIMER_Pphi_4 = 0x00,
|
||||||
|
TIMER_Pphi_16 = 0x10,
|
||||||
|
TIMER_Pphi_64 = 0x20,
|
||||||
|
TIMER_Pphi_256 = 0x30,
|
||||||
|
};
|
||||||
|
|
||||||
You don't really need to choose an input clock unless you are doing
|
/* Timer selection; see timer_setup() */
|
||||||
something very specific. In most practical cases you can use timer_default
|
enum {
|
||||||
which is 0. See the timer_delay() function for more information. */
|
TIMER_ANY = -1,
|
||||||
typedef enum
|
TIMER_TMU = -2,
|
||||||
|
TIMER_ETMU = -3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Type of callback functions
|
||||||
|
|
||||||
|
This module used to require callbacks of type int (*)(volatile void *), but
|
||||||
|
callbacks are rarely this generic. Now timer_setup() accepts functions of
|
||||||
|
any of the types below. */
|
||||||
|
typedef union
|
||||||
{
|
{
|
||||||
timer_Po_4 = 0,
|
/* No argument, returns either TIMER_CONTINUE or TIMER_STOP */
|
||||||
timer_Po_16 = 1,
|
int (*v)(void);
|
||||||
timer_Po_64 = 2,
|
/* Single integer argument */
|
||||||
timer_Po_256 = 3,
|
int (*i)(int);
|
||||||
timer_TCLK = 5,
|
/* Single pointer argument, cv-qualified as needed */
|
||||||
|
int (*pv) (void *);
|
||||||
|
int (*pVv) (volatile void *);
|
||||||
|
int (*pCv) (const void *);
|
||||||
|
int (*pCVv)(volatile const void *);
|
||||||
|
|
||||||
timer_default = timer_Po_4,
|
} GTRANSPARENT timer_callback_t;
|
||||||
|
|
||||||
} timer_input_t;
|
/* Return value for timer callbacks, indicating whether the timer should
|
||||||
|
continue running and fire again, or stop now */
|
||||||
|
enum {
|
||||||
|
TIMER_CONTINUE = 0,
|
||||||
|
TIMER_STOP = 1,
|
||||||
|
};
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Timer functions
|
// Timer functions
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* timer_setup() - set up a timer
|
/* timer_setup(): Reserve and configure a timer
|
||||||
|
|
||||||
This function configures the requested timer without starting it. On
|
This function finds and configures a timer (without starting it). On
|
||||||
success, it returns the first argument "timer", which is used as a timer
|
success, it returns the ID of the configured timer, which is used in all
|
||||||
identifier in all other timer functions. If the requested timer is already
|
other timer functions. If no timer matching the requested settings is
|
||||||
in use, this function fails and returns a negative number.
|
available, this function returns -1.
|
||||||
|
|
||||||
This function sets the timer delay, the clock source, and registers a
|
When started, the configured timer will run for the requested delay and call
|
||||||
callback function to be called when the timer fires. An argument can be
|
the supplied callback function at the end of this delay. The callback
|
||||||
supplied to the callback function in the form of a pointer.
|
function can then decide whether to leave the timer running (and be called
|
||||||
|
again after the same delay) or stop the timer.
|
||||||
|
|
||||||
When the timer fires, the callback function is called with the provided
|
The first argument specifies what kind of timer you want.
|
||||||
argument pointer. The callback decides whether the timer should continue
|
* TIMER_ANY will let timer_setup() choose any available timer. timer_setup()
|
||||||
running (by returning 0) or stop (by returning nonzero). In the latter case,
|
will only use an ETMU if the delay is more than 0.1 ms to avoid resolution
|
||||||
events accumulated while the callback was running are dropped.
|
issues. Most of the time this is what you need.
|
||||||
|
* TIMER_TMU or TIMER_ETMU will let timer_setup() choose an available TMU or
|
||||||
|
ETMU, respectively.
|
||||||
|
* Specifying an ID (0..8) will request exactly that timer. In this case, and
|
||||||
|
if the ID is a TMU (0,1,2), you may add (with + or |) a prescaler value to
|
||||||
|
specify the clock input. Otherwise the clock is set to Pphi/4.
|
||||||
|
If no timer matching the supplied settings is available, timer_setup()
|
||||||
|
returns -1.
|
||||||
|
|
||||||
It is sometimes difficult to choose a timer constant and a clock source
|
The second argument is the delay. With TIMER_ANY, TIMER_TMU and TIMER_ETMU,
|
||||||
given a wished delay in seconds, especially when overclock is used. The
|
the delay is interpreted as a number of µs. With an explicit ID, the delay
|
||||||
timer_delay() function is provided for this purpose.
|
is interpreted as a value of TCOR; see timer_delay() in this case. Note that
|
||||||
|
TCOR values are sensitive to the overclock level!
|
||||||
|
|
||||||
@timer Requested timer id
|
The third argument is a function to be called when the timer expires. It may
|
||||||
@delay Delay between each event (the unit depends on the clock source)
|
have a single 4-byte argument (int or pointer), in which case you should
|
||||||
@clock Clock source used by the timer for counting down
|
provide the value to call it with as an optional argument (you can only use
|
||||||
@callback Callback function (called when the timer fires)
|
a single argument). It must return an int equal to either TIMER_CONTINUE or
|
||||||
@arg Passed as argument to the callback function */
|
TIMER_STOP to control the subsequent operation of the timer. See the
|
||||||
int timer_setup(int timer, uint32_t delay, timer_input_t clock,
|
definition of timer_callback_t above for the possible function types. If
|
||||||
int (*callback)(volatile void *arg), volatile void *arg);
|
this argument is NULL, a default function that stops the timer will be used.
|
||||||
|
|
||||||
/* timer_delay() - compute a delay constant from a duration in seconds
|
On success, the configured timer becomes reserved; it can no longer be
|
||||||
|
returned by timer_setup() until:
|
||||||
|
* Either timer_stop() is called,
|
||||||
|
* Or the callback returns TIMER_STOP (which also stops the timer).
|
||||||
|
Remember than the returned timer is not started yet; see timer_start().
|
||||||
|
|
||||||
This function can used as a facility to calculate the [delay] argument to
|
@timer Requested timer; TIMER_{ANY,TMU,ETMU} or an ID with prescaler
|
||||||
the timer_setup() function. It takes a microsecond delay as an argument and
|
@delay Delay between each event, in µs unless first argument is an ID
|
||||||
returns the corresponding timer constant. A typical use to start a timer
|
@callback Function to be called when the timer fires
|
||||||
with a 25 ms interval would be:
|
@... If the callback takes an argument, specify that value here */
|
||||||
|
int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...);
|
||||||
|
|
||||||
timer_setup(0, timer_delay(0, 25 * 1000), 0, callback, arg);
|
/* Makes sure an argument is always provided, for va_arg() */
|
||||||
|
#define timer_setup(...) timer_setup(__VA_ARGS__, 0)
|
||||||
|
|
||||||
WARNING: Only timers 0 to 2 can count microseconds! Other timers have a
|
/* timer_start(): Start a configured timer
|
||||||
resolution of around 30 us. Counting in ms is safe for all timers, though.
|
The specified timer will start counting down and call its callback function
|
||||||
|
at regular intervals until it is paused or stopped. */
|
||||||
For standard timers (0 to 2) it uses Po / 4 as clock input, which is very
|
|
||||||
precise and can represent up to 3 minutes' time; for extra timers (3 and
|
|
||||||
above) the clock is fixed to 32768 Hz.
|
|
||||||
|
|
||||||
@timer The timer you are planning to use
|
|
||||||
@delay_us Requested delay in microseconds */
|
|
||||||
uint32_t timer_delay(int timer, uint64_t delay_us);
|
|
||||||
|
|
||||||
/* timer_start() - start a configured timer
|
|
||||||
The specified timer will start counting down and fire callbacks at regular
|
|
||||||
intervals.
|
|
||||||
|
|
||||||
@timer Timer id, as returned by timer_setup() */
|
|
||||||
void timer_start(int timer);
|
void timer_start(int timer);
|
||||||
|
|
||||||
/* timer_reload() - change a timer's delay constant for next interrupts
|
/* timer_pause(): Pause a timer without freeing it
|
||||||
|
Pauses the specified timer will be paused. The timer stops counting but is
|
||||||
|
not freed and can be resumed by calling timer_start(). */
|
||||||
|
void timer_pause(int timer);
|
||||||
|
|
||||||
|
/* timer_stop(): Stop and free a timer
|
||||||
|
Stops and frees a timer, making it available to timer_setup(). This timer
|
||||||
|
should no longer be used unless it is returned again by timer_setup(). */
|
||||||
|
void timer_stop(int timer);
|
||||||
|
|
||||||
|
/* timer_wait(): Wait for a timer to stop
|
||||||
|
Waits until the timer pauses or stops. If the timer is not running, returns
|
||||||
|
immediately. Even after timer_wait(), the timer may not be available since
|
||||||
|
it may have only paused. If the timer never stops, you're in trouble. */
|
||||||
|
void timer_wait(int timer);
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Low-level functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* timer_delay(): Compute a timer constant from a duration in seconds
|
||||||
|
|
||||||
|
This function calculates the timer constant for a given delay. timer_setup()
|
||||||
|
does this computation when TIMER_ANY, TIMER_TMU or TIMER_ETMU is provided,
|
||||||
|
but expects you to provide the exact constant when an explicit timer ID is
|
||||||
|
requested. timer_reload() also expects the constant.
|
||||||
|
|
||||||
|
For TMU the clock can be Pphi/4, Pphi/16, Pphi/64 and Pphi/256, which can
|
||||||
|
respectively count up to about 350 seconds, 23 minutes, 95 minutes and 381
|
||||||
|
minutes.
|
||||||
|
|
||||||
|
For ETMU the clock is TCLK at 32768 Hz, which can count up to 36 hours. Any
|
||||||
|
longer delay should probably be managed by the RTC (which counts even when
|
||||||
|
the calculator is off).
|
||||||
|
|
||||||
|
@timer The timer you are planning to use
|
||||||
|
@delay_us Requested delay in microseconds
|
||||||
|
@clock The clock value, irrelevant if timer >= 3 */
|
||||||
|
uint32_t timer_delay(int timer, uint64_t delay_us, int clock);
|
||||||
|
|
||||||
|
/* timer_reload(): Change a timer's delay constant for next interrupts
|
||||||
|
|
||||||
Changes the delay constant of the given timer. Nothing will happen until the
|
Changes the delay constant of the given timer. Nothing will happen until the
|
||||||
next callback; then the timer will update its delay to reflect the new
|
next callback; then the timer will update its delay to reflect the new
|
||||||
|
@ -126,35 +215,14 @@ void timer_start(int timer);
|
||||||
@delay New delay (unit depends on the clock source) */
|
@delay New delay (unit depends on the clock source) */
|
||||||
void timer_reload(int timer, uint32_t delay);
|
void timer_reload(int timer, uint32_t delay);
|
||||||
|
|
||||||
/* timer_pause() - stop a running timer
|
|
||||||
The specified timer will be paused; its counter will not be reset. A stopped
|
|
||||||
timer can be resumed anytime by calling timer_start(). If you want to also
|
|
||||||
reset the counter, use timer_reload().
|
|
||||||
|
|
||||||
@timer Timer id, as returned by timer_setup() */
|
|
||||||
void timer_pause(int timer);
|
|
||||||
|
|
||||||
/* timer_stop() - stop and free a timer
|
|
||||||
Stops and destroys a timer, making its id free for re-use. The id must not
|
|
||||||
be used anymore until it is returned by a further call to timer_setup().
|
|
||||||
|
|
||||||
@timer Timer id, as returned by timer_setup() */
|
|
||||||
void timer_stop(int timer);
|
|
||||||
|
|
||||||
/* timer_wait() - wait for a timer to stop
|
|
||||||
Waits until the specified timer stops running. If the timer is not running,
|
|
||||||
returns immediately. The timer might not be free if it has just been paused
|
|
||||||
instead of stopped entirely. */
|
|
||||||
void timer_wait(int timer);
|
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Predefined timer callbacks
|
// Predefined timer callbacks
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* timer_timeout() - callback that sets a flag and halts the timer
|
/* timer_timeout(): Callback that sets a flag and halts the timer
|
||||||
This predefined callback may be used when a timeout is required. It sets its
|
This predefined callback may be used when a timeout is required. It takes a
|
||||||
argument pointer to 1 and halts the timer. The pointer must be of type
|
single argument which must be a pointer to int (or other integer type of 4
|
||||||
int * and you must declare the variable as volatile int. */
|
bytes) and increments it. */
|
||||||
int timer_timeout(volatile void *arg);
|
int timer_timeout(volatile void *arg);
|
||||||
|
|
||||||
#endif /* GINT_TIMER */
|
#endif /* GINT_TIMER */
|
||||||
|
|
|
@ -74,13 +74,13 @@ GDESTRUCTOR static void gray_quit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gray_int(): Interrupt handler */
|
/* gray_int(): Interrupt handler */
|
||||||
int gray_int(GUNUSED volatile void *arg)
|
int gray_int(void)
|
||||||
{
|
{
|
||||||
t6k11_display(vrams[st ^ 2], 0, 64, 16);
|
t6k11_display(vrams[st ^ 2], 0, 64, 16);
|
||||||
timer_reload(GRAY_TIMER, delays[(st ^ 3) & 1]);
|
timer_reload(GRAY_TIMER, delays[(st ^ 3) & 1]);
|
||||||
st ^= 1;
|
st ^= 1;
|
||||||
|
|
||||||
return 0;
|
return TIMER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gray_start(): Start the gray engine */
|
/* gray_start(): Start the gray engine */
|
||||||
|
@ -92,9 +92,8 @@ void gray_start(void)
|
||||||
|
|
||||||
if(runs) return;
|
if(runs) return;
|
||||||
|
|
||||||
int free = timer_setup(GRAY_TIMER, delays[0], timer_Po_64, gray_int,
|
int timer = timer_setup(GRAY_TIMER|TIMER_Pphi_64, delays[0], gray_int);
|
||||||
NULL);
|
if(timer != GRAY_TIMER) return;
|
||||||
if(free != GRAY_TIMER) return;
|
|
||||||
|
|
||||||
timer_start(GRAY_TIMER);
|
timer_start(GRAY_TIMER);
|
||||||
st = 0;
|
st = 0;
|
||||||
|
|
|
@ -248,7 +248,7 @@ int keydown_any(int key, ...)
|
||||||
// Driver initialization
|
// Driver initialization
|
||||||
//---
|
//---
|
||||||
|
|
||||||
static int callback(GUNUSED volatile void *arg)
|
static int callback(void)
|
||||||
{
|
{
|
||||||
keysc_frame();
|
keysc_frame();
|
||||||
time++;
|
time++;
|
||||||
|
@ -258,20 +258,18 @@ static int callback(GUNUSED volatile void *arg)
|
||||||
/* init() - setup the support timer */
|
/* init() - setup the support timer */
|
||||||
static void init(void)
|
static void init(void)
|
||||||
{
|
{
|
||||||
int tid = isSH3() ? 3 : 8;
|
|
||||||
|
|
||||||
/* Configure the timer to do 128 keyboard scans per second. This
|
/* Configure the timer to do 128 keyboard scans per second. This
|
||||||
frequency *must* be high for the KEYSC interface to work! */
|
frequency *must* be high for the KEYSC interface to work! */
|
||||||
/* Note: the supporting timer always runs at 32768 Hz. */
|
/* Note: the supporting timer always runs at 32768 Hz. */
|
||||||
int delay = 32768 / KEYBOARD_SCAN_FREQUENCY;
|
int delay = 1000000 / KEYBOARD_SCAN_FREQUENCY;
|
||||||
if(!delay) delay = 1;
|
if(!delay) delay = 1;
|
||||||
|
|
||||||
/* Set the default repeat times (milliseconds) */
|
/* Set the default repeat times (milliseconds) */
|
||||||
getkey_repeat(400, 40);
|
getkey_repeat(400, 40);
|
||||||
|
|
||||||
/* The timer will be stopped when the timer driver is unloaded */
|
/* The timer will be stopped when the timer driver is unloaded */
|
||||||
timer_setup(tid, delay, 0, callback, NULL);
|
int tid = timer_setup(TIMER_ANY, delay, callback);
|
||||||
timer_start(tid);
|
if(tid >= 0) timer_start(tid);
|
||||||
|
|
||||||
gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI);
|
gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI);
|
||||||
gint[HWKBDSF] = KEYBOARD_SCAN_FREQUENCY;
|
gint[HWKBDSF] = KEYBOARD_SCAN_FREQUENCY;
|
||||||
|
|
|
@ -5,15 +5,14 @@
|
||||||
#include <gint/clock.h>
|
#include <gint/clock.h>
|
||||||
#include <gint/timer.h>
|
#include <gint/timer.h>
|
||||||
|
|
||||||
/* sleep_us() - sleep for a definite duration in microseconds */
|
/* sleep_us(): Sleep for a fixed duration in microseconds */
|
||||||
void sleep_us(int tid, int us_delay)
|
void sleep_us(uint64_t delay_us)
|
||||||
{
|
{
|
||||||
volatile int flag = 0;
|
volatile int flag = 0;
|
||||||
uint32_t delay = timer_delay(tid, us_delay);
|
|
||||||
|
|
||||||
int free = timer_setup(tid, delay, 0, timer_timeout, &flag);
|
int timer = timer_setup(TIMER_ANY, delay_us, timer_timeout, &flag);
|
||||||
if(free < 0) return;
|
if(timer < 0) return;
|
||||||
|
|
||||||
timer_start(tid);
|
timer_start(timer);
|
||||||
while(!flag) sleep();
|
timer_wait(timer);
|
||||||
}
|
}
|
||||||
|
|
160
src/tmu/tmu.c
160
src/tmu/tmu.c
|
@ -7,12 +7,11 @@
|
||||||
#include <gint/gint.h>
|
#include <gint/gint.h>
|
||||||
#include <gint/clock.h>
|
#include <gint/clock.h>
|
||||||
#include <gint/intc.h>
|
#include <gint/intc.h>
|
||||||
|
|
||||||
#include <gint/mpu/intc.h>
|
|
||||||
#include <gint/mpu/tmu.h>
|
#include <gint/mpu/tmu.h>
|
||||||
|
|
||||||
#include <gint/defs/attributes.h>
|
#include <stdarg.h>
|
||||||
#include <gint/defs/types.h>
|
|
||||||
|
#undef timer_setup
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Driver storage
|
// Driver storage
|
||||||
|
@ -21,9 +20,9 @@
|
||||||
/* inth_data_t - data storage inside interrupt handlers */
|
/* inth_data_t - data storage inside interrupt handlers */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int (*cb)(volatile void *arg); /* User-provided callback */
|
void *function; /* User-provided callback */
|
||||||
volatile void *arg; /* Argument for [callback] */
|
uint32_t arg; /* Argument for function */
|
||||||
volatile void *TCR; /* TCR address for TMU */
|
volatile void *TCR; /* TCR address for TMU */
|
||||||
|
|
||||||
} GPACKED(4) inth_data_t;
|
} GPACKED(4) inth_data_t;
|
||||||
|
|
||||||
|
@ -37,21 +36,17 @@ GDATA static etmu_t *ETMU = SH7305_ETMU;
|
||||||
GDATA static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
|
GDATA static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Timer API
|
// Local functions
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* timer_setup() - set up a timer */
|
/* configure(): Configure a fixed timer */
|
||||||
int timer_setup(int id, uint32_t delay, timer_input_t clock,
|
static void configure(int id, uint32_t delay, int clock, void *f, uint32_t arg)
|
||||||
int (*callback)(volatile void *arg), volatile void *arg)
|
|
||||||
{
|
{
|
||||||
/* Timer is not installed (TCR address is really required) */
|
|
||||||
if(!timers[id]) return -1;
|
|
||||||
|
|
||||||
if(id < 3)
|
if(id < 3)
|
||||||
{
|
{
|
||||||
/* Refuse to setup timers that are already in use */
|
/* Refuse to setup timers that are already in use */
|
||||||
tmu_t *T = &TMU[id];
|
tmu_t *T = &TMU[id];
|
||||||
if(T->TCR.UNIE || *TSTR & (1 << id)) return -1;
|
if(T->TCR.UNIE || *TSTR & (1 << id)) return;
|
||||||
|
|
||||||
/* Configure the counter, clear interrupt flag*/
|
/* Configure the counter, clear interrupt flag*/
|
||||||
T->TCOR = delay;
|
T->TCOR = delay;
|
||||||
|
@ -67,10 +62,10 @@ int timer_setup(int id, uint32_t delay, timer_input_t clock,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
etmu_t *T = &ETMU[id-3];
|
etmu_t *T = &ETMU[id-3];
|
||||||
if(T->TCR.UNIE) return -1;
|
if(T->TCR.UNIE) return;
|
||||||
|
|
||||||
/* No clock input and clock edge here. But TCR and TCNT need a
|
/* No clock input and clock edge here. But TCR and TCNT need
|
||||||
delay to retain the value */
|
some time to execute the write */
|
||||||
do T->TCR.UNF = 0;
|
do T->TCR.UNF = 0;
|
||||||
while(T->TCR.UNF);
|
while(T->TCR.UNF);
|
||||||
|
|
||||||
|
@ -83,25 +78,128 @@ int timer_setup(int id, uint32_t delay, timer_input_t clock,
|
||||||
T->TCR.UNIE = 1;
|
T->TCR.UNIE = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
timers[id]->cb = callback;
|
timers[id]->function = f;
|
||||||
timers[id]->arg = arg;
|
timers[id]->arg = arg;
|
||||||
return id;
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
/* The timer should also be installed... */
|
||||||
|
if(!timers[id]) return 0;
|
||||||
|
|
||||||
|
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
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* timer_setup(): Reserve and configure a timer */
|
||||||
|
int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...)
|
||||||
|
{
|
||||||
|
int clock = 0;
|
||||||
|
|
||||||
|
/* Get the optional argument */
|
||||||
|
va_list va;
|
||||||
|
va_start(va, function);
|
||||||
|
uint32_t arg = va_arg(va, uint32_t);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
/* Default value for the callback */
|
||||||
|
if(!function.v) function.v = stop_callback;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
configure(id, delay, clock, function.v, arg);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* timer_delay() - compute a delay constant from a duration in seconds */
|
/* timer_delay() - compute a delay constant from a duration in seconds */
|
||||||
uint32_t timer_delay(int id, uint64_t delay_us)
|
uint32_t timer_delay(int id, uint64_t delay_us, int clock)
|
||||||
{
|
{
|
||||||
/* TODO: Proper timer_delay() */
|
uint64_t freq;
|
||||||
const clock_frequency_t *clock = clock_freq();
|
|
||||||
uint64_t freq = clock->Pphi_f >> 2;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/* fxcg50: Calculated = 29491200 but it's too low */
|
/* fxcg50: Calculated = 29491200 but it's too low */
|
||||||
/* TODO: Account for down spread spectrum in the CPG */
|
/* TODO: Account for down spread spectrum in the CPG */
|
||||||
// uint64_t freq = 29020000 >> 2;
|
// uint64_t freq = 29020000 >> 2;
|
||||||
|
|
||||||
/* Extra timers all run at 32768 Hz */
|
|
||||||
if(id >= 3) freq = 32768;
|
|
||||||
|
|
||||||
uint64_t product = freq * delay_us;
|
uint64_t product = freq * delay_us;
|
||||||
return product / 1000000;
|
return product / 1000000;
|
||||||
}
|
}
|
||||||
|
@ -184,11 +282,11 @@ void timer_wait(int id)
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* timer_timeout() - callback that sets a flag and halts the timer */
|
/* timer_timeout() - callback that sets a flag and halts the timer */
|
||||||
int timer_timeout(volatile void *arg)
|
int timer_timeout(void volatile *arg)
|
||||||
{
|
{
|
||||||
volatile int *x = arg;
|
int volatile *x = arg;
|
||||||
(*x)++;
|
if(x) (*x)++;
|
||||||
return 1;
|
return TIMER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
|
|
Loading…
Add table
Reference in a new issue