2016-11-09 19:33:11 +00:00
|
|
|
/*******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
* Copyright (C) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
|
2016-11-09 19:33:11 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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 MAXIM INTEGRATED 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 Maxim Integrated
|
|
|
|
* Products, Inc. shall not be used except as stated in the Maxim Integrated
|
|
|
|
* Products, Inc. Branding Policy.
|
|
|
|
*
|
|
|
|
* The mere transfer of this software does not imply any licenses
|
|
|
|
* of trade secrets, proprietary technology, copyrights, patents,
|
|
|
|
* trademarks, maskwork rights, or any other form of intellectual
|
|
|
|
* property whatsoever. Maxim Integrated Products, Inc. retains all
|
|
|
|
* ownership rights.
|
|
|
|
*******************************************************************************
|
|
|
|
*/
|
|
|
|
|
2018-05-17 19:35:28 +00:00
|
|
|
#include <string.h>
|
2016-11-09 19:33:11 +00:00
|
|
|
#include "rtc_api.h"
|
|
|
|
#include "lp_ticker_api.h"
|
|
|
|
#include "rtc.h"
|
|
|
|
#include "lp.h"
|
2018-04-23 20:53:20 +00:00
|
|
|
#include <string.h>
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
// LOG2 for 32-bit powers of 2
|
|
|
|
#define LOG2_1(n) (((n) >= (1 << 1)) ? 1 : 0)
|
|
|
|
#define LOG2_2(n) (((n) >= (1 << 2)) ? ( 2 + (LOG2_1((n) >> 2))) : LOG2_1(n))
|
|
|
|
#define LOG2_4(n) (((n) >= (1 << 4)) ? ( 4 + (LOG2_2((n) >> 4))) : LOG2_2(n))
|
|
|
|
#define LOG2_8(n) (((n) >= (1 << 8)) ? ( 8 + (LOG2_4((n) >> 8))) : LOG2_4(n))
|
|
|
|
#define LOG2(n) (((n) >= (1 << 16)) ? (16 + (LOG2_8((n) >> 16))) : LOG2_8(n))
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
#define LP_TIMER_FREQ_HZ 4096
|
|
|
|
#define LP_TIMER_PRESCALE RTC_PRESCALE_DIV_2_0
|
|
|
|
#define LP_TIMER_RATE_HZ (LP_TIMER_FREQ_HZ >> LP_TIMER_PRESCALE)
|
|
|
|
#define LP_TIMER_WIDTH 32
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
static volatile int rtc_inited = 0;
|
|
|
|
static volatile int lp_ticker_inited = 0;
|
2016-11-09 19:33:11 +00:00
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
static void init_rtc(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
|
|
|
/* Enable power for RTC for all LPx states */
|
|
|
|
MXC_PWRSEQ->reg0 |= (MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN |
|
|
|
|
MXC_F_PWRSEQ_REG0_PWR_RTCEN_SLP);
|
|
|
|
|
|
|
|
/* Enable clock to synchronizers */
|
|
|
|
CLKMAN_SetClkScale(CLKMAN_CLK_SYNC, CLKMAN_SCALE_DIV_1);
|
|
|
|
|
|
|
|
/* RTC registers are only reset on a power cycle. Do not reconfigure the RTC
|
|
|
|
* if it is already running.
|
|
|
|
*/
|
|
|
|
if (!RTC_IsActive()) {
|
2018-04-23 20:53:20 +00:00
|
|
|
rtc_cfg_t cfg;
|
|
|
|
memset(&cfg, 0, sizeof(rtc_cfg_t));
|
2018-04-09 21:57:28 +00:00
|
|
|
cfg.prescaler = LP_TIMER_PRESCALE;
|
2016-11-09 19:33:11 +00:00
|
|
|
cfg.snoozeMode = RTC_SNOOZE_DISABLE;
|
|
|
|
|
|
|
|
int retval = RTC_Init(&cfg);
|
|
|
|
MBED_ASSERT(retval == E_NO_ERROR);
|
|
|
|
|
|
|
|
RTC_EnableINT(MXC_F_RTC_FLAGS_OVERFLOW);
|
|
|
|
RTC_Start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
static void overflow_handler(void)
|
|
|
|
{
|
|
|
|
MXC_RTCTMR->comp[1] += ((UINT32_MAX >> LOG2(LP_TIMER_RATE_HZ)) + 1);
|
2018-05-17 19:35:28 +00:00
|
|
|
RTC_ClearFlags(MXC_F_RTC_FLAGS_OVERFLOW);
|
2018-04-09 21:57:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
|
|
void rtc_init(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
|
|
|
|
NVIC_EnableIRQ(RTC3_IRQn);
|
2018-05-17 19:35:28 +00:00
|
|
|
|
|
|
|
// Enable as LP wakeup source
|
|
|
|
MXC_PWRSEQ->msk_flags |= MXC_F_PWRSEQ_FLAGS_RTC_ROLLOVER;
|
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
init_rtc();
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
|
|
void rtc_free(void)
|
|
|
|
{
|
2018-05-17 19:35:28 +00:00
|
|
|
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
|
|
int rtc_isenabled(void)
|
|
|
|
{
|
2018-05-17 19:35:28 +00:00
|
|
|
return !!RTC_IsActive();
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
void rtc_write(time_t t)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
MXC_RTCTMR->comp[1] = t - (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ));
|
|
|
|
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
time_t rtc_read(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
return (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ)) + MXC_RTCTMR->comp[1];
|
|
|
|
}
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
//******************************************************************************
|
|
|
|
void lp_ticker_init(void)
|
|
|
|
{
|
2018-05-17 19:46:36 +00:00
|
|
|
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
|
2018-04-09 21:57:28 +00:00
|
|
|
NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
|
|
|
|
NVIC_EnableIRQ(RTC0_IRQn);
|
|
|
|
init_rtc();
|
2018-05-17 19:46:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
|
|
void lp_ticker_free(void)
|
|
|
|
{
|
|
|
|
// Disable interrupt associated with LPTICKER API
|
|
|
|
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
|
|
|
|
|
|
|
|
// RTC hardware is shared by LPTICKER and RTC APIs.
|
|
|
|
// Prior initialization of the RTC API gates disabling the RTC hardware.
|
|
|
|
if (!(MXC_RTCTMR->inten & MXC_F_RTC_INTEN_OVERFLOW)) {
|
|
|
|
RTC_Stop();
|
|
|
|
}
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
uint32_t lp_ticker_read(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
return MXC_RTCTMR->timer;
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
|
|
void lp_ticker_set_interrupt(timestamp_t timestamp)
|
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
MXC_RTCTMR->comp[0] = timestamp;
|
2018-05-17 19:46:36 +00:00
|
|
|
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0;
|
|
|
|
RTC_EnableINT(MXC_F_RTC_INTEN_COMP0);
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-05-17 19:46:36 +00:00
|
|
|
// Enable as LP wakeup source
|
|
|
|
MXC_PWRSEQ->msk_flags |= MXC_F_PWRSEQ_FLAGS_RTC_CMPR0;
|
2016-11-09 19:33:11 +00:00
|
|
|
|
2018-05-17 19:46:36 +00:00
|
|
|
// Postponed write pending wait for comp0 and flags
|
2016-11-09 19:33:11 +00:00
|
|
|
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
|
|
|
|
}
|
|
|
|
|
2018-04-09 21:57:28 +00:00
|
|
|
//******************************************************************************
|
|
|
|
void lp_ticker_disable_interrupt(void)
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
}
|
|
|
|
|
2016-11-09 19:33:11 +00:00
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
void lp_ticker_clear_interrupt(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-05-17 19:46:36 +00:00
|
|
|
RTC_ClearFlags(MXC_F_RTC_FLAGS_COMP0);
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
void lp_ticker_fire_interrupt(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
NVIC_SetPendingIRQ(RTC0_IRQn);
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//******************************************************************************
|
2018-04-09 21:57:28 +00:00
|
|
|
const ticker_info_t *lp_ticker_get_info(void)
|
2016-11-09 19:33:11 +00:00
|
|
|
{
|
2018-04-09 21:57:28 +00:00
|
|
|
static const ticker_info_t info = {
|
|
|
|
LP_TIMER_RATE_HZ,
|
|
|
|
LP_TIMER_WIDTH
|
|
|
|
};
|
|
|
|
|
|
|
|
return &info;
|
2016-11-09 19:33:11 +00:00
|
|
|
}
|