diff --git a/include/gint/mpu/tmu.h b/include/gint/mpu/tmu.h new file mode 100644 index 0000000..a07d34d --- /dev/null +++ b/include/gint/mpu/tmu.h @@ -0,0 +1,89 @@ +//--- +// gint:intc:tmu - Timer Unit +// +// The definitions in this file cover both the Timer Unit and the Extra +// Timers. The structures are related but not identical; the behaviour is +// subtly different. +//--- + +#ifndef GINT_MPU_TMU +#define GINT_MPU_TMU + +#include + +//--- +// Common structures +//--- + +/* tmu_t - a single timer from a standard timer unit */ +typedef volatile struct +{ + uint32_t TCOR; /* Constant register */ + uint32_t TCNT; /* Counter register, counts down */ + + word_union(TCR, + uint16_t :7; + uint16_t UNF :1; /* Underflow flag */ + uint16_t :2; + uint16_t UNIE :1; /* Underflow interrupt enable */ + uint16_t CKEG :2; /* Input clock edge */ + uint16_t TPSC :3; /* Timer prescaler (input clock) */ + ); + +} GPACKED(4) tmu_t; + +/* etmu_t - extra timers on SH7337, SH7355 and SH7305 */ +typedef volatile struct +{ + uint8_t TSTR; /* Only bit 0 is used */ + pad(3); + + uint32_t TCOR; /* Constant register */ + uint32_t TCNT; /* Counter register */ + + byte_union(TCR, + uint8_t :6; + uint8_t UNF :1; /* Underflow flag */ + uint8_t UNIE :1; /* Underflow interrupt enable */ + ); + +} GPACKED(4) etmu_t; + +//--- +// SH7705 Timer Unit. Refer to: +// "Renesas SH7705 Group Hardware Manual" +// Section 12: "Timer Unit (TMU)" +//--- + +typedef volatile struct +{ + pad(2); + uint8_t TSTR; /* Timer Start Register */ + pad(1); + + tmu_t TMU[3]; + + uint32_t TCPR2; /* Timer Input Capture Register 2 */ + +} GPACKED(4) sh7705_tmu_t; + +#define SH7705_TMU (*((sh7705_tmu_t *)0xfffffe90)) + +//--- +// SH7305 Timer Unit. Refer to: +// "Renesas SH7724 User's Manual: Hardware" +// Section 20: "Timer Unit (TMU)" +//--- + +typedef volatile struct +{ + uint8_t TSTR; /* Timer Start Register */ + pad(3); + + tmu_t TMU[3]; + +} GPACKED(4) sh7305_tmu_t; + +#define SH7305_TMU (*((sh7305_tmu_t *)0xa4490004)) + +#endif /* GINT_MPU_TMU */ diff --git a/include/gint/timer.h b/include/gint/timer.h index 01af28d..27b51fc 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -6,6 +6,7 @@ #define GINT_TIMER #include +#include #include /* Timer identifiers @@ -151,4 +152,16 @@ void timer_stop(int timer); int * and you must declare the variable as volatile int. */ int timer_timeout(volatile void *arg); +//--- +// Low-level functions +//--- + +/* timer_address() - get the address of a timer structure + Returns a tmu_t if the id is 0, 1 or 2 and an etmu_t otherwise. The address + will be NULL if the requested timer does not exist. + + @timer Requested timer + @TSTR If the requested timer is a TMU, set to the TSTR address */ +void *timer_address(int timer, volatile uint8_t **TSTR); + #endif /* GINT_TIMER */ diff --git a/src/tmu/inth.s b/src/tmu/inth.s index d5b4fd8..143c964 100644 --- a/src/tmu/inth.s +++ b/src/tmu/inth.s @@ -12,9 +12,9 @@ .global _inth_tmu_storage /* Gates for the extra timers (informally called ETMU) */ -.global _inth_tmu_extra2 -.global _inth_tmu_extra_help -.global _inth_tmu_extra_others +.global _inth_etmu2 +.global _inth_etmu_help +.global _inth_etmux .section .gint.blocks, "ax" .align 4 @@ -39,11 +39,12 @@ /* FIRST GATE - TMU0 entry, clear underflow flag and call back */ _inth_tmu_0: mova .storage0, r0 - mov #-2, r1 + mov #0, r1 /*** This is the first shared section ***/ .clearflag: + sts.l pr, @-r15 mov.l r1, @-r15 /* Load the TCR address and clear the interrupt flag */ @@ -54,22 +55,21 @@ _inth_tmu_0: mov.w r2, @r1 /* Invoke the callback function and pass the argument */ - sts.l pr, @-r15 mov.l @r0, r1 jsr @r1 mov.l @(4, r0), r4 - lds.l @r15+, pr /* Prepare stopping the timer and jump to second section */ - mov.l .tstr, r5 + mov.l @r15+, r4 + mov.l .timer_stop, r1 bra .stoptimer - mov.l @r15+, r1 + nop /* SECOND GATE - TMU1 entry and stop timer */ _inth_tmu_1: mova .storage1, r0 bra .clearflag - mov #-3, r1 + mov #1, r1 /*** This is the second shared section ***/ @@ -77,11 +77,11 @@ _inth_tmu_1: /* Stop the timer if the return value is not zero */ tst r0, r0 bt .end - mov.b @r5, r2 - and r1, r2 - mov.b r2, @r5 + jsr @r1 + nop .end: + lds.l @r15+, pr rte nop @@ -91,7 +91,7 @@ _inth_tmu_1: _inth_tmu_2: mova .storage2, r0 bra .clearflag - mov #-5, r1 + mov #2, r1 .zero 14 @@ -103,8 +103,10 @@ _inth_tmu_2: /* FOURTH GATE - Storage for TMU1, TMU2 and other values */ _inth_tmu_storage: -.mask: .long 0x0000feff -.tstr: .long 0xa4490004 /* TSTR: Overridden at startup on SH3 */ +.mask: + .long 0x0000feff +.timer_stop: + .long _timer_stop /* gint's function from */ .storage1: .long 0 /* Callback: Configured dynamically */ @@ -139,87 +141,72 @@ _inth_tmu_storage: can think of is hardcoding the relative displacements, and one would need to use the unnatural and unmaintainable @(disp, pc) addressing modes. */ -/* FIRST GATE - ETMU2 entry, clear flag and prepare callback */ -_inth_tmu_extra2: - /* Warning: the size of the following instruction (2 bytes) is - hardcoded in another interrupt handler, _inth_tmu_extra_others */ - mova .storage_extra_1, r0 +/* FIRST GATE - ETMU2 entry, invoke callback and prepare clear flag */ +_inth_etmu2: + /* Warning: the size of the following section (4 bytes) is hardcoded in + the jump in _inth_etmux */ + mova .storage_etmu2, r0 + mov #5, r1 .extra_callback: - mov.l r8, @-r15 - mov r0, r8 - stc.l gbr, @-r15 + sts.l pr, @-r15 + mov.l r1, @-r15 /* Invoke the callback function */ - sts.l pr, @-r15 - mov.l @r8, r1 + mov.l @r0, r1 jsr @r1 - mov.l @(4, r8), r4 + mov.l @(4, r0), r4 - bra .extra_clearflag - lds.l @r15+, pr - -.storage_extra_1: - .long 0 /* Callback: Configured dynamically */ - .long 0 /* Argument: Configured dynamically */ - .long 0 /* Structure address: Edited at startup */ - -/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */ -_inth_tmu_extra_help: - -.extra_clearflag: + /* Load timer ID and forward the callback's return value */ + mov.l .timer_clear, r1 + mov.l @r15+, r4 + bra _inth_etmu_help mov r0, r5 - /* Load struture address */ - mov.l @(8, r8), r0 - ldc r0, gbr - mov #12, r0 + nop -.extra_loopclear: - /* Aggressively clear the interrupt flag. The loop is required because - clearing takes "some time". The system does it. Not doing it will - cause the interrupt to be triggered again until the flag is cleared, - which can be ~20 interrupts if the handler is fast! */ - and.b #0xfd, @(r0, gbr) - tst.b #0x02, @(r0, gbr) - bf .extra_loopclear +.storage_etmu2: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ - /* Check whether to stop the timer */ - tst r5, r5 - bt .extra_end +/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */ +_inth_etmu_help: -.extra_stoptimer: - mov.b @(0, gbr), r0 - and #0xfe, r0 - mov.b r0, @(0, gbr) + /* Clear the flag and possibly stop the timer */ + jsr @r1 + nop -.extra_end: - ldc.l @r15+, gbr - mov.l @r15+, r8 + lds.l @r15+, pr rte nop -/* FOURTH GATE - All other ETMU entries, deferred to the previous ones */ -_inth_tmu_extra_others: - /* Dynamically compute the target of the jump */ - stc vbr, r1 - mov.l 1f, r2 - add r2, r1 + .zero 18 - mova .storage_extra_others, r0 - jmp @r1 +.timer_clear: + .long _timer_clear /* gint's function from src/tmu/tmu.c */ + +/* THIRD GATE - All other ETMU entries, deferred to the previous ones */ +_inth_etmux: + /* Dynamically compute the target of the jump */ + stc vbr, r3 + mov.l 1f, r2 + add r2, r3 + + mova .storage_etmux, r0 + mov.l .id_etmux, r1 + jmp @r3 + nop nop /* Offset from VBR where extra timer 2 is located: - 0x600 to reach the interrupt handlers - 0x020 to jump over the entry gate - - 0x840 to reach the handler of extra timer 2 - - 0x002 to skip its first instruction (the size is hardcoded) */ -1: .long 0xe62 + - 0x840 to reach the handler of ETMU2 + - 0x004 to skip its first instructions (the size is hardcoded) */ +1: .long 0xe64 - .zero 4 - -.storage_extra_others: +.storage_etmux: .long 0 /* Callback: Configured dynamically */ .long 0 /* Argument: Configured dynamically */ - .long 0 /* Structure address: Edited at startup */ +.id_etmux: + .long 0 /* Timer ID */ diff --git a/src/tmu/sleep.c b/src/tmu/sleep.c index caf298f..c7c18dd 100644 --- a/src/tmu/sleep.c +++ b/src/tmu/sleep.c @@ -10,8 +10,7 @@ void sleep_us(int tid, int us_delay) { volatile int flag = 0; - timer_setup(tid, timer_delay(tid, us_delay), 0, timer_timeout, - (void *)&flag); + timer_setup(tid, timer_delay(tid, us_delay), 0, timer_timeout, &flag); timer_start(tid); while(!flag) sleep(); diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index 1088d3d..760eeab 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -16,46 +17,13 @@ // Timer structures //--- -/* tmu_t - a single timer from a standard timer unit */ -typedef volatile struct -{ - uint32_t TCOR; /* Constant register */ - uint32_t TCNT; /* Counter register, counts down */ - - word_union(TCR, - uint16_t :7; - uint16_t UNF :1; /* Underflow flag */ - uint16_t :2; - uint16_t UNIE :1; /* Underflow interrupt enable */ - uint16_t CKEG :2; /* Input clock edge */ - uint16_t TPSC :3; /* Timer prescaler (input clock) */ - ); - -} GPACKED(4) tmu_t; - -/* tmu_extra_t - extra timers on sh7337, sh7355 and sh7305 */ -typedef volatile struct -{ - uint8_t TSTR; /* Only bit 0 is used */ - pad(3); - - uint32_t TCOR; /* Constant register */ - uint32_t TCNT; /* Counter register */ - - byte_union(TCR, - uint8_t :6; - uint8_t UNF :1; /* Underflow flag */ - uint8_t UNIE :1; /* Underflow interrupt enable */ - ); - -} GPACKED(4) tmu_extra_t; /* inth_data_t - data storage inside interrupt handlers */ typedef struct { int (*cb)(volatile void *arg); /* User-provided callback */ volatile void *arg; /* Argument for [callback] */ - volatile void *structure; /* Either TCR or timer address */ + volatile void *TCR; /* TCR address for TMU */ } GPACKED(4) inth_data_t; @@ -119,7 +87,7 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock, /* Extra timers have a simpler structure */ else { - tmu_extra_t *t = timers[tid].tmu; + etmu_t *t = timers[tid].tmu; if(t->TCR.UNIE) return -1; /* Clear the interrupt flag */ @@ -173,7 +141,7 @@ static void timer_control(int tid, int state) /* For standard timers, use the MPU's TSTR register */ if(tid < 3) *TSTR = (*TSTR | (1 << tid)) ^ (state << tid); /* Extra timers all have their own TSTR register */ - else ((tmu_extra_t *)timers[tid].tmu)->TSTR = state ^ 1; + else ((etmu_t *)timers[tid].tmu)->TSTR = state ^ 1; } /* timer_start() - start a configured timer */ @@ -186,7 +154,7 @@ void timer_start(int tid) void timer_reload(int tid, uint32_t delay) { if(tid < 3) ((tmu_t *)timers[tid].tmu)->TCOR = delay; - else ((tmu_extra_t *)timers[tid].tmu)->TCOR = delay; + else ((etmu_t *)timers[tid].tmu)->TCOR = delay; } /* timer_pause() - stop a running timer */ @@ -205,10 +173,14 @@ void timer_stop(int tid) { tmu_t *t = timers[tid].tmu; t->TCR.UNIE = 0; + + /* Clear TCOR and TCNT */ + t->TCOR = 0xffffffff; + t->TCNT = 0xffffffff; } else { - tmu_extra_t *t = timers[tid].tmu; + etmu_t *t = timers[tid].tmu; t->TCR.UNIE = 0; /* Also clear TCOR and TCNT to avoid spurious interrupts */ @@ -232,7 +204,32 @@ int timer_timeout(volatile void *arg) { volatile int *x = arg; (*x)++; - return 0; + + /* Always stop after firing once */ + return 1; +} + +//--- +// Low-level functions +//--- + +/* timer_address() - get the address of a timer structure */ +void *timer_address(int timer, volatile uint8_t **TSTR_arg) +{ + if((uint)timer < 2 && TSTR_arg) *TSTR_arg = TSTR; + return (uint)timer < timer_count() ? timers[timer].tmu : NULL; +} + +/* timer_clear() - clear an ETMU flag and possibly stop the timer + @timer Timer ID, must be an ETMU + @stop Non-zero to stop the timer */ +void timer_clear(int timer, int stop) +{ + etmu_t *t = timers[timer].tmu; + do t->TCR.UNF = 0; + while(t->TCR.UNF); + + if(stop) timer_stop(timer); } //--- @@ -246,10 +243,9 @@ extern void inth_tmu_2(void); extern void inth_tmu_storage(void); /* Interrupt handlers provided by tmu/inth.s for extra timers */ -extern void inth_tmu_extra1(void); -extern void inth_tmu_extra2(void); -extern void inth_tmu_extra_help(void); -extern void inth_tmu_extra_others(void); +extern void inth_etmu2(void); +extern void inth_etmu_help(void); +extern void inth_etmux(void); #ifdef FX9860G static void driver_sh3(void) @@ -297,13 +293,13 @@ static void init(void) while(t->TCR.word); /* Standard timers: TCR is provided to the interrupt handler */ - timers[i].data->structure = &t->TCR; + timers[i].data->TCR = &t->TCR; } /* Clear the extra timers */ for(int i = 3; i < timer_count(); i++) { - tmu_extra_t *t = timers[i].tmu; + etmu_t *t = timers[i].tmu; /* Extra timers seem to generate interrupts as long as TCNT=0, regardless of TSTR. I'm not entirely sure about this weird @@ -329,17 +325,18 @@ static void init(void) for(int i = 3; i < limit; i++) { - void *handler = (i == 5) - ? inth_tmu_extra2 - : inth_tmu_extra_others; + void *handler = (i == 5) ? inth_etmu2 : inth_etmux; void *h = gint_inthandler(timers[i].event, handler, 32); - timers[i].data = h + 20; - timers[i].data->structure = timers[i].tmu; + timers[i].data = (i == 5) ? (h + 24) : (h + 20); + + if(i == 5) continue; + uint32_t *data_id = (h + 28); + *data_id = i; } /* Also install the helper handler */ - gint_inthandler(0xc60, inth_tmu_extra_help, 32); + gint_inthandler(0xc60, inth_etmu_help, 32); /* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */ gint_intlevel(0, 13); @@ -374,7 +371,7 @@ static void init(void) for(int i = 3; i < timer_count(); i++) { - tmu_extra_t *t = timers[i].tmu; + etmu_t *t = timers[i].tmu; int v = !(t->TCOR + 1) && !(t->TCNT + 1) && !(t->TSTR) @@ -412,7 +409,7 @@ static const char *tmu_status(void) int j = 5; for(int i = 3; i < timer_count(); i++) { - tmu_extra_t *t = timers[i].tmu; + etmu_t *t = timers[i].tmu; int v1 = (!(t->TCOR + 1)) | (!(t->TCNT + 1) << 1) | (!(t->TSTR) << 2); @@ -435,7 +432,7 @@ static const char *tmu_status(void) typedef struct { tmu_t std[3]; - tmu_extra_t extra[6]; + etmu_t extra[6]; uint8_t TSTR; } GPACKED(4) ctx_t; @@ -459,7 +456,7 @@ static void ctx_save(void *buf) for(int i = 0; i < timer_count() - 3; i++) { - tmu_extra_t *t = timers[i + 3].tmu; + etmu_t *t = timers[i + 3].tmu; ctx->extra[i].TCOR = t->TCOR; ctx->extra[i].TCNT = t->TCNT; ctx->extra[i].TCR.byte = t->TCR.byte; @@ -483,7 +480,7 @@ static void ctx_restore(void *buf) for(int i = 0; i < timer_count() - 3; i++) { - tmu_extra_t *t = timers[i + 3].tmu; + etmu_t *t = timers[i + 3].tmu; /* This thing is being unloaded while interrupts are disabled, so we don't have to heed for ctx->extra[i].TCNT = 0, which