#include #include #include #include #include #include #include //--- // RTC periodic interrupt //--- static vhex_call_t rtc_call; /* rtc_periodic interrupt() : internal interrupt handler */ static void sh7305_rtc_periodic_inth(void) { int rc = vhex_call(rtc_call); /* Clear the interrupt flag */ do SH7305_RTC.RCR2.PEF = 0; while(SH7305_RTC.RCR2.PEF); /* Stop the interrupt if the callback returns non-zero */ if(rc) sh7305_rtc_periodic_disable(); } /* rtc_periodic_enable(): Enable the periodic interrupt */ bool sh7305_rtc_periodic_enable(int frequency, vhex_call_t callback) { /* Refuse to override an existing interrupt */ if(SH7305_RTC.RCR2.PES != RTC_NONE) return false; if(frequency == RTC_NONE) { sh7305_rtc_periodic_disable(); return true; } /* Temporarily disable the interrupt and set up the callback */ SH7305_RTC.RCR2.PES = RTC_NONE; memcpy(&rtc_call, &callback, sizeof(vhex_call_t)); /* Clear the interrupt flag */ do SH7305_RTC.RCR2.PEF = 0; while(SH7305_RTC.RCR2.PEF); /* Enable the interrupt */ SH7305_RTC.RCR2.PES = frequency; return true; } /* rtc_periodic_disable(): Stop the periodic interrupt */ void sh7305_rtc_periodic_disable(void) { SH7305_RTC.RCR2.PES = RTC_NONE; } //--- // User API //--- /* 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 */ 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); } /* sh7305_rtc_get_time(): Read the current time from the RTC */ int sh7305_rtc_get_time(rtc_time_t *time) { do { SH7305_RTC.RCR1.CF = 0; time->ticks = SH7305_RTC.R64CNT; time->seconds = int8(SH7305_RTC.RSECCNT.byte); time->minutes = int8(SH7305_RTC.RMINCNT.byte); time->hours = int8(SH7305_RTC.RHRCNT.byte); time->month_day = int8(SH7305_RTC.RDAYCNT.byte); time->month = int8(SH7305_RTC.RMONCNT.byte); time->year = int16(SH7305_RTC.RYRCNT.word); time->week_day = SH7305_RTC.RWKCNT; } while(SH7305_RTC.RCR1.CF != 0); return 0; } /* sh7305_rtc_set_time(): Set current time in the RTC */ int sh7305_rtc_set_time(rtc_time_t const *time) { int wday = time->week_day; if(wday >= 7) wday = 0; do { SH7305_RTC.RCR1.CF = 0; SH7305_RTC.RSECCNT.byte = bcd8(time->seconds); SH7305_RTC.RMINCNT.byte = bcd8(time->minutes); SH7305_RTC.RHRCNT.byte = bcd8(time->hours); SH7305_RTC.RDAYCNT.byte = bcd8(time->month_day); SH7305_RTC.RMONCNT.byte = bcd8(time->month); SH7305_RTC.RYRCNT.word = bcd16(time->year); SH7305_RTC.RWKCNT = wday; } while(SH7305_RTC.RCR1.CF != 0); return 0; } //--- // Define driver information //--- struct rtc_ctx { struct __sh7305_rtc_s rtc; }; static bool __rtc_hpowered(void) { return (SH7305_POWER.MSTPCR0.RTC == 0); } static int __rtc_hpoweron(void) { SH7305_POWER.MSTPCR0.RTC = 0; return 0; } static int __rtc_hpoweroff(void) { SH7305_POWER.MSTPCR0.RTC = 1; return 0; } /* __rtc_configure() : configure the RTC */ static void __rtc_configure(struct rtc_ctx *state) { /* Clear and disable the carry and alamr interrupts */ state->rtc.RCR1.CF = 0; state->rtc.RCR1.CIE = 0; state->rtc.RCR1.AIE = 0; state->rtc.RCR1.AF = 0; /* Disable the periodic interrupt flag */ state->rtc.RCR2.PEF = 0; state->rtc.RCR2.PES = RTC_NONE; state->rtc.RCR2.ADJ = 0; state->rtc.RCR2.RESET = 1; state->rtc.RCR2.START = 1; /* Disable year alarm check */ state->rtc.RCR3.BEN = 0; /* set the interrupt level (max) */ sh7305_intc_priority(INTC_RTC_ATI, 0); sh7305_intc_priority(INTC_RTC_CUI, 0); sh7305_intc_priority(INTC_RTC_PRI, 1); /* intall the RTC interupt handler */ sh7305_intc_install_inth_generic( 0xaa0, VHEX_CALL(&sh7305_rtc_periodic_inth) ); } /* __rtc_hsave() : save hardware information */ static void __rtc_hsave(struct rtc_ctx *state) { /* stop as soon as possible RTC counters */ state->rtc.RCR2.START = SH7305_RTC.RCR2.START; SH7305_RTC.RCR2.START = 0; state->rtc.RCR1.CF = SH7305_RTC.RCR1.CF; state->rtc.RCR1.CIE = SH7305_RTC.RCR1.CIE; state->rtc.RCR1.AIE = SH7305_RTC.RCR1.AIE; state->rtc.RCR1.AF = SH7305_RTC.RCR1.AF; state->rtc.RCR2.PEF = SH7305_RTC.RCR2.PEF; state->rtc.RCR2.PES = SH7305_RTC.RCR2.PES; state->rtc.RCR2.ADJ = SH7305_RTC.RCR2.ADJ; state->rtc.RCR3.BEN = SH7305_RTC.RCR3.BEN; } /* __rtc_hrestore() : restore hardware information */ static void __rtc_hrestore(struct rtc_ctx *state) { /* stop as soon as possible RTC counters */ SH7305_RTC.RCR2.START = 0; SH7305_RTC.RCR1.CF = state->rtc.RCR1.CF; SH7305_RTC.RCR1.CIE = state->rtc.RCR1.CIE; SH7305_RTC.RCR1.AIE = state->rtc.RCR1.AIE; SH7305_RTC.RCR1.AF = state->rtc.RCR1.AF; SH7305_RTC.RCR2.PEF = state->rtc.RCR2.PEF; SH7305_RTC.RCR2.PES = state->rtc.RCR2.PES; SH7305_RTC.RCR2.ADJ = state->rtc.RCR2.ADJ; SH7305_RTC.RCR3.BEN = state->rtc.RCR3.BEN; SH7305_RTC.RCR2.START = state->rtc.RCR2.START; } struct vhex_driver drv_rtc = { .name = "RTC", .hpowered = (void*)&__rtc_hpowered, .hpoweron = (void*)&__rtc_hpoweron, .hpoweroff = (void*)&__rtc_hpoweroff, .hsave = (void*)&__rtc_hsave, .hrestore = (void*)&__rtc_hrestore, .configure = (void*)&__rtc_configure, .state_size = sizeof(struct rtc_ctx), .flags = { .RTC = 1, .SHARED = 0, .UNUSED = 0, }, .module_data = &(struct rtc_drv_interface){ .rtc_get_time = &sh7305_rtc_get_time, .rtc_set_time = &sh7305_rtc_set_time, } }; VHEX_DECLARE_DRIVER(06, drv_rtc);