gint/src/rtc/rtc.c

196 lines
4.0 KiB
C
Raw Normal View History

//---
// gint:rtc - Real-Time Clock
//---
#include <gint/rtc.h>
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/defs/types.h>
#include <gint/hardware.h>
#include <gint/mpu/rtc.h>
//---
// Real-Time Clock peripheral registers
//---
/* RTC address on SH7305, adjusted at startup on SH7337 and SH7355 */
GDATA static rtc_t *RTC = &SH7305_RTC;
/* Address of interrupt handler parameters */
GBSS struct {
int (*callback)(volatile void *arg);
volatile void *arg;
volatile uint8_t *RCR2;
} GPACKED(4) *params;
//---
// Time management
//---
/* int8(), int16() - convert BCD to integer */
static int int8(uint8_t bcd)
{
return (bcd & 0x0f) + 10 * (bcd >> 4);
}
static int int16(uint16_t bcd)
{
return (bcd & 0xf) + 10 * ((bcd >> 4) & 0xf) + 100 * ((bcd >> 8) & 0xf)
+ 1000 * (bcd >> 12);
}
/* bcd8(), bcd16() - convert integer to BCD
TODO: Use some kind of qdiv() for bcd8() and bcd16() */
static uint8_t bcd8(int integer)
{
integer %= 100;
return ((integer / 10) << 4) | (integer % 10);
}
static uint16_t bcd16(int integer)
{
integer %= 10000;
return (bcd8(integer / 100) << 8) | bcd8(integer % 100);
}
/* rtc_get_time() - read the current time from the RTC */
void rtc_get_time(rtc_time_t *time)
{
if(!time) return;
do {
RTC->RCR1.CF = 0;
time->seconds = int8(RTC->RSECCNT.byte);
time->minutes = int8(RTC->RMINCNT.byte);
time->hours = int8(RTC->RHRCNT.byte);
time->month_day = int8(RTC->RDAYCNT.byte);
time->month = int8(RTC->RMONCNT.byte);
time->year = int16(RTC->RYRCNT.word);
time->week_day = RTC->RWKCNT;
} while(RTC->RCR1.CF != 0);
}
/* rtc_set_time() - write a new current time to the RTC */
void rtc_set_time(const rtc_time_t *time)
{
if(!time) return;
int wday = (time->week_day < 7) ? (time->week_day) : (0);
do {
RTC->RCR1.CF = 0;
RTC->RSECCNT.byte = bcd8(time->seconds);
RTC->RMINCNT.byte = bcd8(time->minutes);
RTC->RHRCNT.byte = bcd8(time->hours);
RTC->RDAYCNT.byte = bcd8(time->month_day);
RTC->RMONCNT.byte = bcd8(time->month);
RTC->RYRCNT.word = bcd16(time->year);
RTC->RWKCNT = wday;
} while(RTC->RCR1.CF != 0);
}
//---
// RTC timer
//---
/* rtc_start_timer() - configure the RTC timer */
void rtc_start_timer(rtc_frequency_t freq,
int (*callback)(volatile void *arg), volatile void *arg)
{
/* Temporarily disable the interrupt */
RTC->RCR2.PES = RTC_NONE;
/* Set up the callback */
params->callback = callback;
params->arg = arg;
/* Clear the interrupt flag */
do RTC->RCR2.PEF = 0;
while(RTC->RCR2.PEF);
/* Enable the interrupt */
RTC->RCR2.PES = freq;
}
/* rtc_stop_timer() - stop the RTC timer */
void rtc_stop_timer(void)
{
RTC->RCR2.PES = RTC_NONE;
}
//---
// Driver initialization
//---
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
static void driver_sh3(void)
{
/* Adjust the address of the RTC */
RTC = &SH7705_RTC;
}
#endif
static void init(void)
{
/* Interrupt handlers provided by rtc/inth.s */
extern void inth_rtc_pri(void);
extern void inth_rtc_pri_helper(void);
/* Install the RTC interrupt handler */
GUNUSED void *h0, *h1;
h0 = gint_inthandler(0xaa0, inth_rtc_pri, 32);
h1 = gint_inthandler(0xae0, inth_rtc_pri_helper, 32);
params = h0 + 20;
params->RCR2 = &RTC->RCR2.byte;
/* Disable the periodic interrupt for now, but give it priority 5 */
RTC->RCR2.PES = RTC_NONE;
gint_intlevel(isSH3() ? 3 : 40, 5);
gint[HWRTC] = HW_LOADED | HWRTC_TIMER;
}
//---
// Context system for this driver
//---
typedef struct
{
uint8_t RCR1;
uint8_t RCR2;
} ctx_t;
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf)
{
ctx_t *ctx = buf;
ctx->RCR1 = RTC->RCR1.byte;
ctx->RCR2 = RTC->RCR2.byte;
}
static void ctx_restore(void *buf)
{
ctx_t *ctx = buf;
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
RTC->RCR1.byte = ctx->RCR1;
RTC->RCR2.byte = ctx->RCR2;
}
//---
// Driver structure definition
//---
gint_driver_t drv_rtc = {
.name = "RTC",
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
.init = init,
.sys_ctx = &sys_ctx,
core, tmu: add gint_switch(), return to menu, and improve timer code * Add the gint_switch() function which executes user-provided code from the system (CASIOWIN) context. * Added interrupt masks to the core context (should have been there long ago). * Added the gint_osmenu() function that switches out of gint to invoke GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This uses many CASIOWIN syscalls, but we don't care because gint is unloaded. Trickery is used to catch the key following the return in the add-in and/or display a new application frame before GetKeyWait() even finishes after coming back. This is only available on fx9860g for now. * Removed any public syscall definition to clear up interfaces. * Patched the DMA interruption problem in a weird way on fxcg50, a driver function will be used to do that properly eventually. * Changed the driver model to save driver contexts in preallocated spaces instead of on the stack for overall less risk. * Enabled return-to-menu with the MENU key on fx9860g in getkey(). * Changed the keyboard driver to emit releases before presses, as a return-to-menu acts as a press+release of different keys in a single driver frame, which confuses getkey(). * Fixed a really stupid bug in memcpy() that made the function really not work. Improvements in the timer driver: * Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>. * Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead. Only interrupt gate entries are left hardcoded. * Discovered that not only every write to the TCNT or TCR of an ETMU takes about 1/32k of a second (hinting at registers being powered by the same clock as the timer), but every write occuring while a previous write is pending is *lost*. This led to terrible bugs when switching ETMU contexts too fast in gint_switch(). * Removed an internal timer_address() function. * Overall simplified the handling of timers and the initialization step.
2020-05-10 14:03:41 +02:00
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save,
.ctx_restore = ctx_restore,
};
GINT_DECLARE_DRIVER(2, drv_rtc);