/*
 * stm32f10x_rtc_ex.c
 *
 * Copyright (C) 2016 Choutouridis Christos <houtouridis.ch@gmail.com>
 * All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Choutouridis Christos. The intellectual
 * and technical concepts contained herein are proprietary to
 * Choutouridis Christos and are protected by copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Choutouridis Christos.
 *
 */
#include <stm32f10x_rtc_ex.h>

/*!
 * Check the run Status of the RTC
 */
FunctionalState RTC_GetStatus (void) {
   uint32_t mask = RCC_RTCCLKSource_LSE | RCC_RTCCLKSource_LSI | RCC_RTCCLKSource_HSE_Div128;

   uint32_t rtc_clk_en = RCC->BDCR & RCC_BDCR_RTCEN;

   switch (RCC->BDCR & mask) {
      case RCC_RTCCLKSource_LSE:
         return (FunctionalState)(RCC_GetFlagStatus (RCC_FLAG_LSERDY) && rtc_clk_en);
      case RCC_RTCCLKSource_LSI:
         return (FunctionalState)(RCC_GetFlagStatus (RCC_FLAG_LSIRDY) && rtc_clk_en);
      case RCC_RTCCLKSource_HSE_Div128:
         return (FunctionalState)(RCC_GetFlagStatus (RCC_FLAG_HSERDY) && rtc_clk_en);
      default:
         return DISABLE;
   }
}

/*!
 * De-Initialize the RTC clock as time provider
 */
void RTC_Sys_DeInit (void)
{
   RCC_RTCCLKCmd (DISABLE);
   RCC_LSEConfig(RCC_LSE_OFF);
}


/*!
 * \brief
 *    Initialize RTC as a time provider.
 * \param   xtal  The XTAL clock used for RTC
 * \return  The status of the operation
 *    \arg  0     Success  RTC is running with LSE
 *    \arg  1     Fail     RTC is running with LSI
 *    \arg  2     Fail     RTC is not running
 */
int RTC_Sys_Init (clock_t xtal)
{
   uint32_t div, clk_src, timeout;
   int ret;

   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

   // Check if we have a running system depending on RTCSEL
   if (RTC_GetStatus () != ENABLE) {
      // Reset bkp domain and reconfigure rtc
      PWR_BackupAccessCmd(ENABLE);
      BKP_DeInit();

      // Try to enable LSE
      timeout = RTC_SYS_STARTUP_TIMEOUT;
      RCC_LSEConfig(RCC_LSE_ON);
      while ( (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) && --timeout)
         ;
      if (timeout) {
         div = xtal; /* RTC period = RTCCLK/RTC_PR = (xtal KHz)/((xtal-1)+1) */
         clk_src = RCC_RTCCLKSource_LSE;
         ret = 0;
      }
      else {
         // Try to enable LSI
         RCC_LSEConfig(RCC_LSE_OFF);
         timeout = RTC_SYS_STARTUP_TIMEOUT;
         RCC_LSICmd (ENABLE);
         while ( (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) && --timeout)
            ;
         if (timeout) {
            div = RTC_SYS_LSI_CLOCK;  /* RTC period = RTCCLK/RTC_PR */
            clk_src = RCC_RTCCLKSource_LSI;
            ret = 1;
         }
         else     // Give up
            return 2;
      }

      RCC_RTCCLKConfig (clk_src);
      RCC_RTCCLKCmd (ENABLE);
      RTC_WaitForSynchro ();
      RTC_WaitForLastTask ();

      RTC_SetPrescaler(div);
      RTC_WaitForLastTask();

      PWR_BackupAccessCmd(DISABLE);
   }
   return ret=0;
}

/*!
 * \brief
 *    determines the current calendar time. The encoding of the value is
 *    unspecified.
 * \param   timer    Pointer to time_t variable to write the current time
 * \return
 *    The implementations best approximation to the current calendar
 *    time. If timer is not a null pointer, the return value
 *    is also assigned to the object it points to.
 */
time_t RTC_Sys_time (time_t *timer)
{
   time_t now =
      (time_t)(((uint32_t)RTC->CNTH << 16 ) | (uint32_t)RTC->CNTL);
   if (timer)  *timer = now;
   return now;
}

/*!
 * \brief
 *    Sets the system's idea of the time and date. The time,
 *    pointed to by timer, is measured in seconds since the
 *    Epoch, 1970-01-01 00:00:00 +0000 (UTC).
 *
 * \param   timer    Pointer to time_t variable with the time to set
 * \return           On success, zero is returned. On error, -1 is returned
 */
int RTC_Sys_settime (const time_t *timer)
{
   PWR_BackupAccessCmd (ENABLE);
   RTC_WaitForLastTask ();
   RTC_SetCounter ((uint32_t)*timer);
   RTC_WaitForLastTask ();
   PWR_BackupAccessCmd (DISABLE);
   return 0;
}
