gint/src/rtc/rtc.c

197 lines
4.3 KiB
C

//---
// gint:rtc - Real-Time Clock
//---
#include <gint/rtc.h>
#include <gint/drivers.h>
#include <gint/drivers/states.h>
#include <gint/intc.h>
#include <gint/defs/types.h>
#include <gint/hardware.h>
#include <gint/mpu/rtc.h>
#include <stdarg.h>
#undef rtc_start_timer
//---
// Real-Time Clock peripheral registers
//---
/* RTC address on SH7305, adjusted at startup on SH7337 and SH7355 */
static rtc_t *RTC = &SH7305_RTC;
/* Address of interrupt handler parameters */
GBSS static struct {
void *function;
uint32_t arg;
} GPACKED(4) *timer_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)
{
do {
RTC->RCR1.CF = 0;
time->ticks = RTC->R64CNT;
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(): Set current time in the RTC */
void rtc_set_time(rtc_time_t const *time)
{
int wday = time->week_day;
if(wday >= 7) wday = 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 */
int rtc_start_timer(int freq, timer_callback_t f, ...)
{
/* Refuse to override an existing callback */
if(RTC->RCR2.PES != RTC_NONE) return 1;
if(freq == RTC_NONE) return 1;
/* Get the argument */
va_list args;
va_start(args, f);
uint32_t arg = va_arg(args, uint32_t);
va_end(args);
/* Temporarily disable the interrupt and set up the callback */
RTC->RCR2.PES = RTC_NONE;
timer_params->function = f.v;
timer_params->arg = arg;
/* Clear the interrupt flag */
do RTC->RCR2.PEF = 0;
while(RTC->RCR2.PEF);
/* Enable the interrupt */
RTC->RCR2.PES = freq;
return 0;
}
/* rtc_stop_timer(): Stop the RTC timer */
void rtc_stop_timer(void)
{
RTC->RCR2.PES = RTC_NONE;
}
//---
// Driver initialization
//---
static void constructor(void)
{
/* Adjust the address of the RTC */
if(isSH3()) RTC = &SH7705_RTC;
}
static void configure(void)
{
/* Disable the carry and alarm interrupts (they share their IPR bits
with the periodic interrupt, which we want to enable) */
RTC->RCR1.byte = 0;
/* Clear the periodic interrupt flag */
RTC->RCR2.PEF = 0;
/* 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 = intc_handler(0xaa0, inth_rtc_pri, 32);
h1 = intc_handler(0xae0, inth_rtc_pri_helper, 32);
timer_params = h0 + 20;
volatile uint8_t **RCR2_pointer = h1 + 28;
*RCR2_pointer = &RTC->RCR2.byte;
/* Disable the RTC interrupts for now. Give them priority 1; higher
priorities cause freezes when going back to the system on SH3
(TODO: Find out about the RTC interrupt problem on SH3) */
RTC->RCR2.PES = RTC_NONE;
intc_priority(INTC_RTC_PRI, 1);
}
//---
// State and driver metadata
//---
static void hsave(rtc_state_t *s)
{
s->RCR1 = RTC->RCR1.byte;
s->RCR2 = RTC->RCR2.byte;
}
static void hrestore(rtc_state_t const *s)
{
RTC->RCR1.byte = s->RCR1 & 0x18;
RTC->RCR2.byte = s->RCR2 & 0x7f;
}
gint_driver_t drv_rtc = {
.name = "RTC",
.constructor = constructor,
.configure = configure,
.hsave = (void *)hsave,
.hrestore = (void *)hrestore,
.state_size = sizeof(rtc_state_t),
};
GINT_DECLARE_DRIVER(13, drv_rtc);