Add RTC and Sleep to CM3DS

This commit represents the second stage of the low power implementations
that are required from Mbed 5.10 onwards. Besides the default hal
implementations (rtc_api.c and sleep.c), the PL031 RTC's native driver
needed to be added. Due to HW limitations in SSE-050 and the CM3DS,
Deep Sleep couldn't be implemented, therefore it is functionally
identical to Sleep (WFI).

Change-Id: Ibed2bdb452f48c98024dc7ef07fb51a4425e0a80
Signed-off-by: Bence Kaposzta <bence.kaposzta@arm.com>
pull/8737/head
Bence Kaposzta 2018-09-05 15:37:09 +02:00
parent 1b792317e4
commit ee7cefc868
8 changed files with 494 additions and 10 deletions

View File

@ -49,7 +49,8 @@
*/
#define USEC_TIMER_BIT_WIDTH 32U
#define USEC_REPORTED_SHIFT 5U
#define USEC_REPORTED_FREQ_HZ (TIMERS_INPUT_CLOCK_FREQ_HZ >> USEC_REPORTED_SHIFT)
#define USEC_REPORTED_FREQ_HZ (TIMERS_INPUT_CLOCK_FREQ_HZ >> \
USEC_REPORTED_SHIFT)
#define USEC_REPORTED_BITS (USEC_TIMER_BIT_WIDTH - USEC_REPORTED_SHIFT)
/* mbed low power ticker configuration */
@ -72,6 +73,9 @@
(LP_TIMER_HW_PRESCALER+LP_REPORTED_SHIFT))
#define LP_REPORTED_BITS (LP_TIMER_BIT_WIDTH - LP_REPORTED_SHIFT)
/* RTC PL031 */
#define RTC_PL031
/* ARM GPIO */
#define ARM_GPIO0
#define ARM_GPIO1

View File

