From c19cc87c6c9dcb54b614dbc81cbbed2f2825ef5c Mon Sep 17 00:00:00 2001 From: Heesub Shin Date: Mon, 3 Apr 2017 09:16:45 +0900 Subject: [PATCH] s5j/rtc: rework rtc driver This commit completely rewrites the RTC driver. Unnecessary code lines were removed and overcall callflow were designed, following the conventions at upstream. As a result, overall binary footprint reduced, when CONFIG_RTC_ALARM=n: Before text data bss dec filename 1736 4 1 1741 s5j_rtc.o After text data bss dec filename 1248 0 1 1249 s5j_rtc.o 108 4 0 112 s5j_rtc_lowerhalf.o Change-Id: Ic641828546827cdff51f8bbf2ee05eb3f501ac88 Signed-off-by: Heesub Shin --- apps/examples/testcase/le_tc/kernel/tc_clock.c | 6 +- os/arch/arm/src/s5j/Kconfig | 34 -- os/arch/arm/src/s5j/Make.defs | 3 + os/arch/arm/src/s5j/s5j_rtc.c | 799 ++++++------------------- os/arch/arm/src/s5j/s5j_rtc.h | 181 ++++-- os/arch/arm/src/s5j/s5j_rtc_lowerhalf.c | 320 ++++++++++ 6 files changed, 645 insertions(+), 698 deletions(-) create mode 100644 os/arch/arm/src/s5j/s5j_rtc_lowerhalf.c diff --git a/apps/examples/testcase/le_tc/kernel/tc_clock.c b/apps/examples/testcase/le_tc/kernel/tc_clock.c index 8b126cf..86a4c44 100644 --- a/apps/examples/testcase/le_tc/kernel/tc_clock.c +++ b/apps/examples/testcase/le_tc/kernel/tc_clock.c @@ -40,6 +40,10 @@ extern struct timespec g_basetime; #define SEC_10 10 #define NSEC_20 20 +#define START_YEAR 2017 +#define START_MONTH 4 +#define START_DAY 1 + /** * @fn :tc_clock_clock_initialize * @brief :Initialize the time value to match the RTC @@ -54,7 +58,7 @@ static void tc_clock_clock_initialize(void) g_basetime.tv_nsec = NSEC_20; time_t jdn = 0; - jdn = clock_calendar2utc(CONFIG_START_YEAR, CONFIG_START_MONTH, CONFIG_START_DAY); + jdn = clock_calendar2utc(START_YEAR, START_MONTH, START_DAY); clock_initialize(); diff --git a/os/arch/arm/src/s5j/Kconfig b/os/arch/arm/src/s5j/Kconfig index 760d472..5b299bf 100644 --- a/os/arch/arm/src/s5j/Kconfig +++ b/os/arch/arm/src/s5j/Kconfig @@ -174,40 +174,6 @@ config S5J_WDT_DEFTIMEOUT endmenu -menu "S5J RTC(Real Time Clock)" -config S5J_RTC - bool "S5J RTC support" - default n - select RTC - select RTC_DRIVER - select RTC_DATETIME - -config START_YEAR - int "year" - default 2016 - depends on S5J_RTC - -config START_MONTH - int "month" - default 8 - depends on S5J_RTC - -config START_DAY - int "day" - default 11 - depends on S5J_RTC - -config GREGORIAN_TIME - bool "S5J RTC for GREGORIAN TIME" - default n - depends on S5J_RTC - -config RTC_BASE_YEAR - int "Offset of year for S5JRTC(BASE + BCD)" - default 1900 - depends on S5J_RTC -endmenu #"S5J_RTC" - menu "S5J ADC" config S5J_ADC bool "S5J ADC support" diff --git a/os/arch/arm/src/s5j/Make.defs b/os/arch/arm/src/s5j/Make.defs index dd03a5f..a0d5d77 100644 --- a/os/arch/arm/src/s5j/Make.defs +++ b/os/arch/arm/src/s5j/Make.defs @@ -189,6 +189,9 @@ endif ifeq ($(CONFIG_RTC),y) CHIP_CSRCS += s5j_rtc.c +ifeq ($(CONFIG_RTC_DRIVER),y) +CHIP_CSRCS += s5j_rtc_lowerhalf.c +endif endif ifeq ($(CONFIG_S5J_MAILBOX),y) diff --git a/os/arch/arm/src/s5j/s5j_rtc.c b/os/arch/arm/src/s5j/s5j_rtc.c index bde9e6d..361f6f0 100644 --- a/os/arch/arm/src/s5j/s5j_rtc.c +++ b/os/arch/arm/src/s5j/s5j_rtc.c @@ -56,783 +56,378 @@ #include #include - #include -#include -#include -#include -#include +#include +#include +#include -#include "chip.h" #include "up_arch.h" -#include "s5j_rtc.h" -#include +#include "s5j_rtc.h" #ifdef CONFIG_RTC - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/*************************************************************************** - * This is the private type for the RTC state. It must be cast compatible - * with struct rtc_lowerhalf_s. - ***************************************************************************/ -#ifdef CONFIG_RTC_DRIVER -struct s5j_rtc_lowerhalf_s { - FAR const struct rtc_ops_s *ops; - - /* Data following is private to this driver and not visible outside of - * this file. - */ - - FAR struct s5j_rtc_lowerhalf_s *next; - uint32_t irqno; - uintptr_t base; -}; -#endif - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ -/* Prototypes for static methods in struct rtc_ops_s */ - -#ifdef CONFIG_RTC_DRIVER -static int s5j_rtc_rdtime(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *rtctime); -static int s5j_rtc_settime(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime); -#endif - -#ifdef CONFIG_RTC_ALARM - -static int s5j_rtc_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *almtime); -static int s5j_rtc_setalarm(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime); -static int s5j_rtc_aiealarm(FAR struct rtc_lowerhalf_s *lower, bool enable); +#if !defined(CONFIG_RTC_DATETIME) +#error "S5J does not support other than CONFIG_RTC_DATETIME." #endif -#ifdef CONFIG_RTC_PERIODIC -static int s5j_rtc_set_tick_period(FAR struct rtc_lowerhalf_s *lower, FAR unsigned long irqpfreq); -static int s5j_rtc_enable_tickirq(FAR struct rtc_lowerhalf_s *lower, bool enable); -#endif /**************************************************************************** * Private Data ****************************************************************************/ - -#ifdef CONFIG_RTC_DRIVER -static const struct rtc_ops_s g_rtc_ops = { - .rdtime = s5j_rtc_rdtime, - .settime = s5j_rtc_settime, -#ifdef CONFIG_RTC_ALARM - .almread = s5j_rtc_rdalarm, - .almset = s5j_rtc_setalarm, -#endif -#ifdef CONFIG_RTC_PERIODIC - .irqpread = NULL, - .irqpset = s5j_rtc_set_tick_period, -#endif -#ifdef CONFIG_RTC_ALARM - .aie = s5j_rtc_aiealarm, -#endif -#ifdef CONFIG_RTC_ONESEC - .uie = NULL, -#endif -#ifdef CONFIG_RTC_PERIODIC - .pie = s5j_rtc_enable_tickirq, -#endif -#ifdef CONFIG_RTC_EPOCHYEAR - .rdepoch = NULL, - .setepoch = NULL, -#endif #ifdef CONFIG_RTC_ALARM - .rdwkalm = NULL, - .setwkalm = NULL, +/* Callback to use when the alarm expires */ +static alarmcb_t g_alarmcb; #endif -#ifdef CONFIG_RTC_IOCTL - .ioctl = NULL, -#endif -}; -static struct rtc_lowerhalf_s g_rtc_lowerhalf = { - .ops = &g_rtc_ops, -}; - -#endif /**************************************************************************** * Public Data ****************************************************************************/ - +/* g_rtc_enabled is set true after the RTC has successfully initialized */ volatile bool g_rtc_enabled = false; /**************************************************************************** - * Private Functions + * Private Types ****************************************************************************/ +struct rtc_regvals_s +{ + uint8_t bcdsec; + uint8_t bcdmin; + uint8_t bcdhour; + uint8_t bcdday; +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + uint8_t bcddayweek; +#endif + uint8_t bcdmon; + uint16_t bcdyear; +}; /**************************************************************************** - * Name: rtc_bin2bcd - * - * Description: - * Converts binary time format into bcd - * - * Input Parameters: - * value - binary time value - * - * Returned Value: - * BCD converted value - * + * Private Functions ****************************************************************************/ static uint32_t rtc_bin2bcd(int value) { - uint32_t hundred = 0; - uint32_t ten = 0; + uint32_t hundreds, tens; + + hundreds = tens = 0; while (value >= 100) { - hundred++; + hundreds++; value -= 100; } while (value >= 10) { - ten++; + tens++; value -= 10; } - return (hundred << 8) | (ten << 4) | value; + return (hundreds << 8) | (tens << 4) | value; } -/**************************************************************************** - * Name: rtc_bcd2bin - * - * Description: - * Converts BCD time format into binary - * - * Input Parameters: - * value - BCD value - * - * Returned Value: - * binary converted value - * - ****************************************************************************/ static int rtc_bcd2bin(uint32_t value) { uint32_t tens = ((value >> 4) & 0xf) * 10; - uint32_t hundred = ((value >> 8) & 0xf) * 100; - return (int)(hundred + tens + (value & 0x0f)); + uint32_t hundreds = ((value >> 8) & 0xf) * 100; + return (int)(hundreds + tens + (value & 0xf)); } -/**************************************************************************** - * Name: __isr_rtc_tick - * - * Description: - * RTC TICK ISR - ****************************************************************************/ -void __isr_rtc_tick(void) +static void rtc_breakout(FAR const struct tm *tm, + FAR struct rtc_regvals_s *regvals) { - lldbg("%s\n", __func__); - HW_REG32(S5J_RTC_BASE, INTP) = RTC_INTP_TIC_MASK; + regvals->bcdsec = rtc_bin2bcd(tm->tm_sec); + regvals->bcdmin = rtc_bin2bcd(tm->tm_min); + regvals->bcdhour = rtc_bin2bcd(tm->tm_hour); + regvals->bcdday = rtc_bin2bcd(tm->tm_mday); +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + regvals->bcddayweek = rtc_bin2bcd(tm->tm_wday + 1); +#endif + regvals->bcdmon = rtc_bin2bcd(tm->tm_mon + 1); + regvals->bcdyear = rtc_bin2bcd(tm->tm_year); } -/**************************************************************************** - * Name: __isr_rtc_alarm - * - * Description: - * RTC ALARM ISR - ****************************************************************************/ -void __isr_rtc_alarm(void) +static void rtc_wprunlock(void) { - lldbg("%s\n", __func__); - HW_REG32(S5J_RTC_BASE, INTP) = RTC_INTP_ALM_MASK; + modifyreg32(S5J_RTC_RTCCON, 0, RTC_RTCCON_CTLEN_ENABLE); } -/**************************************************************************** - * Name: s5j_gettime - * - * Description: - * HW level get current time function call - * - * Input Parameters: - * base - base address on RTC IP SFRs - * t - pointer on tm structure where current time values will be stored - * - * Returned Value: - * OK - * - ****************************************************************************/ -int s5j_gettime(uintptr_t base, struct tm *t) +static void rtc_wprlock(void) { - uint32_t sec, min, hour, mday, mon, year; - irqstate_t flags; -#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - uint32_t wday; -#endif - flags = irqsave(); - - do { - sec = getreg32(base + BCDSEC); - min = getreg32(base + BCDMIN); - hour = getreg32(base + BCDHOUR); - mday = getreg32(base + BCDDAY); - mon = getreg32(base + BCDMON); - year = getreg32(base + BCDYEAR); -#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - wday = getreg32(base + BCDDAYWEEK); -#endif - } while (sec != getreg32(base + BCDSEC)); + modifyreg32(S5J_RTC_RTCCON, RTC_RTCCON_CTLEN_ENABLE, 0); +} - irqrestore(flags); +#if defined(CONFIG_RTC_ALARM) +static int rtc_alarm_handler(int irq, void *context, FAR void *arg) +{ + if (getreg32(S5J_RTC_INTP) & RTC_INTP_ALARM) { + /* Clear pending flags */ + putreg32(RTC_INTP_ALARM, S5J_RTC_INTP); - t->tm_sec = rtc_bcd2bin(sec); - t->tm_min = rtc_bcd2bin(min); - t->tm_hour = rtc_bcd2bin(hour); - t->tm_mday = rtc_bcd2bin(mday); - t->tm_mon = rtc_bcd2bin(mon) - 1; /* range 0-11, need to -1 */ - t->tm_year = rtc_bcd2bin(year); + /* Disable alarm */ + modifyreg32(S5J_RTC_RTCALM, RTC_RTCALM_ALMEN_MASK, + RTC_RTCALM_ALMEN_DISABLE); -#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - t->tm_wday = rtc_bcd2bin(wday); -#endif + /* Alarm callback */ + g_alarmcb(); + g_alarmcb = NULL; + } return OK; } +#endif /**************************************************************************** - * Name: s5j_settime + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: up_rtc_getdatetime * * Description: - * HW level set current time function call + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS + * during initialization to set up the system time when CONFIG_RTC and + * CONFIG_RTC_DATETIME are selected (and CONFIG_RTC_HIRES is not). + * + * NOTE: Some date/time RTC hardware is capability of sub-second accuracy. + * That sub-second accuracy is lost in this interface. However, since the + * system time is reinitialized on each power-up/reset, there will be no + * timing inaccuracy in the long run. * * Input Parameters: - * base - base address on RTC IP SFRs - * t - pointer on tm structure with values to set + * tp - The location to return the high resolution time value. * * Returned Value: - * OK + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -int s5j_settime(uintptr_t base, struct tm *t) +int up_rtc_getdatetime(FAR struct tm *tp) { - uint32_t sec; - uint32_t min; - uint32_t hour; - uint32_t mday; - uint32_t mon; - uint32_t year; irqstate_t flags; -#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - uint32_t wday; -#endif + struct rtc_regvals_s regvals; flags = irqsave(); - sec = rtc_bin2bcd(t->tm_sec); - min = rtc_bin2bcd(t->tm_min); - hour = rtc_bin2bcd(t->tm_hour); - mday = rtc_bin2bcd(t->tm_mday); - mon = rtc_bin2bcd(t->tm_mon); - year = rtc_bin2bcd(t->tm_year); + /* read bcd counters */ + do { + regvals.bcdsec = getreg32(S5J_RTC_BCDSEC); + regvals.bcdmin = getreg32(S5J_RTC_BCDMIN); + regvals.bcdhour = getreg32(S5J_RTC_BCDHOUR); + regvals.bcdday = getreg32(S5J_RTC_BCDDAY); #if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - wday = rtc_bin2bcd(t->tm_wday); + regvals.bcddayweek = getreg32(S5J_RTC_BCDDAYWEEK); #endif + regvals.bcdmon = getreg32(S5J_RTC_BCDMON); + regvals.bcdyear = getreg32(S5J_RTC_BCDYEAR); + } while (regvals.bcdsec != getreg32(S5J_RTC_BCDSEC)); - putreg32(sec, base + BCDSEC); - putreg32(min, base + BCDMIN); - putreg32(hour, base + BCDHOUR); - putreg32(mday, base + BCDDAY); - putreg32(mon, base + BCDMON); - putreg32(year, base + BCDYEAR); + irqrestore(flags); + + tp->tm_sec = rtc_bcd2bin(regvals.bcdsec); + tp->tm_min = rtc_bcd2bin(regvals.bcdmin); + tp->tm_hour = rtc_bcd2bin(regvals.bcdhour); + tp->tm_mday = rtc_bcd2bin(regvals.bcdday); #if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) - putreg32(wday, base + BCDDAYWEEK); + tp->tm_wday = rtc_bcd2bin(regvals.bcddayweek) - 1; #endif - - irqrestore(flags); + tp->tm_mon = rtc_bcd2bin(regvals.bcdmon) - 1; + tp->tm_year = rtc_bcd2bin(regvals.bcdyear); return OK; } -#ifdef CONFIG_RTC_ALARM - /**************************************************************************** - * Name: s5j_getalarm + * Name: up_rtc_setdatetime * * Description: - * HW level get alarm function call + * Set the RTC to the provided time. RTC implementations which provide + * up_rtc_getdatetime() (CONFIG_RTC_DATETIME is selected) should provide + * this function. * * Input Parameters: - * base - base address on RTC IP SFRs - * t - pointer on tm structure where alarm values will be stored + * tp - the time to use * * Returned Value: - * OK + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -static int s5j_getalarm(uintptr_t base, struct tm *t) +int up_rtc_setdatetime(FAR struct tm *tm) { - uint32_t sec; - uint32_t min; - uint32_t hour; - uint32_t mday; - uint32_t mon; - uint32_t year; irqstate_t flags; - flags = irqsave(); - - do { - sec = getreg32(base + ALMSEC); - min = getreg32(base + ALMMIN); - hour = getreg32(base + ALMHOUR); - mday = getreg32(base + ALMDAY); - mon = getreg32(base + ALMMON); - year = getreg32(base + ALMYEAR); - } while (sec != getreg32(base + ALMSEC)); + struct rtc_regvals_s regvals; - irqrestore(flags); - - t->tm_sec = rtc_bcd2bin(sec); - t->tm_min = rtc_bcd2bin(min); - t->tm_hour = rtc_bcd2bin(hour); - t->tm_mday = rtc_bcd2bin(mday); - t->tm_mon = rtc_bcd2bin(mon) - 1; /* range 0-11, need to -1 */ - t->tm_year = rtc_bcd2bin(year); - return OK; -} - -/**************************************************************************** - * Name: s5j_setalarm - * - * Description: - * HW level set alarm function call - * - * Input Parameters: - * base - base address on RTC IP SFRs - * t - pointer on tm structure with values to set - * - * Returned Value: - * OK - * - ****************************************************************************/ -static int s5j_setalarm(uintptr_t base, struct tm *t) -{ - uint32_t sec; - uint32_t min; - uint32_t hour; - uint32_t mday; - uint32_t mon; - uint32_t year; - irqstate_t flags; + rtc_breakout(tm, ®vals); flags = irqsave(); - sec = rtc_bin2bcd(t->tm_sec); - min = rtc_bin2bcd(t->tm_min); - hour = rtc_bin2bcd(t->tm_hour); - mday = rtc_bin2bcd(t->tm_mday); - mon = rtc_bin2bcd(t->tm_mon); - year = rtc_bin2bcd(t->tm_year); + rtc_wprunlock(); - putreg32(sec, base + ALMSEC); - putreg32(min, base + ALMMIN); - putreg32(hour, base + ALMHOUR); - putreg32(mday, base + ALMDAY); - putreg32(mon, base + ALMMON); - putreg32(year, base + ALMYEAR); - - irqrestore(flags); - - return OK; -} - -#endif /* CONFIG_RTC_ALARM */ - -/**************************************************************************** - * Name: s5j_rtc_rdtime - * - * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_ALARM is enabled. - * Gets current time. - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * rtctime - pointer on rtc_time structure where current time - * will be stored - * - * Returned Value: - * OK - * - ****************************************************************************/ -#ifdef CONFIG_RTC_DRIVER -static int s5j_rtc_rdtime(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *rtctime) -{ - return s5j_gettime(S5J_RTC_BASE, (FAR struct tm *)rtctime); -} + /* update BCD counters */ + putreg32(regvals.bcdsec, S5J_RTC_BCDSEC); + putreg32(regvals.bcdmin, S5J_RTC_BCDMIN); + putreg32(regvals.bcdhour, S5J_RTC_BCDHOUR); + putreg32(regvals.bcdday, S5J_RTC_BCDDAY); +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + putreg32(regvals.bcddayweek, S5J_RTC_BCDDAYWEEK); #endif + putreg32(regvals.bcdmon, S5J_RTC_BCDMON); + putreg32(regvals.bcdyear, S5J_RTC_BCDYEAR); -#ifdef CONFIG_RTC_ALARM -/**************************************************************************** - * Name: s5j_rtc_rdalarm - * - * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_ALARM is enabled. - * Set ALARM time. - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * almtime - pointer on rtc_time structure where current alarm time - * will be stored - * - * Returned Value: - * OK - * - ****************************************************************************/ -static int s5j_rtc_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *almtime) -{ - return s5j_getalarm(S5J_RTC_BASE, (FAR struct tm *)almtime); -} - -/**************************************************************************** - * Name: s5j_rtc_setalarm - * - * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_ALARM is enabled. - * Set ALARM time. - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * almtime - pointer on rtc_time structure with desired alarm time - * - * Returned Value: - * OK - * - ****************************************************************************/ -static int s5j_rtc_setalarm(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *almtime) -{ - return s5j_setalarm(S5J_RTC_BASE, (FAR struct tm *)almtime); -} + rtc_wprlock(); -/**************************************************************************** - * Name: s5j_rtc_aiealarm - * - * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_ALARM is enabled. - * Enables ALARM interrupt. - * RTC ISR __isr_rtc_alarm will be called. - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * enable - enable od disable alarm interrupt - * - * Returned Value: - * OK - * - ****************************************************************************/ -static int s5j_rtc_aiealarm(FAR struct rtc_lowerhalf_s *lower, bool enable) -{ - if (enable) { - irq_attach(IRQ_TOP_RTC_ALARM, (xcpt_t) __isr_rtc_alarm, NULL); - up_enable_irq(IRQ_TOP_RTC_ALARM); - - HW_REG32(S5J_RTC_BASE, RTCALM) |= RTC_GLB_ALM_EN; /* Enable Alarm Global */ - } else { - HW_REG32(S5J_RTC_BASE, RTCALM) &= ~RTC_GLB_ALM_EN; /* Disable Alarm Global */ - } - return OK; - - return OK; -} -#endif /* CONFIG_RTC_ALARM */ + irqrestore(flags); -#ifdef CONFIG_RTC_PERIODIC -/**************************************************************************** - * Name: s5j_rtc_set_tick_period - * - * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_PERIODIC is enabled. - * Defines period (not freq) of rtc interrupts with 1 sec resolution. - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * irqfreq - periodic interrupt period, in seconds - * - * Returned Value: - * OK - * - ****************************************************************************/ -static int s5j_rtc_set_tick_period(FAR struct rtc_lowerhalf_s *lower, FAR unsigned long irqpfreq) -{ - /* Set TICKCNT value for every 1 sec */ - /* - * RTC Tick Interrupt Period - * Period = (n + 1) /(Tick clock source frequency) second. (n = tick counter value - * Refer to Tick Interrupt Resolution in S5JT200 UM - */ - HW_REG32(S5J_RTC_BASE, TICCNT) = RTC_CLK_FREQ * irqpfreq + 1; /* in sec */ return OK; } /**************************************************************************** - * Name: s5j_rtc_enable_tickirq + * Name: up_rtc_settime * * Description: - * Part of rtc_ops_s structure, used when CONFIG_RTC_PERIODIC is enabled. - * Enables periodic interrupts, with defined period - * by s5j_rtc_set_tick_period function call. - * RTC ISR __isr_rtc_tick will be periodically called. + * Set the RTC to the provided time. All RTC implementations must be able + * to set their time based on a standard timespec. * * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * enable - enable od disable periodic interrupt + * tp - the time to use * * Returned Value: - * OK + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -static int s5j_rtc_enable_tickirq(FAR struct rtc_lowerhalf_s *lower, bool enable) +int up_rtc_settime(FAR const struct timespec *tp) { - if (enable) { - irq_attach(IRQ_TOP_RTC_TIC, (xcpt_t) __isr_rtc_tick, NULL); - up_enable_irq(IRQ_TOP_RTC_TIC); + struct tm t; - HW_REG32(S5J_RTC_BASE, RTCCON) |= RTCCON_TICEN; /* Enable Tick Timer */ - } else { - HW_REG32(S5J_RTC_BASE, RTCCON) &= ~RTCCON_TICEN; /* disable Tick Timer */ - } - return OK; -} -#endif + /* convert timepsec to tm */ + gmtime_r(&tp->tv_sec, &t); -/**************************************************************************** - * Name: s5j_rtc_settime - * - * Description: - * Initialize the hardware RTC per the selected configuration. - * This function is called once during the OS initialization sequence - * - * Input Parameters: - * lower - Lower half rtc structure pointer, corresponded to s5j_rtc - * rtctime - pointer on rtc_time structure with values to set - * - * Returned Value: - * OK - * - ****************************************************************************/ -#ifdef CONFIG_RTC_DRIVER -static int s5j_rtc_settime(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime) -{ - return s5j_settime(S5J_RTC_BASE, (FAR struct tm *)rtctime); + return up_rtc_setdatetime(&t); } -#endif - -/************************************************************************************ - * Public Functions - ************************************************************************************/ +#ifdef CONFIG_RTC_ALARM /**************************************************************************** - * Name: up_rtc_initialize + * Name: s5j_rtc_setalarm * * Description: - * Initialize the hardware RTC per the selected configuration. - * This function is called once during the OS initialization sequence. + * Set up an alarm * * Input Parameters: - * None + * tp - the time to set the alarm + * callback - the function to call when the alarm expires * * Returned Value: - * OK + * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ -int s5j_rtc_initialize(void) +int s5j_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback) { - irqstate_t flags; struct tm t; + struct rtc_regvals_s regvals; + irqstate_t flags; - /* No clocking setup need be performed. The Real-time Clock is continuously clocked - * at 32768 Hz (SCLK). The Power Management Controller has no effect on RTC behavior. - */ - - flags = irqsave(); - - /* Set RTC input clock to external XTAL oscillator (32768HZ) explicily*/ - HW_REG32(0x800A0000, 0x554) = 0x1; - - /* Reset RTC and Tick timer. */ - HW_REG32(S5J_RTC_BASE, RTCCON) = 0x0; - - /* Enable RTC counter, Tick clock 32768, Tick Timer enable */ - HW_REG32(S5J_RTC_BASE, RTCCON) |= RTCCON_CTLEN | RTCCON_TICCKSEL(clk_32768HZ); + /* convert timepsec to tm */ + gmtime_r(&tp->tv_sec, &t); - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = CONFIG_START_DAY; - t.tm_mon = CONFIG_START_MONTH; - t.tm_year = CONFIG_START_YEAR - CONFIG_RTC_BASE_YEAR; + rtc_breakout(&t, ®vals); - s5j_settime(S5J_RTC_BASE, &t); + flags = irqsave(); -#ifdef CONFIG_RTC_ALARM + if (g_alarmcb != NULL) { + irqrestore(flags); + return -EBUSY; + } - irq_attach(IRQ_TOP_RTC_ALARM, (xcpt_t) __isr_rtc_alarm, NULL); + /* Set the alarm */ + putreg32(regvals.bcdsec, S5J_RTC_ALMSEC); + putreg32(regvals.bcdmin, S5J_RTC_ALMMIN); + putreg32(regvals.bcdhour, S5J_RTC_ALMHOUR); + putreg32(regvals.bcdday, S5J_RTC_ALMDAY); + putreg32(regvals.bcdmon, S5J_RTC_ALMMON); + putreg32(regvals.bcdyear, S5J_RTC_ALMYEAR); + + /* Enable RTC alarm */ + putreg32(RTC_RTCALM_ALMEN_ENABLE | RTC_RTCALM_YEAREN_ENABLE | + RTC_RTCALM_MONEN_ENABLE | RTC_RTCALM_DAYEN_ENABLE | + RTC_RTCALM_HOUREN_ENABLE | RTC_RTCALM_MINEN_ENABLE | + RTC_RTCALM_SECEN_ENABLE, + S5J_RTC_RTCALM); + + /* save the callback function pointer */ + g_alarmcb = callback; + irq_attach(IRQ_TOP_RTC_ALARM, rtc_alarm_handler, NULL); up_enable_irq(IRQ_TOP_RTC_ALARM); - /* Set Alarm after 1 min */ - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = CONFIG_START_DAY; - t.tm_mon = CONFIG_START_MONTH; - t.tm_year = CONFIG_START_YEAR - CONFIG_RTC_BASE_YEAR; - s5j_setalarm(S5J_RTC_BASE, &t); - - HW_REG32(S5J_RTC_BASE, RTCALM) = RTC_YEAR_ALM_EN | RTC_MONTH_ALM_EN | RTC_DAY_ALM_EN | RTC_HOUR_ALM_EN | RTC_MIN_ALM_EN | RTC_SEC_ALM_EN; - -#endif - irqrestore(flags); return OK; } /**************************************************************************** - * Name: up_rtc_getdatetime + * Name: s5j_rtc_cancelalarm * * Description: - * Get the current date and time from the date/time RTC. This interface - * is only supported by the date/time RTC hardware implementation. - * It is used to replace the system timer. It is only used by the RTOS during - * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME - * are selected (and CONFIG_RTC_HIRES is not). Due to low resoluton of RTC, - * subsecond values will be lost. - * - * Input Parameters: - * tp - pointer on tm type structure where date/time will be saved - * - * Returned Value: - * OK - * - ****************************************************************************/ - -int up_rtc_getdatetime(FAR struct tm *tp) -{ - return s5j_gettime(S5J_RTC_BASE, tp); -} - -/**************************************************************************** - * Name: up_rtc_time - * - * Description: - * Get the current time in seconds. This is similar to the standard time() - * function. This interface is only required if the low-resolution RTC/counter - * hardware implementation selected. It is only used by the RTOS during - * initialization to set up the system time when CONFIG_RTC is set but neither - * CONFIG_RTC_HIRES nor CONFIG_RTC_DATETIME are set. + * Cancel a pending alarm * * Input Parameters: * None * * Returned Value: - * time_t value in seconds - * - ****************************************************************************/ - -#ifndef CONFIG_RTC_HIRES -time_t up_rtc_time(void) -{ - struct tm t; - - s5j_gettime(S5J_RTC_BASE, &t); - - return mktime(&t); -} -#endif - -/**************************************************************************** - * Name: up_rtc_gettime - * - * Description: - * Returns current time value in timespec structure. - * Exists when RTC_HIRES configuration is enabled. - * nsec value is always 0 due to low actual resolution of RTC counter. - * - * Input Parameters: - * tp - pointer on timespec structure where time value will be stored. - * - * Returned Value: - * OK + * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ - -#ifdef CONFIG_RTC_HIRES -int up_rtc_gettime(FAR struct timespec *tp) +int s5j_rtc_cancelalarm(void) { - struct tm t; + if (g_alarmcb == NULL) + return -ENODATA; - s5j_gettime(S5J_RTC_BASE, &t); + /* Disable alarm */ + modifyreg32(S5J_RTC_RTCALM, RTC_RTCALM_ALMEN_MASK, + RTC_RTCALM_ALMEN_DISABLE); - tp->tv_sec = mktime(&t); - tp->tv_nsec = 0; + g_alarmcb = NULL; return OK; } -#endif +#endif /* CONFIG_RTC_ALARM */ /**************************************************************************** - * Name: up_rtc_settime + * Name: up_rtc_initialize * * Description: - * Function called by OS to set RTC date/time value. + * Initialize the hardware RTC per the selected configuration. This + * function is called once during the OS initialization sequence. * * Input Parameters: - * tp - pointer on timespec structure with desired value. + * None * * Returned Value: - * OK + * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ - -int up_rtc_settime(FAR const struct timespec *tp) +int up_rtc_initialize(void) { - FAR struct tm newtime; + /* Disable RTC Alarm */ + putreg32(0, S5J_RTC_RTCALM); - (void)gmtime_r(&tp->tv_sec, &newtime); + /* Clear interrupt pending (if any) */ + putreg32(RTC_INTP_ALARM | RTC_INTP_TIMETIC0, S5J_RTC_INTP); - return s5j_settime(S5J_RTC_BASE, &newtime); + rtc_wprunlock(); -} + /* Reset to all initial state */ + modifyreg32(S5J_RTC_RTCCON, + RTC_RTCCON_TICCKSEL0_MASK | RTC_RTCCON_CLKRST_MASK | + RTC_RTCCON_CNTSEL_MASK | RTC_RTCCON_CLKSEL_MASK, + RTC_RTCCON_TICCKSEL0_32768HZ | + RTC_RTCCON_CLKRST_ENABLE | + RTC_RTCCON_CNTSEL_MERGE_BCDCNT | + RTC_RTCCON_CLKSEL_DIV32768); -/**************************************************************************** - * Name: up_rtc_initialize - * - * Description: - * Function called by OS to initialize RTC. - * Initialyizes HW and sublmits lower half OPS structure. - * - * Input Parameters: - * None - * - * Returned Value: - * OK - * - ****************************************************************************/ - -int up_rtc_initialize(void) -{ -#ifdef CONFIG_S5J_RTC - int ret; - s5j_rtc_initialize(); - ret = rtc_initialize(0, &g_rtc_lowerhalf); - if (ret < 0) { - lldbg("Failed the RTC initialize. (%d)\n", ret); - return ret; - } + rtc_wprlock(); - /* Set RTC enable flag */ g_rtc_enabled = true; -#else - lldbg("no support Hardware RTC functions\n"); -#endif + return OK; } - -#endif +#endif /* CONFIG_RTC */ diff --git a/os/arch/arm/src/s5j/s5j_rtc.h b/os/arch/arm/src/s5j/s5j_rtc.h index 50d3793..7f8e5ca 100644 --- a/os/arch/arm/src/s5j/s5j_rtc.h +++ b/os/arch/arm/src/s5j/s5j_rtc.h @@ -56,81 +56,140 @@ /**************************************************************************** * Included Files ****************************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +#define S5J_RTC_INTP 0x80100030 +#define S5J_RTC_RTCCON 0x80100040 +#define S5J_RTC_TICCNT0 0x80100044 +#define S5J_RTC_RTCALM 0x80100050 +#define S5J_RTC_ALMSEC 0x80100054 +#define S5J_RTC_ALMMIN 0x80100058 +#define S5J_RTC_ALMHOUR 0x8010005C +#define S5J_RTC_ALMDAY 0x80100060 +#define S5J_RTC_ALMMON 0x80100064 +#define S5J_RTC_ALMYEAR 0x80100068 +#define S5J_RTC_BCDSEC 0x80100070 +#define S5J_RTC_BCDMIN 0x80100074 +#define S5J_RTC_BCDHOUR 0x80100078 +#define S5J_RTC_BCDDAY 0x8010007C +#define S5J_RTC_BCDDAYWEEK 0x80100080 +#define S5J_RTC_BCDMON 0x80100084 +#define S5J_RTC_BCDYEAR 0x80100088 +#define S5J_RTC_CURTICCNT0 0x80100090 -/* Register definition for RTC */ -#define INTP 0x0030 -#define RTCCON 0x0040 -#define TICCNT 0x0044 -#define RTCALM 0x0050 -#define ALMSEC 0x0054 -#define ALMMIN 0x0058 -#define ALMHOUR 0x005C -#define ALMDAY 0x0060 -#define ALMMON 0x0064 -#define ALMYEAR 0x0068 -#define BCDSEC 0x0070 -#define BCDMIN 0x0074 -#define BCDHOUR 0x0078 -#define BCDDAY 0x007C -#define BCDDAYWEEK 0x0080 -#define BCDMON 0x0084 -#define BCDYEAR 0x0088 -#define CURTICCNT 0x0090 - -#define RTC_GLB_ALM_EN (0x1 << 6) -#define RTC_YEAR_ALM_EN (0x1 << 5) -#define RTC_MONTH_ALM_EN (0x1 << 4) -#define RTC_DAY_ALM_EN (0x1 << 3) -#define RTC_HOUR_ALM_EN (0x1 << 2) -#define RTC_MIN_ALM_EN (0x1 << 1) -#define RTC_SEC_ALM_EN (0x1 << 0) +/* S5J_RTC_INTP */ +#define RTC_INTP_ALARM (0x1 << 1) +#define RTC_INTP_TIMETIC0 (0x1 << 0) +/* S5J_RTC_RTCCON */ +#define RTC_RTCCON_CLKOUTEN (0x1 << 9) +#define RTC_RTCCON_TICKEN0 (0x1 << 8) +#define RTC_RTCCON_TICCKSEL0_MASK (0xf << 4) +#define RTC_RTCCON_TICCKSEL0_32768HZ (0x0 << 4) +#define RTC_RTCCON_TICCKSEL0_16384HZ (0x1 << 4) +#define RTC_RTCCON_TICCKSEL0_8192HZ (0x2 << 4) +#define RTC_RTCCON_TICCKSEL0_4096HZ (0x3 << 4) +#define RTC_RTCCON_TICCKSEL0_2048HZ (0x4 << 4) +#define RTC_RTCCON_TICCKSEL0_1024HZ (0x5 << 4) +#define RTC_RTCCON_TICCKSEL0_512HZ (0x6 << 4) +#define RTC_RTCCON_TICCKSEL0_256HZ (0x7 << 4) +#define RTC_RTCCON_TICCKSEL0_128HZ (0x8 << 4) +#define RTC_RTCCON_TICCKSEL0_64HZ (0x9 << 4) +#define RTC_RTCCON_TICCKSEL0_32HZ (0xa << 4) +#define RTC_RTCCON_TICCKSEL0_16HZ (0xb << 4) +#define RTC_RTCCON_TICCKSEL0_8HZ (0xc << 4) +#define RTC_RTCCON_TICCKSEL0_4HZ (0xd << 4) +#define RTC_RTCCON_TICCKSEL0_2HZ (0xe << 4) +#define RTC_RTCCON_TICCKSEL0_1HZ (0xf << 4) +#define RTC_RTCCON_CLKRST_MASK (0x1 << 3) +#define RTC_RTCCON_CLKRST_ENABLE (0x0 << 3) +#define RTC_RTCCON_CLKRST_DISABLE (0x1 << 3) +#define RTC_RTCCON_CNTSEL_MASK (0x1 << 2) +#define RTC_RTCCON_CNTSEL_MERGE_BCDCNT (0x0 << 2) +#define RTC_RTCCON_CNTSEL_RESERVED (0x1 << 2) +#define RTC_RTCCON_CLKSEL_MASK (0x1 << 1) +#define RTC_RTCCON_CLKSEL_DIV32768 (0x0 << 1) +#define RTC_RTCCON_CLKSEL_RESERVED (0x1 << 1) +#define RTC_RTCCON_CTLEN_MASK (0x1 << 0) +#define RTC_RTCCON_CTLEN_DISABLE (0x0 << 0) +#define RTC_RTCCON_CTLEN_ENABLE (0x1 << 0) -#define RTCCON_CLKOUTEN (1 << 9) -#define RTCCON_TICEN (1 << 8) -#define RTCCON_TICCKSEL(x) ((x & 0xf) << 4) -#define RTCCON_CLKSTOP (1 << 3) -#define RTCCON_CTLEN (1 << 0) +/* S5J_RTC_RTCALM */ +#define RTC_RTCALM_ALMEN_MASK (0x1 << 6) +#define RTC_RTCALM_ALMEN_DISABLE (0x0 << 6) +#define RTC_RTCALM_ALMEN_ENABLE (0x1 << 6) +#define RTC_RTCALM_YEAREN_MASK (0x1 << 5) +#define RTC_RTCALM_YEAREN_DISABLE (0x0 << 5) +#define RTC_RTCALM_YEAREN_ENABLE (0x1 << 5) +#define RTC_RTCALM_MONEN_MASK (0x1 << 4) +#define RTC_RTCALM_MONEN_DISABLE (0x0 << 4) +#define RTC_RTCALM_MONEN_ENABLE (0x1 << 4) +#define RTC_RTCALM_DAYEN_MASK (0x1 << 3) +#define RTC_RTCALM_DAYEN_DISABLE (0x0 << 3) +#define RTC_RTCALM_DAYEN_ENABLE (0x1 << 3) +#define RTC_RTCALM_HOUREN_MASK (0x1 << 2) +#define RTC_RTCALM_HOUREN_DISABLE (0x0 << 2) +#define RTC_RTCALM_HOUREN_ENABLE (0x1 << 2) +#define RTC_RTCALM_MINEN_MASK (0x1 << 1) +#define RTC_RTCALM_MINEN_DISABLE (0x0 << 1) +#define RTC_RTCALM_MINEN_ENABLE (0x1 << 1) +#define RTC_RTCALM_SECEN_MASK (0x1 << 0) +#define RTC_RTCALM_SECEN_DISABLE (0x0 << 0) +#define RTC_RTCALM_SECEN_ENABLE (0x1 << 0) -#define RTC_CLK_FREQ 32768 - -typedef enum t_ticcksel { - clk_32768HZ = 0, - clk_16384HZ, - clk_8192HZ, - clk_4096HZ, - clk_2048HZ, - clk_1024HZ, - clk_512HZ, - clk_256HZ, - clk_128HZ, - clk_64HZ, - clk_32HZ, - clk_16HZ, - clk_8HZ, - clk_4HZ, - clk_2HZ, - clk_1HZ, -} d_ticcksel; +#ifndef __ASSEMBLY__ +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif -#define RTC_INTP_TIC_MASK (1 << 0) -#define RTC_INTP_ALM_MASK (1 << 1) +/**************************************************************************** + * Public Types + ****************************************************************************/ +/* The form of an alarm callback */ +typedef void (*alarmcb_t)(void); /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: s5j_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the S5J. + * General usage: + * + * #include + * #include "s5j_rtc.h" + * + * struct rtc_lowerhalf_s *lower; + * lower = s5j_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ +#ifdef CONFIG_RTC_DRIVER +FAR struct rtc_lowerhalf_s *s5j_rtc_lowerhalf(void); +#endif /* CONFIG_RTC_DRIVER */ - -#ifdef __cplusplus +#undef EXTERN +#if defined(__cplusplus) } #endif -#endif /* __ARCH_ARM_SRC_S5J_S5J_RTC_H */ + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_S5J_S5J_RTC_H */ diff --git a/os/arch/arm/src/s5j/s5j_rtc_lowerhalf.c b/os/arch/arm/src/s5j/s5j_rtc_lowerhalf.c new file mode 100644 index 0000000..237abc8 --- /dev/null +++ b/os/arch/arm/src/s5j/s5j_rtc_lowerhalf.c @@ -0,0 +1,320 @@ +/**************************************************************************** + * + * Copyright 2016 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +/**************************************************************************** + * arch/arm/src/s5j/s5j_rtc.c + * + * Copyright (C) 2009-2010, 2014-2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "up_arch.h" + +#include "s5j_rtc.h" + +#ifdef CONFIG_RTC_DRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ +#ifdef CONFIG_RTC_ALARM +struct s5j_cbinfo_s +{ + volatile rtc_alarm_callback_t cb; /* Callback when the alarm expires */ + volatile FAR void *priv; /* Private arg passed to callback */ +}; +#endif /* CONFIG_RTC_ALARM */ + +struct s5j_lowerhalf_s +{ + /* + * This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM + */ + FAR const struct rtc_ops_s *ops; + +#ifdef CONFIG_RTC_ALARM + /* + * Data following is private to this driver and not visible + * outside of this file. + */ + struct s5j_cbinfo_s cbinfo; +#endif /* CONFIG_RTC_ALARM */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: s5j_alarm_callback + * + * Description: + * This is the function that is called from the RTC driver when the alarm + * goes off. It just invokes the upper half drivers callback. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ +#ifdef CONFIG_RTC_ALARM +static struct s5j_lowerhalf_s g_rtc_lowerhalf; +static void s5j_alarm_callback(void) +{ + FAR struct s5j_cbinfo_s *cbinfo = &g_rtc_lowerhalf.cbinfo; + + /* + * Sample and clear the callback information to minimize the window + * in time in which race conditions can occur. + */ + rtc_alarm_callback_t cb = (rtc_alarm_callback_t)cbinfo->cb; + FAR void *arg = (FAR void *)cbinfo->priv; + + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Perform the callback */ + if (cb != NULL) { + cb(arg, 0); + } +} +#endif /* CONFIG_RTC_ALARM */ + +static int rtc_rdtime(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_time *rtctime) +{ + return up_rtc_getdatetime((FAR struct tm *)rtctime); +} + +static int rtc_settime(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_time *rtctime) +{ + return up_rtc_setdatetime((FAR struct tm *)rtctime); +} + +#ifdef CONFIG_RTC_ALARM +static int rtc_setalarm(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo) +{ + FAR struct s5j_lowerhalf_s *priv; + FAR struct s5j_cbinfo_s *cbinfo; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->id == 0); + priv = (FAR struct s5j_lowerhalf_s *)lower; + + if (alarminfo->id == 0) { + struct timespec ts; + + /* Convert the RTC time to a timespec (1 second accuracy) */ + ts.tv_sec = mktime((FAR struct tm *)&alarminfo->time); + ts.tv_nsec = 0; + + /* Remember the callback information */ + cbinfo = &priv->cbinfo; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* And set the alarm */ + ret = s5j_rtc_setalarm(&ts, s5j_alarm_callback); + if (ret < 0) { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + } + + return ret; +} + +static int rtc_setrelative(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setrelative_s *alarminfo) +{ + FAR struct s5j_lowerhalf_s *priv; + FAR struct s5j_cbinfo_s *cbinfo; + struct tm time; + FAR struct timespec ts; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL && alarminfo->id == 0); + priv = (FAR struct s5j_lowerhalf_s *)lower; + + if (alarminfo->id == 0 && alarminfo->reltime > 0) { + /* + * Disable preemption while we do this so that we don't have + * to worry about being suspended and working on an old time. + */ + sched_lock(); + + /* Get the current time in seconds */ + + ret = up_rtc_getdatetime(&time); + if (ret < 0) { + sched_unlock(); + return ret; + } + + ts.tv_sec = mktime(&time); + ts.tv_nsec = 0; + + /* + * Add the seconds offset. Add one to the number of seconds + * because we are unsure of the phase of the timer. + */ + ts.tv_sec += (alarminfo->reltime + 1); + + /* Remember the callback information */ + cbinfo = &priv->cbinfo; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* And set the alarm */ + ret = s5j_rtc_setalarm(&ts, s5j_alarm_callback); + if (ret < 0) { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + + sched_unlock(); + } + + return ret; +} + +static int rtc_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) +{ + FAR struct s5j_lowerhalf_s *priv; + FAR struct s5j_cbinfo_s *cbinfo; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(alarmid == 0); + priv = (FAR struct s5j_lowerhalf_s *)lower; + + /* Nullify callback information to reduce window for race conditions */ + cbinfo = &priv->cbinfo; + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Then, cancel the alarm */ + return s5j_rtc_cancelalarm(); +} +#endif /* CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ +static const struct rtc_ops_s g_rtc_ops = { + .rdtime = rtc_rdtime, + .settime = rtc_settime, +#ifdef CONFIG_RTC_ALARM + .setalarm = rtc_setalarm, + .setrelative = rtc_setrelative, + .cancelalarm = rtc_cancelalarm, +#endif +#ifdef CONFIG_RTC_IOCTL + .ioctl = NULL, +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .destroy = NULL, +#endif +}; + +static struct s5j_lowerhalf_s g_rtc_lowerhalf = { + .ops = &g_rtc_ops, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: s5j_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the S5J. + * General usage: + * + * #include + * #include "s5j_rtc.h" + * + * struct rtc_lowerhalf_s *lower; + * lower = s5j_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ +FAR struct rtc_lowerhalf_s *s5j_rtc_lowerhalf(void) +{ + return (FAR struct rtc_lowerhalf_s *)&g_rtc_lowerhalf; +} +#endif /* CONFIG_RTC_DRIVER */ -- 2.7.4