/**
 * @file xmc_rtc.c
 * @date 2019-05-07
 *
 * @cond
 *****************************************************************************
 * XMClib v2.2.0 - XMC Peripheral Driver Library
 *
 * Copyright (c) 2015-2020, Infineon Technologies AG
 * All rights reserved.
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * To improve the quality of the software, users are encouraged to share
 * modifications, enhancements or bug fixes with Infineon Technologies AG
 * at XMCSupport@infineon.com.
 *****************************************************************************
 *
 * Change History
 * --------------
 *
 * 2015-02-20:
 *     - Initial <br>
 *
 * 2015-06-20:
 *     - Removed GetDriverVersion API
 *
 * 2016-05-19:
 *     - Added XMC_RTC_SetTimeStdFormat() and XMC_RTC_SetAlarmStdFormat()
 *
 * 2018-06-21:
 *     - Fixed value of XMC_RTC_MAXSECONDS, XMC_RTC_MAXMINUTES, XMC_RTC_MAXHOURS macros
 *     - Fixed assertion on XMC_RTC_MAXDAYS
 *
 * 2019-05-07:
 *     - Fixed compilation warnings
 *
 * @endcond
 *
 */

/**
 *
 * @brief RTC driver for XMC microcontroller family.
 *
 */

/*********************************************************************************************************************
 * HEADER FILES
 *********************************************************************************************************************/

#include "xmc_scu.h"
#include "xmc_rtc.h"

/*********************************************************************************************************************
 * MACROS
 *********************************************************************************************************************/

#define XMC_RTC_MAXSECONDS  (60U)   /**< RTC time : Maximum seconds */
#define XMC_RTC_MAXMINUTES  (60U)   /**< RTC time : Maximum minutes */
#define XMC_RTC_MAXHOURS    (24U)   /**< RTC time : Maximum hours */
#define XMC_RTC_MAXDAYS     (31U)   /**< RTC time : Maximum days */
#define XMC_RTC_MAXDAYSOFWEEK  (7U) /**< RTC time : Maximum days of week */
#define XMC_RTC_MAXMONTH  (12U)     /**< RTC time : Maximum month */
#define XMC_RTC_MAXYEAR  (0xFFFFU)  /**< RTC time : Maximum year */
#define XMC_RTC_MAXPRESCALER  (0xFFFFU)  /**< RTC time : Maximum prescaler */
#define XMC_RTC_YEAR_OFFSET (1900U)      /**< RTC year offset : Year offset */

#if (UC_FAMILY == XMC4)
#define XMC_RTC_INIT_SEQUENCE  (1U)
#endif
#if (UC_FAMILY == XMC1)
#define XMC_RTC_INIT_SEQUENCE  (0U)
#endif

/*********************************************************************************************************************
 * API IMPLEMENTATION
 *********************************************************************************************************************/

/*
 * Enables RTC peripheral to start counting time
 */
void XMC_RTC_Start(void)
{
  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_CTR_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->CTR |= (uint32_t)RTC_CTR_ENB_Msk;
}

/*
 * Disables RTC peripheral to start counting time
 */
void XMC_RTC_Stop(void)
{
  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_CTR_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->CTR &= ~(uint32_t)RTC_CTR_ENB_Msk;
}

/*
 * Sets the RTC module prescaler value
 */
void XMC_RTC_SetPrescaler(uint16_t prescaler)
{
  XMC_ASSERT("XMC_RTC_SetPrescaler:Wrong prescaler value", (prescaler < XMC_RTC_MAXPRESCALER));

  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_CTR_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->CTR = (RTC->CTR & ~(uint32_t)RTC_CTR_DIV_Msk) |
             ((uint32_t)prescaler << (uint32_t)RTC_CTR_DIV_Pos);
}

/*
 * Sets the RTC_TIM0, RTC_TIM1 registers with time values
 */
