| /****************************************************************************** |
| * |
| * Copyright (C) 2015 - 2017 Xilinx, Inc. All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * Use of the Software is limited solely to applications: |
| * (a) running on a Xilinx device, or |
| * (b) that interact with a Xilinx device through a bus or interconnect. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF |
| * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * Except as contained in this notice, the name of the Xilinx shall not be used |
| * in advertising or otherwise to promote the sale, use or other dealings in |
| * this Software without prior written authorization from Xilinx. |
| * |
| ******************************************************************************/ |
| /*****************************************************************************/ |
| /** |
| * |
| * @file xrtcpsu.c |
| * @addtogroup rtcpsu_v1_5 |
| * @{ |
| * |
| * Functions in this file are the minimum required functions for the XRtcPsu |
| * driver. See xrtcpsu.h for a detailed description of the driver. |
| * |
| * @note None. |
| * |
| * |
| * <pre> |
| * MODIFICATION HISTORY: |
| * |
| * Ver Who Date Changes |
| * ----- ----- -------- ----------------------------------------------- |
| * 1.00 kvn 04/21/15 First release |
| * 1.1 kvn 09/25/15 Modify control register to enable battery |
| * switching when vcc_psaux is not available. |
| * 1.2 02/15/16 Corrected Calibration mask and Fractional |
| * mask in CalculateCalibration API. |
| * 1.3 vak 04/25/16 Corrected the RTC read and write time logic(cr#948833). |
| * 1.5 ms 08/27/17 Fixed compilation warnings. |
| * ms 08/29/17 Updated code as per source code style. |
| * </pre> |
| * |
| ******************************************************************************/ |
| |
| /***************************** Include Files *********************************/ |
| |
| #include "xrtcpsu.h" |
| #include "xrtcpsu_hw.h" |
| |
| /************************** Constant Definitions *****************************/ |
| |
| /**************************** Type Definitions *******************************/ |
| |
| /***************** Macros (Inline Functions) Definitions *********************/ |
| |
| /************************** Variable Definitions *****************************/ |
| |
| static const u32 DaysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; |
| |
| /************************** Function Prototypes ******************************/ |
| |
| static void XRtcPsu_StubHandler(void *CallBackRef, u32 Event); |
| |
| /*****************************************************************************/ |
| /* |
| * |
| * This function initializes a XRtcPsu instance/driver. |
| * |
| * The initialization entails: |
| * - Initialize all members of the XRtcPsu structure. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * @param ConfigPtr points to the XRtcPsu device configuration structure. |
| * @param EffectiveAddr is the device base address in the virtual memory |
| * address space. If the address translation is not used then the |
| * physical address is passed. |
| * Unexpected errors may occur if the address mapping is changed |
| * after this function is invoked. |
| * |
| * @return XST_SUCCESS always. |
| * |
| * @note None. |
| * |
| ******************************************************************************/ |
| s32 XRtcPsu_CfgInitialize(XRtcPsu *InstancePtr, XRtcPsu_Config *ConfigPtr, |
| u32 EffectiveAddr) |
| { |
| s32 Status; |
| u32 ControlRegister; |
| Xil_AssertNonvoid(InstancePtr != NULL); |
| Xil_AssertNonvoid(ConfigPtr != NULL); |
| |
| /* |
| * Set some default values for instance data, don't indicate the device |
| * is ready to use until everything has been initialized successfully. |
| */ |
| InstancePtr->IsReady = 0U; |
| InstancePtr->RtcConfig.BaseAddr = EffectiveAddr; |
| InstancePtr->RtcConfig.DeviceId = ConfigPtr->DeviceId; |
| |
| if(InstancePtr->OscillatorFreq == 0U) { |
| InstancePtr->CalibrationValue = XRTC_CALIBRATION_VALUE; |
| InstancePtr->OscillatorFreq = XRTC_TYPICAL_OSC_FREQ; |
| } |
| |
| /* Set all handlers to stub values, let user configure this data later. */ |
| InstancePtr->Handler = XRtcPsu_StubHandler; |
| |
| InstancePtr->IsPeriodicAlarm = 0U; |
| |
| /* Set the calibration value in calibration register. */ |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_CALIB_WR_OFFSET, |
| InstancePtr->CalibrationValue); |
| |
| /* Set the Oscillator crystal and Battery switch enable in control register. */ |
| ControlRegister = XRtcPsu_ReadReg(InstancePtr->RtcConfig.BaseAddr + XRTC_CTL_OFFSET); |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_CTL_OFFSET, |
| (ControlRegister | (u32)XRTCPSU_CRYSTAL_OSC_EN | (u32)XRTC_CTL_BATTERY_EN_MASK)); |
| |
| /* Clear the Interrupt Status and Disable the interrupts. */ |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_INT_STS_OFFSET, |
| ((u32)XRTC_INT_STS_ALRM_MASK | (u32)XRTC_INT_STS_SECS_MASK)); |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_INT_DIS_OFFSET, |
| ((u32)XRTC_INT_DIS_ALRM_MASK | (u32)XRTC_INT_DIS_SECS_MASK)); |
| |
| /* Indicate the component is now ready to use. */ |
| InstancePtr->IsReady = XIL_COMPONENT_IS_READY; |
| |
| /* Clear TimeUpdated and CurrTimeUpdated */ |
| InstancePtr->TimeUpdated = 0; |
| InstancePtr->CurrTimeUpdated = 0; |
| |
| Status = XST_SUCCESS; |
| return Status; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function is a stub handler that is the default handler such that if the |
| * application has not set the handler when interrupts are enabled, this |
| * function will be called. |
| * |
| * @param CallBackRef is unused by this function. |
| * @param Event is unused by this function. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| *****************************************************************************/ |
| static void XRtcPsu_StubHandler(void *CallBackRef, u32 Event) |
| { |
| (void) CallBackRef; |
| (void) Event; |
| /* Assert occurs always since this is a stub and should never be called */ |
| Xil_AssertVoidAlways(); |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function sets the RTC time by writing into rtc write register. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * @param Time that should be updated into RTC write register. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| *****************************************************************************/ |
| void XRtcPsu_SetTime(XRtcPsu *InstancePtr,u32 Time) |
| { |
| /* Set the calibration value in calibration register, so that |
| * next Second is triggered exactly at 1 sec period |
| */ |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_CALIB_WR_OFFSET, |
| InstancePtr->CalibrationValue); |
| /* clear the RTC secs interrupt from status register */ |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr + XRTC_INT_STS_OFFSET, |
| XRTC_INT_STS_SECS_MASK); |
| InstancePtr->CurrTimeUpdated = 0; |
| /* Update the flag before setting the time */ |
| InstancePtr->TimeUpdated = 1; |
| /* Since RTC takes 1 sec to update the time into current time register, write |
| * load time + 1sec into the set time register. |
| */ |
| XRtcPsu_WriteSetTime(InstancePtr, Time + 1); |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function gets the current RTC time. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * |
| * @return RTC Current time. |
| * |
| * @note None. |
| * |
| *****************************************************************************/ |
| u32 XRtcPsu_GetCurrentTime(XRtcPsu *InstancePtr) |
| { |
| u32 Status; |
| u32 IntMask; |
| u32 CurrTime; |
| |
| IntMask = XRtcPsu_ReadReg(InstancePtr->RtcConfig.BaseAddr + XRTC_INT_MSK_OFFSET); |
| |
| if((IntMask & XRTC_INT_STS_SECS_MASK) != (u32)0) { |
| /* We come here if interrupts are disabled */ |
| Status = XRtcPsu_ReadReg(InstancePtr->RtcConfig.BaseAddr + XRTC_INT_STS_OFFSET); |
| if((InstancePtr->TimeUpdated == (u32)1) && |
| (Status & XRTC_INT_STS_SECS_MASK) == (u32)0) { |
| /* Give the previous written time */ |
| CurrTime = XRtcPsu_GetLastSetTime(InstancePtr) - 1; |
| } else { |
| /* Clear TimeUpdated */ |
| if((InstancePtr->TimeUpdated == (u32)1) && |
| ((Status & XRTC_INT_STS_SECS_MASK) == (u32)1)) { |
| InstancePtr->TimeUpdated = (u32)0; |
| } |
| |
| /* RTC time got updated */ |
| CurrTime = XRtcPsu_ReadCurrentTime(InstancePtr); |
| } |
| } else { |
| /* We come here if interrupts are enabled */ |
| if((InstancePtr->TimeUpdated == (u32)1) && |
| (InstancePtr->CurrTimeUpdated == (u32)0)) { |
| /* Give the previous written time -1 sec */ |
| CurrTime = XRtcPsu_GetLastSetTime(InstancePtr) - 1; |
| } else { |
| /* Clear TimeUpdated */ |
| if(InstancePtr->TimeUpdated == (u32)1) |
| InstancePtr->TimeUpdated = (u32)0; |
| /* RTC time got updated */ |
| CurrTime = XRtcPsu_ReadCurrentTime(InstancePtr); |
| } |
| } |
| return CurrTime; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function sets the alarm value of RTC device. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance |
| * @param Alarm is the desired alarm time for RTC. |
| * @param Periodic says whether the alarm need to set at periodic |
| * Intervals or a one-time alarm. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| *****************************************************************************/ |
| void XRtcPsu_SetAlarm(XRtcPsu *InstancePtr, u32 Alarm, u32 Periodic) |
| { |
| Xil_AssertVoid(InstancePtr != NULL); |
| Xil_AssertVoid(Alarm != 0U); |
| Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); |
| Xil_AssertVoid((Alarm - XRtcPsu_GetCurrentTime(InstancePtr)) > (u32)0); |
| |
| XRtcPsu_WriteReg(InstancePtr->RtcConfig.BaseAddr+XRTC_ALRM_OFFSET, Alarm); |
| if(Periodic != 0U) { |
| InstancePtr->IsPeriodicAlarm = 1U; |
| InstancePtr->PeriodicAlarmTime = |
| Alarm - XRtcPsu_GetCurrentTime(InstancePtr); |
| } |
| } |
| |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function translates time in seconds to a YEAR:MON:DAY HR:MIN:SEC |
| * format and saves it in the DT structure variable. It also reports the weekday. |
| * |
| * @param Seconds is the time value that has to be shown in DateTime |
| * format. |
| * @param dt is the DateTime format variable that stores the translated |
| * time. |
| * |
| * @return None. |
| * |
| * @note This API supports this century i.e., 2000 - 2099 years only. |
| * |
| *****************************************************************************/ |
| void XRtcPsu_SecToDateTime(u32 Seconds, XRtcPsu_DT *dt) |
| { |
| u32 CurrentTime; |
| u32 TempDays; |
| u32 DaysPerMonth; |
| u32 Leap = 0U; |
| |
| CurrentTime = Seconds; |
| dt->Sec = CurrentTime % 60U; |
| CurrentTime /= 60U; |
| dt->Min = CurrentTime % 60U; |
| CurrentTime /= 60U; |
| dt->Hour = CurrentTime % 24U; |
| TempDays = CurrentTime / 24U; |
| |
| if (TempDays == 0U) { |
| TempDays = 1U; |
| } |
| dt->WeekDay = TempDays % 7U; |
| |
| for (dt->Year = 0U; dt->Year <= 99U; ++(dt->Year)) { |
| if ((dt->Year % 4U) == 0U ) { |
| Leap = 1U; |
| } |
| else { |
| Leap = 0U; |
| } |
| if (TempDays < (365U + Leap)) { |
| break; |
| } |
| TempDays -= (365U + Leap); |
| } |
| |
| for (dt->Month = 1U; dt->Month >= 1U; ++(dt->Month)) { |
| DaysPerMonth = DaysInMonth[dt->Month - 1]; |
| if ((Leap == 1U) && (dt->Month == 2U)) { |
| DaysPerMonth++; |
| } |
| if (TempDays < DaysPerMonth) { |
| break; |
| } |
| TempDays -= DaysPerMonth; |
| } |
| |
| dt->Day = TempDays; |
| dt->Year += 2000U; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function translates time in YEAR:MON:DAY HR:MIN:SEC format to |
| * seconds. |
| * |
| * @param dt is a pointer to a DatetTime format structure variable |
| * of time that has to be shown in seconds. |
| * |
| * @return Seconds value of provided in dt time. |
| * |
| * @note None. |
| * |
| *****************************************************************************/ |
| u32 XRtcPsu_DateTimeToSec(XRtcPsu_DT *dt) |
| { |
| u32 i; |
| u32 Days; |
| u32 Seconds; |
| Xil_AssertNonvoid(dt != NULL); |
| |
| if (dt->Year >= 2000U) { |
| dt->Year -= 2000U; |
| } |
| |
| for (i = 1U; i < dt->Month; i++) { |
| dt->Day += (u32)DaysInMonth[i-1]; |
| } |
| |
| if ((dt->Month > 2U) && ((dt->Year % 4U) == 0U)) { |
| dt->Day++; |
| } |
| Days = dt->Day + (365U * dt->Year) + ((dt->Year + 3U) / 4U); |
| Seconds = (((((Days * 24U) + dt->Hour) * 60U) + dt->Min) * 60U) + dt->Sec; |
| return Seconds; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function calculates the calibration value depending on the actual |
| * realworld time and also helps in deriving new calibration value if |
| * the user wishes to change his oscillator frequency.TimeReal is generally the |
| * internet time with EPOCH time as reference i.e.,1/1/1970 1st second. |
| * But this RTC driver assumes start time from 1/1/2000 1st second. Hence,if |
| * the user maps the internet time InternetTimeInSecs, then he has to use |
| * XRtcPsu_SecToDateTime(InternetTimeInSecs,&InternetTime), |
| * TimeReal = XRtcPsu_DateTimeToSec(InternetTime) |
| * consecutively to arrive at TimeReal value. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * @param TimeReal is the actual realworld time generally an |
| * network time / Internet time in seconds. |
| * |
| * @param CrystalOscFreq is the Oscillator new frequency. Say, If the user |
| * is going with the typical 32768Hz, then he inputs the same |
| * frequency value. |
| * |
| * @return None. |
| * |
| * @note After Calculating the calibration register, user / application has to |
| * call again CfgInitialize API to bring the new calibration into effect. |
| * |
| *****************************************************************************/ |
| void XRtcPsu_CalculateCalibration(XRtcPsu *InstancePtr,u32 TimeReal, |
| u32 CrystalOscFreq) |
| { |
| u32 ReadTime; |
| u32 SetTime; |
| u32 Cprev; |
| u32 Fprev; |
| u32 Cnew; |
| u32 Fnew; |
| u32 Calibration; |
| float Xf; |
| Xil_AssertVoid(TimeReal != 0U); |
| Xil_AssertVoid(CrystalOscFreq != 0U); |
| |
| ReadTime = XRtcPsu_GetCurrentTime(InstancePtr); |
| SetTime = XRtcPsu_GetLastSetTime(InstancePtr); |
| Calibration = XRtcPsu_GetCalibration(InstancePtr); |
| /* |
| * When board gets reseted, Calibration value is zero |
| * and Last setTime will be marked as 1st second. This implies |
| * CurrentTime to be in few seconds say something in tens. TimeReal will |
| * be huge, say something in thousands. So to prevent such reset case, Cnew |
| * and Fnew will not be calculated. |
| */ |
| if((Calibration == 0U) || (CrystalOscFreq != InstancePtr->OscillatorFreq)) { |
| Cnew = CrystalOscFreq - (u32)1; |
| Fnew = 0U; |
| } else { |
| Cprev = Calibration & XRTC_CALIB_RD_MAX_TCK_MASK; |
| Fprev = Calibration & XRTC_CALIB_RD_FRACTN_DATA_MASK; |
| |
| Xf = ((ReadTime - SetTime) * ((Cprev+1U) + ((Fprev+1U)/16U))) / (TimeReal - SetTime); |
| Cnew = (u32)(Xf) - (u32)1; |
| Fnew = XRtcPsu_RoundOff((Xf - Cnew) * 16U) - (u32)1; |
| } |
| |
| Calibration = (Fnew << XRTC_CALIB_RD_FRACTN_DATA_SHIFT) + Cnew; |
| Calibration |= XRTC_CALIB_RD_FRACTN_EN_MASK; |
| |
| InstancePtr->CalibrationValue = Calibration; |
| InstancePtr->OscillatorFreq = CrystalOscFreq; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function returns the seconds event status by reading |
| * interrupt status register. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * |
| * @return Returns 1 if a new second event is generated.Else 0.. |
| * |
| * @note This API is used in polled mode operation of RTC. |
| * This also clears interrupt status seconds bit. |
| * |
| *****************************************************************************/ |
| u32 XRtcPsu_IsSecondsEventGenerated(XRtcPsu *InstancePtr) |
| { |
| u32 Status; |
| |
| /* Loop the interrupt status register for Seconds Event */ |
| if ((XRtcPsu_ReadReg(InstancePtr->RtcConfig.BaseAddr + |
| XRTC_INT_STS_OFFSET) & (XRTC_INT_STS_SECS_MASK)) == 0U) { |
| Status = 0U; |
| } else { |
| /* Clear the interrupt status register */ |
| XRtcPsu_WriteReg((InstancePtr)->RtcConfig.BaseAddr + |
| XRTC_INT_STS_OFFSET, XRTC_INT_STS_SECS_MASK); |
| Status = 1U; |
| } |
| return Status; |
| } |
| |
| /****************************************************************************/ |
| /** |
| * |
| * This function returns the alarm event status by reading |
| * interrupt status register. |
| * |
| * @param InstancePtr is a pointer to the XRtcPsu instance. |
| * |
| * @return Returns 1 if the alarm event is generated.Else 0. |
| * |
| * @note This API is used in polled mode operation of RTC. |
| * This also clears interrupt status alarm bit. |
| * |
| *****************************************************************************/ |
| u32 XRtcPsu_IsAlarmEventGenerated(XRtcPsu *InstancePtr) |
| { |
| u32 Status; |
| |
| /* Loop the interrupt status register for Alarm Event */ |
| if ((XRtcPsu_ReadReg(InstancePtr->RtcConfig.BaseAddr + |
| XRTC_INT_STS_OFFSET) & (XRTC_INT_STS_ALRM_MASK)) == 0U) { |
| Status = 0U; |
| } else { |
| /* Clear the interrupt status register */ |
| XRtcPsu_WriteReg((InstancePtr)->RtcConfig.BaseAddr + |
| XRTC_INT_STS_OFFSET, XRTC_INT_STS_ALRM_MASK); |
| Status = 1U; |
| } |
| return Status; |
| } |
| /** @} */ |