| /**************************************************************************** |
| * arch/renesas/src/rx65n/rx65n_rtc.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <time.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/irq.h> |
| #include <nuttx/wqueue.h> |
| #include <nuttx/compiler.h> |
| #include <arch/board/board.h> |
| #include <rx65n_rtc.h> |
| #include "renesas_internal.h" |
| #include "nuttx/compiler.h" |
| #ifdef CONFIG_RX65N_RTC |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| # define rx65n_getreg(addr) getreg8(addr) |
| # define rx65n_putreg(val,addr) putreg8(val,addr) |
| |
| /* Configuration ************************************************************/ |
| |
| #ifdef CONFIG_RTC_HIRES |
| # ifndef CONFIG_RTC_FREQUENCY |
| # error "CONFIG_RTC_FREQUENCY is required for CONFIG_RTC_HIRES" |
| # endif |
| #else |
| # ifndef CONFIG_RTC_FREQUENCY |
| # define CONFIG_RTC_FREQUENCY 1 |
| # endif |
| # if CONFIG_RTC_FREQUENCY != 1 |
| # error "Only lo-res CONFIG_RTC_FREQUENCY of 1Hz is supported" |
| # endif |
| # endif |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| void up_enable_irq(int irq); |
| void up_disable_irq(int irq); |
| void rtc_prd_interrupt(void); |
| static uint32_t rtc_dec2bcd(uint8_t value); |
| #if defined (CONFIG_RTC_HIRES) || defined (CONFIG_RTC_ALARM) || defined (CONFIG_RTC_DATETIME) |
| static int rtc_bcd2dec(uint32_t value); |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Callback to use when the alarm expires */ |
| |
| #ifdef CONFIG_RTC_ALARM |
| struct alm_cbinfo_s |
| { |
| volatile alm_callback_t ac_cb; /* Client callback function */ |
| volatile void *ac_arg; /* Argument to pass with the callback function */ |
| }; |
| #endif |
| |
| /* Callback to use when the periodic interrupt expires */ |
| |
| #ifdef CONFIG_RTC_PERIODIC |
| struct prd_cbinfo_s |
| { |
| volatile periodiccb_t prd_cb; /* Client callback function */ |
| volatile void *prd_arg; /* Argument to pass with the callback function */ |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_ALARM |
| /* Callback to use when an EXTI is activated */ |
| |
| static struct alm_cbinfo_s g_alarmcb; |
| #endif |
| |
| #ifdef CONFIG_RTC_PERIODIC |
| static struct prd_cbinfo_s g_periodiccb; |
| #endif |
| |
| /* Callback to use when the cary interrupt expires */ |
| |
| #ifdef CONFIG_RX65N_CARRY |
| static carrycb_t g_carrycb; |
| #endif |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /* g_rtc_enabled is set true after the RTC has successfully initialized */ |
| |
| volatile bool g_rtc_enabled = false; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rtc_dumpregs |
| * |
| * Description: |
| * Disable RTC write protection |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_RTC_INFO |
| static void rtc_dumpregs(const char *msg) |
| { |
| rtcinfo("%s:\n", msg); |
| rtcinfo(" 64-Hz Counter: %08x\n", getreg8(RX65N_RTC_R64CNT)); |
| rtcinfo(" Second Counter: %08x\n", getreg8(RX65N_RTC_RSECCNT)); |
| rtcinfo(" Minute Counter: %08x\n", getreg8(RX65N_RTC_RMINCNT)); |
| rtcinfo(" Hour Counter: %08x\n", getreg8(RX65N_RTC_RHRCNT)); |
| rtcinfo(" Day-of-Week Counter: %08x\n", getreg8(RX65N_RTC_RWKCNT)); |
| rtcinfo(" Date Counter: %08x\n", getreg8(RX65N_RTC_RDAYCNT)); |
| rtcinfo(" Month Counter: %08x\n", getreg8(RX65N_RTC_RMONCNT)); |
| rtcinfo(" Year Counter: %08x\n", getreg8(RX65N_RTC_RYRCNT)); |
| rtcinfo(" Second Alarm Register: %08x\n", getreg8(RX65N_RTC_RSECAR)); |
| rtcinfo(" Minute Alarm Register: %08x\n", getreg8(RX65N_RTC_RMINAR)); |
| rtcinfo(" Hour Alarm Register: %08x\n", getreg8(RX65N_RTC_RHRAR)); |
| rtcinfo(" Day-of-Week Alarm Register: %08x\n", getreg8(RX65N_RTC_RWKAR)); |
| rtcinfo(" Date Alarm Register: %08x\n", getreg8(RX65N_RTC_RDAYAR)); |
| rtcinfo(" Month Alarm Register: %08x\n", getreg8(RX65N_RTC_RMONAR)); |
| rtcinfo(" Year Alarm Register: %08x\n", getreg8(RX65N_RTC_RYRAR)); |
| rtcinfo(" Year Alarm Enable Register: %08x\n", getreg8(RX65N_RTC_RYRAREN)); |
| rtcinfo(" RTC Control Register 1: %08x\n", getreg8(RX65N_RTC_RCR1)); |
| rtcinfo(" RTC Control Register 2: %08x\n", getreg8(RX65N_RTC_RCR2)); |
| rtcinfo(" RTC Control Register 3: %08x\n", getreg8(RX65N_RTC_RCR3)); |
| rtcinfo(" RTC Control Register 4: %08x\n", getreg8(RX65N_RTC_RCR4)); |
| } |
| #else |
| # define rtc_dumpregs(msg) |
| #endif |
| |
| /**************************************************************************** |
| * Name: rtc_dumptime |
| * |
| * Description: |
| * Disable RTC write protection |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_RTC_INFO |
| static void rtc_dumptime(struct tm *tp, const char *msg) |
| { |
| rtcinfo("%s:\n", msg); |
| rtcinfo(" tm_sec: %08x\n", tp->tm_sec); |
| rtcinfo(" tm_min: %08x\n", tp->tm_min); |
| rtcinfo(" tm_hour: %08x\n", tp->tm_hour); |
| rtcinfo(" tm_mday: %08x\n", tp->tm_mday); |
| rtcinfo(" tm_mon: %08x\n", tp->tm_mon); |
| rtcinfo(" tm_year: %08x\n", tp->tm_year); |
| } |
| #else |
| # define rtc_dumptime(tp, msg) |
| #endif |
| |
| /**************************************************************************** |
| * Name: rtc_dec2bcd |
| * |
| * Description: |
| * Converts decimal value to BCD format |
| * |
| * Input Parameters: |
| * value - The byte to be converted. |
| * |
| * Returned Value: |
| * The value in BCD representation |
| * |
| ****************************************************************************/ |
| |
| static uint32_t rtc_dec2bcd(uint8_t value) |
| { |
| return (uint8_t) ((((value / 10) << 4) & 0xf0) | (value % 10)); |
| } |
| |
| /**************************************************************************** |
| * Name: rtc_bcd2dec |
| * |
| * Description: |
| * Convert from 2 digit BCD to decimal. |
| * |
| * Input Parameters: |
| * value - The BCD value to be converted. |
| * |
| * Returned Value: |
| * The value in binary representation |
| * |
| ****************************************************************************/ |
| |
| #if defined (CONFIG_RTC_HIRES) || defined (CONFIG_RTC_ALARM) || defined (CONFIG_RTC_DATETIME) |
| static int rtc_bcd2dec(uint32_t value) |
| { |
| return (int) ((((value & 0xf0) >> 4) * 10) + (value & 0x0f)); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rtc_interrupt |
| * |
| * Description: |
| * RTC interrupt service routine |
| * |
| * Input Parameters: |
| * irq - The IRQ number that generated the interrupt |
| * context - Architecture specific register save information. |
| * |
| * Returned Value: |
| * Zero (OK) on success; A negated errno value on failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_ALARM |
| static int rtc_alm_interrupt(int irq, void *context, void *arg) |
| { |
| struct alm_cbinfo_s *cbinfo; |
| alm_callback_t cb; |
| uint8_t source = rx65n_getreg(RX65N_RTC_RCR1); |
| if ((source & RTC_ALARM_INT_ENABLE) != 0) |
| { |
| /* Alarm callback */ |
| |
| cbinfo = &g_alarmcb; |
| cb = cbinfo->ac_cb; |
| arg = (void *)cbinfo->ac_arg; |
| cbinfo->ac_cb = NULL; |
| cbinfo->ac_arg = NULL; |
| cb(arg, 0); |
| } |
| |
| up_disable_irq(RX65N_ALM_IRQ); |
| return 0; |
| } |
| #endif |
| |
| #ifdef CONFIG_RTC_PERIODIC |
| static int rtc_periodic_interrupt(int irq, void *context, void *arg) |
| { |
| struct prd_cbinfo_s *cbinfo; |
| periodiccb_t cb; |
| uint8_t source = rx65n_getreg(RX65N_RTC_RCR1); |
| if ((source & RTC_PERIOD_INT_ENABLE) != 0) |
| { |
| /* Periodic callback */ |
| |
| cbinfo = &g_periodiccb; |
| cb = cbinfo->prd_cb; |
| arg = (void *)cbinfo->prd_arg; |
| cb(arg, 0); |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_CARRY |
| static int rtc_carry_interrupt(int irq, void *context, void *arg) |
| { |
| uint8_t source = rx65n_getreg(RX65N_RTC_RCR1); |
| if ((source & RTC_CARRY_INT_ENABLE) != 0) |
| { |
| /* Carry callback */ |
| |
| g_carrycb(); |
| } |
| |
| return 0; |
| } |
| |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: up_rtc_initialize |
| * |
| * Description: |
| * Initialize the hardware RTC per the selected configuration. |
| * This function is |
| * called once during the OS initialization sequence |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| int up_rtc_initialize(void) |
| { |
| uint8_t regval; |
| uint32_t temp_byte; |
| rtc_dumpregs("On reset"); |
| |
| /* Disable ALM, PRD and CUP interrupts */ |
| |
| up_disable_irq(RX65N_ALM_IRQ); |
| up_disable_irq(RX65N_PRD_IRQ); |
| up_disable_irq(RX65N_INTB176_IRQ); |
| |
| /* Set RTC clock source as sub clock */ |
| |
| regval = getreg8(RX65N_RTC_RCR4); |
| regval |= RTC_RCR4_RCKSEL; |
| rx65n_putreg(regval, RX65N_RTC_RCR4); |
| |
| /* Set sub-clock oscillator */ |
| |
| regval = getreg8(RX65N_RTC_RCR3); |
| regval |= RTC_RCR3_RTCEN; |
| rx65n_putreg(regval, RX65N_RTC_RCR3); |
| |
| while (1U != RTC.RCR3.BIT.RTCEN) |
| { |
| /* waiting for sub-clock oscillator is operating */ |
| } |
| |
| /* Stop all counters */ |
| |
| rx65n_putreg(0, RX65N_RTC_RCR2); |
| while (0U != RTC.RCR2.BIT.START) |
| { |
| /* Ensure the clock is stopped while configuring it. */ |
| } |
| |
| /* Select count mode */ |
| |
| regval = getreg8(RX65N_RTC_RCR2); |
| regval |= RTC_RCR2_CNTMD; |
| rx65n_putreg(regval, RX65N_RTC_RCR2); |
| |
| while (0U != RTC.RCR2.BIT.CNTMD) |
| { |
| /* Wait for the calendar count mode complete setting */ |
| } |
| |
| /* Execute RTC software reset */ |
| |
| regval = getreg8(RX65N_RTC_RCR2); |
| regval |= RTC_RCR2_RESET; |
| rx65n_putreg(regval, RX65N_RTC_RCR2); |
| |
| while (0U != RTC.RCR2.BIT.RESET) |
| { |
| /* Wait for the reset to complete */ |
| } |
| |
| /* Stop RTC counter */ |
| |
| regval = getreg8(RX65N_RTC_RCR2); |
| regval &= ~(RTC_RCR2_START); |
| rx65n_putreg(regval, RX65N_RTC_RCR2); |
| |
| while (0U != RTC.RCR2.BIT.START) |
| { |
| /* Ensure the clock is stopped while configuring it. */ |
| } |
| |
| /* Clear ALM,PRD,CUP IR */ |
| |
| IR(RTC, ALM) = 0U; |
| IR(RTC, PRD) = 0U; |
| IR(PERIB, INTB176) = 0U; |
| |
| /* After a reset is generated, write to the RTC register |
| * when six cycles of the count source have elapsed |
| */ |
| |
| up_udelay(RX65N_RTC_WAIT_PERIOD); |
| |
| /* Start the counter and set 24hr mode */ |
| |
| regval = getreg8(RX65N_RTC_RCR2); |
| regval |= (RTC_RCR2_HR24); |
| rx65n_putreg(regval, RX65N_RTC_RCR2); |
| |
| /* Setting RADJ register */ |
| |
| regval = getreg8(RX65N_RTC_RADJ); |
| regval |= (RTC_RADJ_INITVALUE); |
| rx65n_putreg(regval, RX65N_RTC_RADJ); |
| |
| /* Setting AADJE and AADJP register */ |
| |
| regval = getreg8(RX65N_RTC_RCR2); |
| regval |= (RTC_RCR2_AADJE | RTC_RCR2_AADJP); |
| rx65n_putreg(regval, RX65N_RTC_RCR2); |
| g_rtc_enabled = true; |
| UNUSED(temp_byte); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: up_rtc_gettime |
| * |
| * Description: |
| * Get the current date and time from the date/time RTC. |
| * Input Parameters: |
| * tp - The location to return the high resolution time value. |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_RTC_HIRES) |
| int up_rtc_gettime(struct timespec *tp) |
| { |
| uint8_t weekcnt; |
| uint8_t daycnt; |
| uint8_t monthcnt; |
| uint8_t yearcnt; |
| uint8_t seccnt; |
| uint8_t mincnt; |
| uint8_t hrcnt; |
| uint8_t tmp_week; |
| uint8_t tmp_day; |
| uint8_t tmp_month; |
| uint8_t tmp_year; |
| uint16_t bcd_years; |
| uint8_t regval; |
| struct tm t; |
| |
| if (RTC.RCR2.BIT.START == 0) |
| { |
| RTC.RCR2.BIT.START = 1; |
| } |
| |
| do |
| { |
| weekcnt = getreg8(RX65N_RTC_RWKCNT); |
| daycnt = getreg8(RX65N_RTC_RDAYCNT); |
| monthcnt = getreg8(RX65N_RTC_RMONCNT); |
| yearcnt = getreg8(RX65N_RTC_RYRCNT); |
| seccnt = getreg8(RX65N_RTC_RSECCNT); |
| mincnt = getreg8(RX65N_RTC_RMINCNT); |
| hrcnt = getreg8(RX65N_RTC_RHRCNT); |
| tmp_week = getreg8(RX65N_RTC_RWKCNT); |
| tmp_day = getreg8(RX65N_RTC_RDAYCNT); |
| tmp_month = getreg8(RX65N_RTC_RMONCNT); |
| tmp_year = getreg8(RX65N_RTC_RYRCNT); |
| } |
| |
| while (tmp_week != weekcnt && tmp_day != daycnt && |
| tmp_month != monthcnt && tmp_year != yearcnt); |
| |
| /* Disable ICU CUP interrupt */ |
| |
| up_disable_irq(CONFIG_RX65N_PERIB); |
| |
| /* Enable RTC CUP interrupt */ |
| |
| regval = getreg8(RX65N_RTC_RCR1); |
| regval |= (RTC_RCR1_CUP); |
| rx65n_putreg(regval, RX65N_RTC_RCR1); |
| |
| do |
| { |
| /* Clear carry flag in ICU */ |
| |
| IR(PERIB, INTB176) = 0U; |
| |
| /* Read and convert RTC registers; |
| * mask off unknown bits and hour am/pm. |
| */ |
| |
| /* Seconds. (0-59) */ |
| |
| t.tm_sec = rtc_bcd2dec((uint8_t) (RTC.RSECCNT.BYTE & 0x7fu)); |
| t.tm_min = rtc_bcd2dec((uint8_t) (RTC.RMINCNT.BYTE & 0x7fu)); |
| t.tm_hour = rtc_bcd2dec((uint8_t) (RTC.RHRCNT.BYTE & 0x3fu)); |
| t.tm_mday = rtc_bcd2dec(RTC.RDAYCNT.BYTE); |
| t.tm_mon = rtc_bcd2dec(RTC.RMONCNT.BYTE) - 1; |
| |
| /* Years since 2000 */ |
| |
| bcd_years = (uint16_t) RTC.RYRCNT.WORD; |
| |
| t.tm_year = rtc_bcd2dec((uint8_t) (bcd_years & 0xff)) + 100; |
| |
| tp->tv_sec = timegm(&t); |
| tp->tv_nsec = 0; |
| } |
| |
| while (1 == IR(PERIB, INTB176)); |
| UNUSED(hrcnt); |
| UNUSED(mincnt); |
| UNUSED(seccnt); |
| return OK; |
| } |
| #endif |
| |
| int rx65n_rtc_setdatetime(const struct tm *tp) |
| { |
| int i; |
| volatile uint8_t dummy_byte; |
| volatile uint16_t dummy_word; |
| |
| /* Break out the time values (note that the time is set only to units of |
| * seconds) |
| */ |
| |
| /* gmtime_r(&tp->tv_sec, &tp); */ |
| |
| rtc_dumptime(&tp, "Setting time"); |
| |
| /* Then write the broken out values to the RTC */ |
| |
| /* Convert the struct tm format to RTC time register fields. |
| * |
| * struct tm TIMR register |
| * tm_sec 0-61* SEC (0-59) |
| * tm_min 0-59 MIN (0-59) |
| * tm_hour 0-23 HOUR (0-23) |
| * |
| * *To allow for leap seconds. But these never actuall happen. |
| */ |
| |
| /* Stop all counters */ |
| |
| RTC.RCR2.BIT.START = 0U; |
| while (0U != RTC.RCR2.BIT.START) |
| { |
| /* Ensure the clock is stopped while configuring it. */ |
| } |
| |
| /* Execute RTC software reset */ |
| |
| RTC.RCR2.BIT.RESET = 1U; |
| while (1U != RTC.RCR2.BIT.RESET) |
| { |
| /* Wait for the reset to complete */ |
| } |
| |
| RTC.RCR2.BIT.HR24 = 1; |
| |
| /* Set time */ |
| |
| /* Set seconds. (0-59) */ |
| |
| RTC.RSECCNT.BYTE = rtc_dec2bcd((uint8_t)tp->tm_sec); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RSECCNT.BYTE; |
| } |
| |
| /* Set minutes (0-59) */ |
| |
| RTC.RMINCNT.BYTE = rtc_dec2bcd((uint8_t) tp->tm_min); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMINCNT.BYTE; |
| } |
| |
| /* Set hours. (0-23) */ |
| |
| RTC.RHRCNT.BYTE = rtc_dec2bcd((uint8_t) tp->tm_hour); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RHRCNT.BYTE; |
| } |
| |
| /* Set the date */ |
| |
| /* Day of the week (0-6, 0=Sunday) */ |
| |
| #if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) |
| RTC.RWKCNT.BYTE = rtc_dec2bcd((uint8_t) tp->tm_wday); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RWKCNT.BYTE; |
| } |
| #endif |
| |
| /* Day of the month (1-31) */ |
| |
| RTC.RDAYCNT.BYTE = rtc_dec2bcd((uint8_t) tp->tm_mday); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RDAYCNT.BYTE; |
| } |
| |
| /* Month. (1-12, 1=January) */ |
| |
| RTC.RMONCNT.BYTE = rtc_dec2bcd((uint8_t) (tp->tm_mon + 1)); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMONCNT.BYTE; |
| } |
| |
| /* Year. (00-99) */ |
| |
| RTC.RYRCNT.WORD = (uint16_t) (rtc_dec2bcd((uint8_t) |
| ((tp->tm_year + 1900) % 100))); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_word = RTC.RYRCNT.WORD; |
| } |
| |
| RTC.RCR2.BIT.START = 1U; |
| |
| rtc_dumpregs("New time setting"); |
| UNUSED(dummy_word); |
| UNUSED(dummy_byte); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: up_rtc_settime |
| * |
| * Description: |
| * Set the RTC to the provided time. All RTC implementations |
| * must be able to |
| * set their time based on a standard timespec. |
| * |
| * Input Parameters: |
| * tp - the time to use |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| int up_rtc_settime(const struct timespec *tp) |
| { |
| struct tm newtime; |
| int i; |
| volatile uint8_t dummy_byte; |
| volatile uint16_t dummy_word; |
| |
| /* Break out the time values (note that the time is set only to units of |
| * seconds) |
| */ |
| |
| gmtime_r(&tp->tv_sec, &newtime); |
| rtc_dumptime(&newtime, "Setting time"); |
| |
| /* Then write the broken out values to the RTC */ |
| |
| /* Convert the struct tm format to RTC time register fields. |
| * |
| * struct tm TIMR register |
| * tm_sec 0-61* SEC (0-59) |
| * tm_min 0-59 MIN (0-59) |
| * tm_hour 0-23 HOUR (0-23) |
| * |
| * *To allow for leap seconds. But these never actuall happen. |
| */ |
| |
| /* Stop all counters */ |
| |
| RTC.RCR2.BIT.START = 0U; |
| while (0U != RTC.RCR2.BIT.START) |
| { |
| /* Ensure the clock is stopped while configuring it. */ |
| } |
| |
| /* Execute RTC software reset */ |
| |
| RTC.RCR2.BIT.RESET = 1U; |
| while (1U != RTC.RCR2.BIT.RESET) |
| { |
| /* Wait for the reset to complete */ |
| } |
| |
| RTC.RCR2.BIT.HR24 = 1; |
| |
| /* Set time */ |
| |
| /* Set seconds. (0-59) */ |
| |
| RTC.RSECCNT.BYTE = rtc_dec2bcd((uint8_t)newtime.tm_sec); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RSECCNT.BYTE; |
| } |
| |
| /* Set minutes (0-59) */ |
| |
| RTC.RMINCNT.BYTE = rtc_dec2bcd((uint8_t) newtime.tm_min); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMINCNT.BYTE; |
| } |
| |
| /* Set hours. (0-23) */ |
| |
| RTC.RHRCNT.BYTE = rtc_dec2bcd((uint8_t) newtime.tm_hour); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RHRCNT.BYTE; |
| } |
| |
| /* Set the date */ |
| |
| /* Day of the week (0-6, 0=Sunday) */ |
| |
| RTC.RWKCNT.BYTE = rtc_dec2bcd((uint8_t) newtime.tm_wday); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RWKCNT.BYTE; |
| } |
| |
| /* Day of the month (1-31) */ |
| |
| RTC.RDAYCNT.BYTE = rtc_dec2bcd((uint8_t) newtime.tm_mday); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RDAYCNT.BYTE; |
| } |
| |
| /* Month. (1-12, 1=January) */ |
| |
| RTC.RMONCNT.BYTE = rtc_dec2bcd((uint8_t) (newtime.tm_mon + 1)); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMONCNT.BYTE; |
| } |
| |
| /* Year. (00-99) */ |
| |
| RTC.RYRCNT.WORD = (uint16_t) (rtc_dec2bcd((uint8_t) |
| ((newtime.tm_year + 1900) % 100))); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_word = RTC.RYRCNT.WORD; |
| } |
| |
| RTC.RCR2.BIT.START = 1U; |
| |
| rtc_dumpregs("New time setting"); |
| UNUSED(dummy_word); |
| UNUSED(dummy_byte); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: rx65n_rtc_getalarmdatetime |
| * |
| * Description: |
| * Get the current date and time for a RTC alarm. |
| * |
| * Input Parameters: |
| * reg - RTC alarm register |
| * tp - The location to return the high resolution time value. |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_ALARM |
| static int rx65n_rtc_getalarmdatetime(struct tm *tp) |
| { |
| uint8_t bcd_years; |
| DEBUGASSERT(tp != NULL); |
| |
| tp->tm_sec = rtc_bcd2dec((uint8_t) (RTC.RSECAR.BYTE & 0x7fu)); |
| tp->tm_min = rtc_bcd2dec((uint8_t) (RTC.RMINAR.BYTE & 0x7fu)); |
| tp->tm_hour = rtc_bcd2dec((uint8_t) (RTC.RHRAR.BYTE & 0x3fu)); |
| tp->tm_mday = rtc_bcd2dec(RTC.RDAYAR.BYTE & 0x3fu); |
| tp->tm_mon = rtc_bcd2dec(RTC.RMONAR.BYTE & 0x1fu) - 1; |
| |
| /* Years since 2000 */ |
| |
| bcd_years = (uint8_t) RTC.RYRAR.WORD; |
| |
| tp->tm_year = rtc_bcd2dec((uint8_t) (bcd_years & 0xff)) + 100; |
| return 0; |
| } |
| |
| #endif |
| /**************************************************************************** |
| * Name: rx65n_rtc_rdalarm |
| * |
| * Description: |
| * Query an alarm configured in hardware. |
| * |
| * Input Parameters: |
| * alminfo - Information about the alarm configuration. |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_ALARM |
| int rx65n_rtc_rdalarm(struct alm_rdalarm_s *alminfo) |
| { |
| int ret = -EINVAL; |
| DEBUGASSERT(alminfo != NULL); |
| ret = rx65n_rtc_getalarmdatetime((struct tm *)alminfo->ar_time); |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_rtc_setalarm |
| * |
| * Description: |
| * Set up an alarm. |
| * |
| * Input Parameters: |
| * tp - the time to set the alarm |
| * callback - the function to call when the alarm expires. |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_ALARM |
| int rx65n_rtc_setalarm(struct alm_setalarm_s *alminfo) |
| { |
| irqstate_t flags; |
| uint8_t dummy_byte; |
| uint8_t dummy_word; |
| uint8_t i; |
| |
| /* Is there already something waiting on the ALARM? */ |
| |
| flags = enter_critical_section(); |
| |
| /* Save the callback info */ |
| |
| g_alarmcb.ac_cb = alminfo->as_cb; |
| g_alarmcb.ac_arg = alminfo->as_arg; |
| |
| IEN(RTC, ALM) = 0U; |
| |
| /* Attach the Alarm Interrupt */ |
| |
| irq_attach(RX65N_ALM_IRQ, rtc_alm_interrupt, NULL); |
| |
| /* Start RTC counter */ |
| |
| RTC.RCR2.BIT.START = 1U; |
| while (1U != RTC.RCR2.BIT.START) |
| { |
| /* Wait for the register modification to complete */ |
| } |
| |
| /* Set time */ |
| |
| /* Set seconds. (0-59) */ |
| |
| RTC.RSECAR.BYTE |= 0x80u; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RSECAR.BYTE; |
| } |
| |
| RTC.RSECAR.BYTE |= rtc_dec2bcd((uint8_t)alminfo->as_time.tm_sec); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RSECAR.BYTE; |
| } |
| |
| /* Set minutes (0-59) */ |
| |
| RTC.RMINAR.BYTE |= 0x80u; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMINAR.BYTE; |
| } |
| |
| RTC.RMINAR.BYTE |= rtc_dec2bcd((uint8_t) alminfo->as_time.tm_min); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMINAR.BYTE; |
| } |
| |
| /* Set hours. (0-23) */ |
| |
| RTC.RHRAR.BYTE |= 0x80u; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RHRAR.BYTE; |
| } |
| |
| RTC.RHRAR.BYTE |= rtc_dec2bcd((uint8_t) alminfo->as_time.tm_hour); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RHRAR.BYTE; |
| } |
| |
| /* Day of the month (1-31) */ |
| |
| RTC.RDAYAR.BYTE |= 0x80u; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RDAYAR.BYTE; |
| } |
| |
| RTC.RDAYAR.BYTE |= rtc_dec2bcd((uint8_t) alminfo->as_time.tm_mday); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RDAYAR.BYTE; |
| } |
| |
| /* Month. (1-12, 1=January) */ |
| |
| RTC.RMONAR.BYTE |= 0x80u; |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMONAR.BYTE; |
| } |
| |
| RTC.RMONAR.BYTE |= rtc_dec2bcd((uint8_t) ((alminfo->as_time.tm_mon)+ 1)); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_byte = RTC.RMONAR.BYTE; |
| } |
| |
| /* Year. (00-99) */ |
| |
| RTC.RYRAR.WORD = (uint16_t) (rtc_dec2bcd((uint8_t) |
| ((alminfo->as_time.tm_year) + 1900))); |
| |
| /* WAIT_LOOP */ |
| |
| for (i = 0; i < RTC_DUMMY_READ; i++) |
| { |
| dummy_word = RTC.RYRAR.WORD; |
| } |
| |
| rtc_dumpregs("New alarm setting"); |
| |
| /* Enable RTC ALARM interrupt */ |
| |
| RTC.RCR1.BIT.AIE = 1U; |
| |
| /* Clear IR flag of ICU ALARM interrupt */ |
| |
| IR(RTC, ALM) = 0U; |
| |
| /* Enable alarm interrupts */ |
| |
| IEN(RTC, ALM) = 1U; |
| |
| /* Set Priority of ALM interrupt */ |
| |
| IPR(RTC, ALM) = _0F_RTC_PRIORITY_LEVEL15; |
| leave_critical_section(flags); |
| UNUSED(dummy_byte); |
| UNUSED(dummy_word); |
| return OK; |
| } |
| #endif |
| |
| #ifdef CONFIG_RTC_PERIODIC |
| int rx65n_rtc_setperiodic(const struct timespec *period, |
| periodiccb_t callback) |
| { |
| irqstate_t flags; |
| volatile uint8_t regval; |
| uint8_t prd; |
| flags = enter_critical_section(); |
| |
| /* No.. Save the callback function pointer */ |
| |
| g_periodiccb.prd_cb = callback; |
| prd = period->tv_sec; |
| |
| /* Disable ICU PRD interrupt */ |
| |
| IEN(RTC, PRD) = 0U; |
| |
| /* Clear IR flag of PRD interrupt */ |
| |
| IR(RTC, PRD) = 0U; |
| |
| /* Set RTC control register 1 */ |
| |
| regval = getreg8(RX65N_RTC_RCR1); |
| regval |= 0x04 | (prd << 4); |
| rx65n_putreg(regval, RX65N_RTC_RCR1); |
| |
| irq_attach(RX65N_PRD_IRQ, rtc_periodic_interrupt, NULL); |
| |
| /* Start RTC counter */ |
| |
| RTC.RCR2.BIT.START = 1U; |
| |
| /* Enable ICU PRD interrupt */ |
| |
| IEN(RTC, PRD) = 1U; |
| |
| /* Set PRD priority level */ |
| |
| IPR(RTC, PRD) = _0F_RTC_PRIORITY_LEVEL15; |
| leave_critical_section(flags); |
| return OK; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_CARRY |
| void rx65n_rtc_set_carry(carrycb_t callback) |
| { |
| irqstate_t flags; |
| flags = enter_critical_section(); |
| |
| /* No.. Save the callback function pointer */ |
| |
| g_carrycb = callback; |
| |
| /* Clear IR flag of CUP interrupt */ |
| |
| IR(PERIB, INTB176) = 0U; |
| irq_attach(RX65N_INTB176_IRQ, rtc_carry_interrupt, NULL); |
| |
| RTC.RCR2.BIT.START = 1U; |
| |
| /* Enable ICU CUP interrupt */ |
| |
| IEN(PERIB, INTB176) = 1U; |
| |
| /* Set CUP priority level */ |
| |
| ICU.SLIBR176.BYTE = 0x31u; |
| IPR(PERIB, INTB176) = 15; |
| RTC.RCR1.BIT.CIE = 1U; |
| |
| leave_critical_section(flags); |
| } |
| |
| #endif |
| #ifdef CONFIG_RTC_ALARM |
| int rx65n_rtc_cancelalarm(void) |
| { |
| irqstate_t flags; |
| int ret = -ENODATA; |
| |
| flags = enter_critical_section(); |
| |
| /* Cancel the global callback function */ |
| |
| g_alarmcb.ac_cb = NULL; |
| g_alarmcb.ac_arg = NULL; |
| |
| /* Unset the alarm */ |
| |
| rx65n_putreg(0x0, RX65N_RTC_RSECAR); |
| rx65n_putreg(0x0, RX65N_RTC_RMINAR); |
| rx65n_putreg(0x0, RX65N_RTC_RHRAR); |
| rx65n_putreg(0x0, RX65N_RTC_RWKAR); |
| rx65n_putreg(0x0, RX65N_RTC_RDAYAR); |
| rx65n_putreg(0x0, RX65N_RTC_RMONAR); |
| rx65n_putreg(0x0, RX65N_RTC_RYRAR); |
| ret = OK; |
| |
| leave_critical_section(flags); |
| |
| return ret; |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_RTC_PERIODIC |
| int rx65n_rtc_cancelperiodic(void) |
| { |
| /* Disable ICU PRD interrupt */ |
| |
| IEN(RTC, PRD) = 0U; |
| |
| /* Clear IR flag of PRD interrupt */ |
| |
| IR(RTC, PRD) = 0U; |
| |
| /* Disable RTC PRD interrupt */ |
| |
| RTC.RCR1.BIT.PIE = 0U; |
| while (0U != RTC.RCR1.BIT.PIE) |
| { |
| /* Wait for this write to complete. */ |
| } |
| |
| return OK; |
| } |
| |
| #endif |
| |
| #if defined(CONFIG_RX65N_CARRY) |
| |
| int rx65n_rtc_cancelcarry(void) |
| { |
| /* Clear IR flag of CUP interrupt */ |
| |
| IR(PERIB, INTB176) = 0U; |
| |
| /* Disable ICU CUP interrupt */ |
| |
| IEN(PERIB, INTB176) = 0U; |
| |
| /* Disable RTC CUP interrupt */ |
| |
| RTC.RCR1.BIT.CIE = 0U; |
| return OK; |
| } |
| |
| #endif |
| /**************************************************************************** |
| * Name: up_rtc_getdatetime |
| * |
| * 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). |
| * |
| * 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: |
| * tp - The location to return the high resolution time value. |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno on failure |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RTC_DATETIME |
| int up_rtc_getdatetime(struct tm *tp) |
| { |
| uint8_t weekcnt; |
| uint8_t daycnt; |
| uint8_t monthcnt; |
| uint8_t yearcnt; |
| uint8_t seccnt; |
| uint8_t mincnt; |
| uint8_t hrcnt; |
| uint8_t tmp_week; |
| uint8_t tmp_day; |
| uint8_t tmp_month; |
| uint8_t tmp_year; |
| uint16_t bcd_years; |
| uint8_t regval; |
| |
| /* Sample the data time registers. There is a race condition here... |
| * If we sample the time just before midnight on December 31, |
| * the date could be wrong because the day rolled over while were sampling. |
| */ |
| |
| if (RTC.RCR2.BIT.START == 0) |
| { |
| RTC.RCR2.BIT.START = 1; |
| } |
| |
| do |
| { |
| weekcnt = getreg8(RX65N_RTC_RWKCNT); |
| daycnt = getreg8(RX65N_RTC_RDAYCNT); |
| monthcnt = getreg8(RX65N_RTC_RMONCNT); |
| yearcnt = getreg8(RX65N_RTC_RYRCNT); |
| seccnt = getreg8(RX65N_RTC_RSECCNT); |
| mincnt = getreg8(RX65N_RTC_RMINCNT); |
| hrcnt = getreg8(RX65N_RTC_RHRCNT); |
| tmp_week = getreg8(RX65N_RTC_RWKCNT); |
| tmp_day = getreg8(RX65N_RTC_RDAYCNT); |
| tmp_month = getreg8(RX65N_RTC_RMONCNT); |
| tmp_year = getreg8(RX65N_RTC_RYRCNT); |
| } |
| |
| while (tmp_week != weekcnt && tmp_day != daycnt && |
| tmp_month != monthcnt && tmp_year != yearcnt); |
| |
| rtc_dumpregs("Reading Time"); |
| |
| /* Convert the RTC time register fields to struct tm format. |
| * |
| * struct tm TIMR register |
| * tm_sec 0-61* SEC (0-59) |
| * tm_min 0-59 MIN (0-59) |
| * tm_hour 0-23 HOUR (0-23) |
| * |
| * *To allow for leap seconds. But these never actuall happen. |
| */ |
| |
| /* Disable ICU CUP interrupt */ |
| |
| up_disable_irq(CONFIG_RX65N_PERIB); |
| |
| /* Enable RTC CUP interrupt */ |
| |
| regval = getreg8(RX65N_RTC_RCR1); |
| regval |= (RTC_RCR1_CUP); |
| rx65n_putreg(regval, RX65N_RTC_RCR1); |
| |
| do |
| { |
| /* Clear carry flag in ICU */ |
| |
| IR(PERIB, INTB176) = 0U; |
| |
| /* Read and convert RTC registers; |
| * mask off unknown bits and hour am/pm. |
| */ |
| |
| /* Seconds. (0-59) */ |
| |
| tp->tm_sec = rtc_bcd2dec((uint8_t) (RTC.RSECCNT.BYTE & 0x7fu)); |
| |
| /* Minutes. (0-59) */ |
| |
| tp->tm_min = rtc_bcd2dec((uint8_t) (RTC.RMINCNT.BYTE & 0x7fu)); |
| |
| /* Hours. (0-23) */ |
| |
| tp->tm_hour = rtc_bcd2dec((uint8_t) (RTC.RHRCNT.BYTE & 0x3fu)); |
| |
| /* Day of the month (1-31) */ |
| |
| tp->tm_mday = rtc_bcd2dec(RTC.RDAYCNT.BYTE); |
| |
| /* Months since January (0-11) */ |
| |
| tp->tm_mon = rtc_bcd2dec(RTC.RMONCNT.BYTE) - 1; |
| |
| /* Years since 2000 */ |
| |
| bcd_years = (uint16_t) RTC.RYRCNT.WORD; |
| |
| /* years years since 1900 (100-199) */ |
| |
| tp->tm_year = rtc_bcd2dec((uint8_t) (bcd_years & 0xff)) + 100; |
| |
| /* Days since Sunday (0-6) */ |
| |
| tp->tm_wday = (int) (RTC.RWKCNT.BYTE & 0x07u); |
| rtc_dumptime(tp, "Returning"); |
| } |
| |
| while (1 == IR(PERIB, INTB176)); |
| UNUSED(hrcnt); |
| UNUSED(mincnt); |
| UNUSED(seccnt); |
| return OK; |
| } |
| |
| #endif |
| #endif /* CONFIG_RX65N_RTC */ |