@ -0,0 +1,226 @@
/*
* Copyright (c) 2018 Arm Limited
*
* Licensed 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.
*/
/**
* \file rtc_pl031_drv.c
* \brief Implementation of the PL031 Real Time Clock (RTC) native driver.
*
* \note PL031 device specific definitions based on
* real_time_clock_pl031_r1p3_technical_reference_manual.pdf
* which is available from http://infocenter.arm.com.
*/
#include <stddef.h>
#include "rtc_pl031_drv.h"
/**
* \brief Structure to access the memory mapped registers of the PL031.
*/
struct rtc_pl031_dev_reg_map_t {
volatile uint32_t rtcdr; /*!< Data Register */
volatile uint32_t rtcmr; /*!< Match Register */
volatile uint32_t rtclr; /*!< Load Register */
volatile uint32_t rtccr; /*!< Control Register */
volatile uint32_t rtcimsc;
/*!< Interrupt Mask Set or Clear Register */
volatile uint32_t rtcris; /*!< Raw Interrupt Status Register */
volatile uint32_t rtcmis; /*!< Masked Interrupt Status Register */
volatile uint32_t rtcicr; /*!< Interrupt Clear Register */
volatile uint32_t reserved[1008]; /*!< Reserved from Offset 0x20-0xFDC */
volatile uint32_t rtcperiphid0; /*!< Peripheral ID0 Register */
volatile uint32_t rtcperiphid1; /*!< Peripheral ID1 Register */
volatile uint32_t rtcperiphid2; /*!< Peripheral ID2 Register */
volatile uint32_t rtcperiphid3; /*!< Peripheral ID3 Register */
volatile uint32_t rtcpcellid0; /*!< Primary Cell ID0 Register */
volatile uint32_t rtcpcellid1; /*!< Primary Cell ID1 Register */
volatile uint32_t rtcpcellid2; /*!< Primary Cell ID2 Register */
volatile uint32_t rtcpcellid3; /*!< Primary Cell ID3 Register */
};
/* RTC Control Register */
#define RTC_PL031_RTCCR_ENABLE_POS 0x0U
#define RTC_PL031_RTCCR_ENABLE_MSK (0x1U << RTC_PL031_RTCCR_ENABLE_POS)
/* RTC Interrupt Mask Set or Clear Register */
#define RTC_PL031_RTCIMSC_SET_CLEAR_POS 0x0U
#define RTC_PL031_RTCIMSC_SET_CLEAR_MSK (0x1U << \
RTC_PL031_RTCIMSC_SET_CLEAR_POS)
/* RTC RAW Interrupt Status Register */
#define RTC_PL031_RTCRIS_STATUS_POS 0x0U
#define RTC_PL031_RTCRIS_STATUS_MSK (0x1U << RTC_PL031_RTCRIS_STATUS_POS)
/* RTC Masked Interrupt Status Register */
#define RTC_PL031_RTCMIS_STATUS_POS 0x0U
#define RTC_PL031_RTCMIS_STATUS_MSK (0x1U << RTC_PL031_RTCMIS_STATUS_POS)
/* RTC Interrupt Clear Register */
#define RTC_PL031_RTCICR_CLEAR_POS 0x0U
#define RTC_PL031_RTCICR_CLEAR_MSK (0x1U << RTC_PL031_RTCICR_CLEAR_POS)
bool rtc_pl031_init(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtcmr = 0U;
p_rtc->rtcicr = RTC_PL031_RTCICR_CLEAR_MSK;
return true;
}
bool rtc_pl031_dev_enable(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtccr = RTC_PL031_RTCCR_ENABLE_MSK;
return true;
}
bool rtc_pl031_dev_disable(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtccr = 0U;
return true;
}
bool rtc_pl031_read_current_time(struct rtc_pl031_dev_t* dev, uint32_t *seconds)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL || seconds == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
*seconds = (uint32_t)p_rtc->rtcdr;
return true;
}
bool rtc_pl031_write_current_time(struct rtc_pl031_dev_t* dev, uint32_t seconds)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtclr = (uint32_t)seconds;
return true;
}
bool rtc_pl031_enable_interrupt(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtcimsc = 0U;
return true;
}
bool rtc_pl031_disable_interrupt(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtcimsc = RTC_PL031_RTCIMSC_SET_CLEAR_MSK;
return true;
}
bool rtc_pl031_is_interrupt_masked(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc =
(struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
if (p_rtc->rtcimsc & RTC_PL031_RTCIMSC_SET_CLEAR_MSK){
return true;
} else {
return false;
}
}
bool rtc_pl031_is_raw_interrupt_pending(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc =
(struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
if (p_rtc->rtcris & RTC_PL031_RTCRIS_STATUS_MSK) {
return true;
} else {
return false;
}
}
bool rtc_pl031_is_masked_interrupt_pending(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc =
(struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
if (p_rtc->rtcmis & RTC_PL031_RTCMIS_STATUS_MSK) {
return true;
} else {
return false;
}
}
bool rtc_pl031_write_match_value(struct rtc_pl031_dev_t* dev, uint32_t seconds)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtcmr = (uint32_t)seconds;
return true;
}
bool rtc_pl031_clear_interrupt(struct rtc_pl031_dev_t* dev)
{
struct rtc_pl031_dev_reg_map_t* p_rtc;
if (dev == NULL) {
return false;
}
p_rtc = (struct rtc_pl031_dev_reg_map_t*) dev->cfg->base;
p_rtc->rtcicr = RTC_PL031_RTCICR_CLEAR_MSK;
return true;
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2018 Arm Limited
*
* Licensed 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.
*/
/**
* \file rtc_pl031_drv.h
* \brief Declarations for the PL031 Real Time Clock (RTC) native driver.
*/
#ifndef __RTC_PL031_DRV_H__
#define __RTC_PL031_DRV_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief RTC PL031 device configuration structure.
*/
struct rtc_pl031_dev_cfg_t {
const uintptr_t base; /*!< RTC PL031 base address */
};
/**
* \brief RTC PL031 device structure.
*/
struct rtc_pl031_dev_t {
const struct rtc_pl031_dev_cfg_t* const cfg;
/*!< RTC driver configuration */
};
/**
* \brief Initializes the RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_init(struct rtc_pl031_dev_t* dev);
/**
* \brief Enables RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_dev_enable(struct rtc_pl031_dev_t* dev);
/**
* \brief Disables RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_dev_disable(struct rtc_pl031_dev_t* dev);
/**
* \brief Reads current time from RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
* \param[out] seconds Current time in seconds.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_read_current_time(struct rtc_pl031_dev_t* dev,
uint32_t *seconds);
/**
* \brief Writes current time to RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
* \param[in] seconds Current time to be set in seconds.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_write_current_time(struct rtc_pl031_dev_t* dev,
uint32_t seconds);
/**
* \brief Clears interrupt mask of RTC PL031.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_enable_interrupt(struct rtc_pl031_dev_t* dev);
/**
* \brief Sets interrupt mask of RTC PL031.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_disable_interrupt(struct rtc_pl031_dev_t* dev);
/**
* \brief Check if RTC PL031 interrupt is masked.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that RTC PL031 interrupt is masked.
*
* \note This function does not check if dev is NULL.
*/
bool rtc_pl031_is_interrupt_masked(struct rtc_pl031_dev_t* dev);
/**
* \brief Gets raw interrupt pending status of RTC PL031.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that RTC PL031 raw interrupt
* status is pending.
*
* \note This function does not check if dev is NULL.
*/
bool rtc_pl031_is_raw_interrupt_pending(struct rtc_pl031_dev_t* dev);
/**
* \brief Gets masked interrupt pending status of RTC PL031.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that RTC PL031 masked interrupt
* status is pending.
*
* \note This function does not check if dev is NULL.
*/
bool rtc_pl031_is_masked_interrupt_pending(struct rtc_pl031_dev_t* dev);
/**
* \brief Writes match value to RTC PL031 device.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
* \param[in] seconds Match value to be set in seconds.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_write_match_value(struct rtc_pl031_dev_t* dev, uint32_t seconds);
/**
* \brief Clear interrupt status bit of RTC PL031.
*
* \param[in] dev RTC device structure \ref rtc_pl031_dev_t.
*
* \return Return true indicates that the function executed successfully,
* otherwise an error occurred.
*/
bool rtc_pl031_clear_interrupt(struct rtc_pl031_dev_t* dev);
#ifdef __cplusplus
}
#endif
#endif /* __RTC_PL031_DRV_H__ */

View File

@ -47,6 +47,13 @@ struct dualtimer_cmsdk_dev_t CMSDK_DUALTIMER_DEV = {&(CMSDK_DUALTIMER_DEV_CFG),
&(CMSDK_DUALTIMER_DEV_DATA)};
#endif
/* PL031 Real-Time Clock structure */
#ifdef RTC_PL031
static const struct rtc_pl031_dev_cfg_t RTC_PL031_DEV_CFG = {
.base = CMSDK_RTC_BASE};
struct rtc_pl031_dev_t RTC_PL031_DEV = {&(RTC_PL031_DEV_CFG)};
#endif
/* ARM GPIO driver structures */
#ifdef ARM_GPIO0
static const struct arm_gpio_dev_cfg_t ARM_GPIO0_DEV_CFG = {

View File

@ -39,6 +39,12 @@ extern struct timer_cmsdk_dev_t CMSDK_TIMER1_DEV;
extern struct dualtimer_cmsdk_dev_t CMSDK_DUALTIMER_DEV;
#endif
/* RTC PL031 */
#ifdef RTC_PL031
#include "rtc_pl031_drv.h"
extern struct rtc_pl031_dev_t RTC_PL031_DEV;
#endif
/* ARM GPIO driver structures */
#ifdef ARM_GPIO0
#include "arm_gpio_drv.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017 ARM Limited
* Copyright (c) 2018 Arm Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,8 +15,10 @@
*/
#include "rtc_api.h"
#include "device.h"
#include "cmsis.h"
#include "platform_devices.h"
#include "rtc_pl031_drv.h"
static uint32_t is_enabled = 0;
/**
* \defgroup hal_rtc RTC hal functions
@ -31,7 +33,9 @@
*/
void rtc_init(void)
{
CMSDK_RTC->RTCCR |= (1 << CMSDK_RTC_ENABLE_Pos);
rtc_pl031_init(&RTC_PL031_DEV);
rtc_pl031_dev_enable(&RTC_PL031_DEV);
is_enabled = 1;
}
/**
@ -41,7 +45,7 @@ void rtc_init(void)
*/
void rtc_free(void)
{
/* Not supported */
is_enabled = 0;
}
/**
@ -51,17 +55,24 @@ void rtc_free(void)
*/
int rtc_isenabled(void)
{
return (CMSDK_RTC->RTCCR & CMSDK_RTC_ENABLE_Msk);
return is_enabled;
}
/**
* \brief Get the current time from the RTC peripheral
*
* Sysclock and RTC clock may not be in sync which can cause reading
* out metastable values. It's usually prevented by adding a loop,
* however PL031 has a syncronisation block to prevent this, therefore
* no additional loop needed.
*
* \return The current time in seconds
*/
time_t rtc_read(void)
{
return (time_t)CMSDK_RTC->RTCDR;
uint32_t val;
rtc_pl031_read_current_time(&RTC_PL031_DEV, &val);
return (time_t)val;
}
/**
@ -72,7 +83,7 @@ time_t rtc_read(void)
void rtc_write(time_t t)
{
CMSDK_RTC->RTCLR = (uint32_t)t;
rtc_pl031_write_current_time(&RTC_PL031_DEV, (uint32_t)t);
}
/**@}*/

View File

@ -0,0 +1,50 @@
/** \addtogroup hal */
/** @{*/
/* mbed Microcontroller Library
* Copyright (c) 2018 Arm Limited
*
* Licensed 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.
*/
#include "platform_devices.h"
#include "sleep_api.h"
#include "timer_cmsdk_drv.h"
#if DEVICE_SLEEP
void hal_sleep(void)
{
__WFI();
}
/* Since there is no power management implemented in CM3DS, Deep Sleep could be
* supported only by additional software components, registering and managing
* the currently configured IPs. This would also mean a huge implementation
* overhead, that is not intended to be added. Therefore, Deep Sleep is almost
* identical to Sleep, representing a "Waiting For Interrupt" state, and
* disabling the Microsec ticker in addition */
void hal_deepsleep(void)
{
#if USEC_TIMER_DEV
timer_cmsdk_disable(&USEC_TIMER_DEV);
#endif
__WFI();
#if USEC_TIMER_DEV
timer_cmsdk_enable(&USEC_TIMER_DEV);
#endif
}
#endif
/**@}*/

View File

@ -2934,7 +2934,7 @@
"extra_labels": ["ARM_SSG", "CM3DS_MPS2"],
"OUTPUT_EXT": "elf",
"macros": ["CMSDK_CM3DS"],
"device_has": ["ANALOGIN", "EMAC", "FLASH", "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", "PORTINOUT", "PORTOUT", "SERIAL", "SPI", "TRNG", "USTICKER"],
"device_has": ["ANALOGIN", "EMAC", "FLASH", "I2C", "INTERRUPTIN", "LPTICKER", "PORTIN", "PORTINOUT", "PORTOUT", "RTC", "SERIAL", "SLEEP", "SPI", "TRNG", "USTICKER"],
"release_versions": ["2", "5"],
"copy_method": "mps2",
"reset_method": "reboot.txt",