void XMC_RTC_SetTime(const XMC_RTC_TIME_t *const time)
{
  XMC_ASSERT("XMC_RTC_SetTime:Wrong seconds value", ((uint32_t)time->seconds < XMC_RTC_MAXSECONDS));
  XMC_ASSERT("XMC_RTC_SetTime:Wrong minutes value", ((uint32_t)time->minutes < XMC_RTC_MAXMINUTES));
  XMC_ASSERT("XMC_RTC_SetTime:Wrong hours value", ((uint32_t)time->hours < XMC_RTC_MAXHOURS));
  XMC_ASSERT("XMC_RTC_SetTime:Wrong week day value", ((uint32_t)time->daysofweek < XMC_RTC_MAXDAYSOFWEEK));
  XMC_ASSERT("XMC_RTC_SetTime:Wrong month value", ((uint32_t)time->month < XMC_RTC_MAXMONTH));
  XMC_ASSERT("XMC_RTC_SetTime:Wrong year value", ((uint32_t)time->year < XMC_RTC_MAXYEAR));

#if (XMC_RTC_INIT_SEQUENCE == 1U)
  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_TIM0_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->TIM0 = time->raw0;

  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_TIM1_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->TIM1 = time->raw1;
#endif
#if (XMC_RTC_INIT_SEQUENCE == 0U)
  while ((XMC_SCU_GetMirrorStatus() & (SCU_GENERAL_MIRRSTS_RTC_TIM0_Msk | SCU_GENERAL_MIRRSTS_RTC_TIM1_Msk)) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->TIM0 = time->raw0;
  RTC->TIM1 = time->raw1;	;
#endif
}

/*
 * Gets the RTC module time value
 */
void XMC_RTC_GetTime(XMC_RTC_TIME_t *const time)
{
  time->raw0 = RTC->TIM0;
  time->raw1 = RTC->TIM1;
}

/*
 * Sets the RTC module time values in standard format
 */
void XMC_RTC_SetTimeStdFormat(const struct tm *const stdtime)
{

  XMC_RTC_TIME_t time;

  time.seconds = stdtime->tm_sec;
  time.minutes = stdtime->tm_min;
  time.hours = stdtime->tm_hour;
  time.days = stdtime->tm_mday - 1;
  time.month = stdtime->tm_mon;
  time.year = stdtime->tm_year + XMC_RTC_YEAR_OFFSET;
  time.daysofweek = stdtime->tm_wday;

  XMC_RTC_SetTime(&time);
}

/*
 * Gets the RTC module time values in standard format
 */
void XMC_RTC_GetTimeStdFormat(struct tm *const stdtime)
{
  XMC_RTC_TIME_t time;
  time.raw0 = RTC->TIM0;
  time.raw1 = RTC->TIM1;

  stdtime->tm_sec = (int8_t)time.seconds;
  stdtime->tm_min = (int8_t)time.minutes;
  stdtime->tm_hour = (int8_t)time.hours;
  stdtime->tm_mday = ((int8_t)time.days + (int8_t)1);
  stdtime->tm_mon = (int8_t)time.month;
  stdtime->tm_year = (int32_t)time.year - (int32_t)XMC_RTC_YEAR_OFFSET;
  stdtime->tm_wday = (int8_t)time.daysofweek;
}

/*
 * Sets the RTC module alarm time value
 */
void XMC_RTC_SetAlarm(const XMC_RTC_ALARM_t *const alarm)
{
  XMC_ASSERT("XMC_RTC_SetAlarm:Wrong seconds value", ((uint32_t)alarm->seconds < XMC_RTC_MAXSECONDS));
  XMC_ASSERT("XMC_RTC_SetAlarm:Wrong minutes value", ((uint32_t)alarm->minutes < XMC_RTC_MAXMINUTES));
  XMC_ASSERT("XMC_RTC_SetAlarm:Wrong hours value", ((uint32_t)alarm->hours < XMC_RTC_MAXHOURS));
  XMC_ASSERT("XMC_RTC_SetAlarm:Wrong month value", ((uint32_t)alarm->month < XMC_RTC_MAXMONTH));
  XMC_ASSERT("XMC_RTC_SetAlarm:Wrong year value", ((uint32_t)alarm->year < XMC_RTC_MAXYEAR));

#if (XMC_RTC_INIT_SEQUENCE == 1U)
  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_ATIM0_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->ATIM0 = alarm->raw0;

  while ((XMC_SCU_GetMirrorStatus() & SCU_GENERAL_MIRRSTS_RTC_ATIM1_Msk) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->ATIM1 = alarm->raw1;
#endif
#if (XMC_RTC_INIT_SEQUENCE == 0U)
  while ((XMC_SCU_GetMirrorStatus() & (SCU_GENERAL_MIRRSTS_RTC_ATIM0_Msk | SCU_GENERAL_MIRRSTS_RTC_ATIM1_Msk)) != 0U)
  {
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
  }
  RTC->ATIM0 = alarm->raw0;
  RTC->ATIM1 = alarm->raw1;
#endif
}

/*
 * Gets the RTC module alarm time value
 */
void XMC_RTC_GetAlarm(XMC_RTC_ALARM_t *const alarm)
{
  alarm->raw0 = RTC->ATIM0;
  alarm->raw1 = RTC->ATIM1;
}


/*
 * Sets the RTC module alarm time value in standard format
 */
void XMC_RTC_SetAlarmStdFormat(const struct tm *const stdtime)
{
  XMC_RTC_ALARM_t alarm;


  alarm.seconds = stdtime->tm_sec;
  alarm.minutes = stdtime->tm_min;
  alarm.hours = stdtime->tm_hour;
  alarm.days = stdtime->tm_mday - 1;
  alarm.month = stdtime->tm_mon;
  alarm.year = stdtime->tm_year + XMC_RTC_YEAR_OFFSET;

  XMC_RTC_SetAlarm(&alarm);
}

/*
 * Gets the RTC module alarm time value in standard format
 */
void XMC_RTC_GetAlarmStdFormat(struct tm *const stdtime)
{
  XMC_RTC_ALARM_t alarm;

  alarm.raw0 = RTC->ATIM0;
  alarm.raw1 = RTC->ATIM1;

  stdtime->tm_sec = (int8_t)alarm.seconds;
  stdtime->tm_min = (int8_t)alarm.minutes;
  stdtime->tm_hour = (int8_t)alarm.hours;
  stdtime->tm_mday = ((int8_t)alarm.days + (int8_t)1);
  stdtime->tm_mon = (int8_t)alarm.month;
  stdtime->tm_year = (int32_t)alarm.year - (int32_t)XMC_RTC_YEAR_OFFSET;
}

/*
 * Gets the RTC periodic and alarm event(s) status
 */
uint32_t XMC_RTC_GetEventStatus(void)
{
  return RTC->STSSR;
}
