From 5d0f0de4aa3791b32b4b48e5f97a1e68f07641e3 Mon Sep 17 00:00:00 2001 From: Alexander Krutwig Date: Thu, 28 Jul 2016 13:33:50 +0200 Subject: bsp/atsam: Add power support --- c/src/lib/libbsp/arm/atsam/Makefile.am | 4 + c/src/lib/libbsp/arm/atsam/include/bsp.h | 2 + c/src/lib/libbsp/arm/atsam/include/power.h | 245 +++++++++++++++++++++++ c/src/lib/libbsp/arm/atsam/preinstall.am | 4 + c/src/lib/libbsp/arm/atsam/startup/power-clock.c | 44 ++++ c/src/lib/libbsp/arm/atsam/startup/power-rtc.c | 104 ++++++++++ c/src/lib/libbsp/arm/atsam/startup/power.c | 102 ++++++++++ 7 files changed, 505 insertions(+) create mode 100644 c/src/lib/libbsp/arm/atsam/include/power.h create mode 100644 c/src/lib/libbsp/arm/atsam/startup/power-clock.c create mode 100644 c/src/lib/libbsp/arm/atsam/startup/power-rtc.c create mode 100644 c/src/lib/libbsp/arm/atsam/startup/power.c diff --git a/c/src/lib/libbsp/arm/atsam/Makefile.am b/c/src/lib/libbsp/arm/atsam/Makefile.am index 68bc07520d..2cfd22777d 100644 --- a/c/src/lib/libbsp/arm/atsam/Makefile.am +++ b/c/src/lib/libbsp/arm/atsam/Makefile.am @@ -53,6 +53,7 @@ include_bsp_HEADERS += include/irq.h include_bsp_HEADERS += include/pin-config.h include_bsp_HEADERS += include/atsam-i2c.h include_bsp_HEADERS += include/i2c.h +include_bsp_HEADERS += include/power.h include_libchipdir = $(includedir)/libchip @@ -394,6 +395,9 @@ libbsp_a_SOURCES += ../shared/armv7m/startup/bspreset.c libbsp_a_SOURCES += startup/bspstart.c libbsp_a_SOURCES += startup/bspstarthooks.c libbsp_a_SOURCES += startup/pin-config.c +libbsp_a_SOURCES += startup/power.c +libbsp_a_SOURCES += startup/power-rtc.c +libbsp_a_SOURCES += startup/power-clock.c # IRQ libbsp_a_SOURCES += ../../shared/src/irq-default-handler.c diff --git a/c/src/lib/libbsp/arm/atsam/include/bsp.h b/c/src/lib/libbsp/arm/atsam/include/bsp.h index 2c1992d9d7..89968e9c07 100644 --- a/c/src/lib/libbsp/arm/atsam/include/bsp.h +++ b/c/src/lib/libbsp/arm/atsam/include/bsp.h @@ -89,6 +89,8 @@ extern char atsam_memory_sdram_size[]; void atsam_rtc_get_time(rtems_time_of_day *tod); + + /** @} */ #ifdef __cplusplus diff --git a/c/src/lib/libbsp/arm/atsam/include/power.h b/c/src/lib/libbsp/arm/atsam/include/power.h new file mode 100644 index 0000000000..a352386a0e --- /dev/null +++ b/c/src/lib/libbsp/arm/atsam/include/power.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef LIBBSP_ARM_ATSAM_POWER_H +#define LIBBSP_ARM_ATSAM_POWER_H + +#include + +#include + +#ifdef __cplusplus +extern "C"{ +#endif /* __cplusplus */ + +/** + * @brief Status of the Low Power Support + */ +typedef enum { + /** + * @brief Used for Initialization of Handlers + */ + ATSAM_POWER_INIT, + /** + * @brief Used for Switching On of Handlers + */ + ATSAM_POWER_ON, + /** + * @brief Used for Switching Off of Handlers + */ + ATSAM_POWER_OFF +} atsam_power_state; + +/** + * @brief Control structure for power control handling + */ +typedef struct atsam_power_control { + /** + * @brief Data pointer to the handler with its desired state + */ + void (*handler)( + const struct atsam_power_control *control, + atsam_power_state state + ); + /** + * @brief Data chunk that is used by the handler + */ + union { + void *arg; + struct { + uint8_t first; + uint8_t last; + } peripherals; + } data; +} atsam_power_control; + +/** + * @brief Performs a power state change according to the state parameter. + * + * The handlers of the control table are invoked in forward order (invocation + * starts with table index zero) for the ATSAM_POWER_INIT and ATSAM_POWER_OFF + * states, otherwise the handlers are invoked in reverse order (invocation + * starts with the last table index). + * + * @param controls Table with power controls. + * @param n Count of power control table entries. + * @param state The desired power state. + * + * @code + * #include + * #include + * + * #include + * + * static atsam_power_data_rtc_driver rtc_data = { .interval = 5 }; + * + * static const atsam_power_control power_controls[] = { + * ATSAM_POWER_CLOCK_DRIVER, + * ATSAM_POWER_RTC_DRIVER(&rtc_data), + * ATSAM_POWER_SLEEP_MODE + * }; + * + * static pthread_once_t once = PTHREAD_ONCE_INIT; + * + * static void init(void) + * { + * atsam_power_change_state( + * &power_controls[0], + * RTEMS_ARRAY_SIZE(power_controls), + * ATSAM_POWER_INIT + * ); + * } + * + * void power_init(void) + * { + * pthread_once(&once, init); + * } + * + * void low_power(void) + * { + * atsam_power_change_state( + * &power_controls[0], + * RTEMS_ARRAY_SIZE(power_controls), + * ATSAM_POWER_OFF + * ); + * atsam_power_change_state( + * &power_controls[0], + * RTEMS_ARRAY_SIZE(power_controls), + * ATSAM_POWER_ON + * ); + * } + * @end + */ +void atsam_power_change_state( + const atsam_power_control *controls, + size_t n, + atsam_power_state state +); + +/** + * @brief Power handler for a set of peripherals according to the specified + * peripheral indices. + * + * For the power off state, the peripherals are enabled in the PMC. + * + * For the power on state, the peripherals are disabled in the Power Management + * Controller (PMC). + * + * @see ATSAM_POWER_PERIPHERAL(). + */ +void atsam_power_handler_peripheral( + const atsam_power_control *controls, + atsam_power_state state +); + +/** + * @brief Power handler for the clock driver. + * + * For the power off state, the system tick is disabled. + * + * For the power on state, the system tick is enabled. In case no clock driver + * is used by the application, then this may lead to a spurious interrupt + * resulting in a fatal error. + * + * @see ATSAM_POWER_CLOCK_DRIVER(). + */ +void atsam_power_handler_clock_driver( + const atsam_power_control *controls, + atsam_power_state state +); + +/** + * @brief Power handler for the RTC driver. + * + * This handler installs an interrupt handler during power support initialization. + * + * For the power off state, the RTC alarm interrupt is set up according to the + * interval of the corresponding handler data. + * + * For the power on state, the RTC alarm interrupt is disabled. + * + * @see ATSAM_POWER_RTC_DRIVER(). + */ +void atsam_power_handler_rtc_driver( + const atsam_power_control *controls, + atsam_power_state state +); + +/** + * @brief Power handler to enter the processor sleep mode. + * + * For the power off state, the processor is set into the sleep mode and issues + * a wait for interrupt instruction. + * + * @see ATSAM_POWER_SLEEP_MODE(). + */ +void atsam_power_handler_sleep_mode( + const atsam_power_control *controls, + atsam_power_state state +); + +/** + * @brief Initializer for a peripheral power support. + * + * @param f The first peripheral index. + * @param l The last peripheral index. + */ +#define ATSAM_POWER_PERIPHERAL(f, l) \ + { \ + .handler = atsam_power_handler_peripheral, \ + .data = { .peripherals = { .first = f, .last = l } } \ + } + +#define ATSAM_POWER_HANDLER(h, a) \ + { \ + .handler = h, \ + .data = { .arg = a } \ + } + +#define ATSAM_POWER_CLOCK_DRIVER \ + { .handler = atsam_power_handler_clock_driver } + +#define ATSAM_POWER_SLEEP_MODE \ + { .handler = atsam_power_handler_sleep_mode } + +/** + * @brief Data for RTC driver power support. + * + * @see ATSAM_POWER_RTC_DRIVER(). + */ +typedef struct { + /** + * @brief Interval in seconds for which the power off mode should be active. + */ + uint8_t interval; +} atsam_power_data_rtc_driver; + +/** + * @brief Initializer for RTC driver power support. + * + * @param a Pointer to RTC driver power data. + * + * @see atsam_power_data_rtc_driver. + */ +#define ATSAM_POWER_RTC_DRIVER(a) \ + { \ + .handler = atsam_power_handler_rtc_driver, \ + .data = { .arg = a } \ + } + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_ARM_ATSAM_POWER_H */ diff --git a/c/src/lib/libbsp/arm/atsam/preinstall.am b/c/src/lib/libbsp/arm/atsam/preinstall.am index d9494fb256..9335a9d693 100644 --- a/c/src/lib/libbsp/arm/atsam/preinstall.am +++ b/c/src/lib/libbsp/arm/atsam/preinstall.am @@ -141,6 +141,10 @@ $(PROJECT_INCLUDE)/bsp/i2c.h: include/i2c.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/i2c.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/i2c.h +$(PROJECT_INCLUDE)/bsp/power.h: include/power.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/power.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/power.h + $(PROJECT_INCLUDE)/libchip/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/libchip @: > $(PROJECT_INCLUDE)/libchip/$(dirstamp) diff --git a/c/src/lib/libbsp/arm/atsam/startup/power-clock.c b/c/src/lib/libbsp/arm/atsam/startup/power-clock.c new file mode 100644 index 0000000000..e04933285e --- /dev/null +++ b/c/src/lib/libbsp/arm/atsam/startup/power-clock.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include +#include + +#include + +#include + +void atsam_power_handler_clock_driver( + const atsam_power_control *control, + atsam_power_state state +) +{ + volatile ARMV7M_Systick *systick = _ARMV7M_Systick; + + (void) control; + + switch (state) { + case ATSAM_POWER_ON: + systick->csr = ARMV7M_SYSTICK_CSR_ENABLE | + ARMV7M_SYSTICK_CSR_TICKINT | + ARMV7M_SYSTICK_CSR_CLKSOURCE; + break; + case ATSAM_POWER_OFF: + systick->csr = 0; + break; + default: + break; + } +} diff --git a/c/src/lib/libbsp/arm/atsam/startup/power-rtc.c b/c/src/lib/libbsp/arm/atsam/startup/power-rtc.c new file mode 100644 index 0000000000..b60235ac29 --- /dev/null +++ b/c/src/lib/libbsp/arm/atsam/startup/power-rtc.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include +#include + +#include + +#define ATSAM_ENABLE_ALARM_INTERRUPT (1u << 1) + +static void set_rtc_alarm_interrupt(uint8_t interval) +{ + Rtc *rtc = RTC; + rtems_time_of_day tod; + + /* Clear current status register */ + RTC_ClearSCCR(rtc, 0x3F); + + atsam_rtc_get_time(&tod); + tod.second = (tod.second + interval) % 60; + tod.second = (((tod.second / 10) << 4) | (tod.second % 10)); + + rtc->RTC_TIMALR &= ~RTC_TIMALR_SECEN; + rtc->RTC_TIMALR = tod.second; + rtc->RTC_TIMALR |= RTC_TIMALR_SECEN; + RTC_EnableIt(rtc, ATSAM_ENABLE_ALARM_INTERRUPT); +} + +static void rtc_interrupt_handler(void *arg) +{ + atsam_power_data_rtc_driver *rtc_data; + + rtc_data = (atsam_power_data_rtc_driver *)arg; + set_rtc_alarm_interrupt(rtc_data->interval); +} + +static void rtc_alarm_handler(void *arg) +{ + Rtc *rtc = RTC; + rtems_status_code sc; + + /* Clear current status register */ + RTC_ClearSCCR(rtc, 0x3F); + + /* Switch off all RTC interrupts */ + RTC_DisableIt(rtc, 0x1F); + + /* Install RTC interrupt handler */ + sc = rtems_interrupt_handler_install(RTC_IRQn, + "RTC", + RTEMS_INTERRUPT_UNIQUE, + rtc_interrupt_handler, + arg + ); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void set_time(void) +{ + rtems_time_of_day tod; + rtems_status_code sc; + + atsam_rtc_get_time(&tod); + sc = rtems_clock_set(&tod); + assert(sc == RTEMS_SUCCESSFUL); +} + +void atsam_power_handler_rtc_driver( + const atsam_power_control *control, + atsam_power_state state +) +{ + atsam_power_data_rtc_driver *rtc_data; + Rtc *rtc = RTC; + + rtc_data = (atsam_power_data_rtc_driver *)control->data.arg; + + switch (state) { + case ATSAM_POWER_ON: + RTC_DisableIt(rtc, ATSAM_ENABLE_ALARM_INTERRUPT); + set_time(); + break; + case ATSAM_POWER_OFF: + set_rtc_alarm_interrupt(rtc_data->interval); + break; + case ATSAM_POWER_INIT: + rtc_alarm_handler(rtc_data); + break; + default: + break; + } +} diff --git a/c/src/lib/libbsp/arm/atsam/startup/power.c b/c/src/lib/libbsp/arm/atsam/startup/power.c new file mode 100644 index 0000000000..f9b5a3925d --- /dev/null +++ b/c/src/lib/libbsp/arm/atsam/startup/power.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include +#include + +#include + +/* SCR Sleep deep bit */ +#define SCR_SLEEPDEEP (1u << 2) + +void atsam_power_change_state( + const atsam_power_control *controls, + size_t n, + atsam_power_state state +) +{ + size_t i; + + switch (state) { + case ATSAM_POWER_ON: + for (i = n; i > 0; --i) { + const atsam_power_control *c; + + c = &controls[i - 1]; + (*c->handler)(c, state); + } + + break; + case ATSAM_POWER_INIT: + case ATSAM_POWER_OFF: + for (i = 0; i < n; ++i) { + const atsam_power_control *c; + + c = &controls[i]; + (*c->handler)(c, state); + } + + break; + default: + break; + } +} + +void atsam_power_handler_peripheral( + const atsam_power_control *control, + atsam_power_state state +) +{ + uint32_t id; + uint32_t end; + + id = control->data.peripherals.first; + end = control->data.peripherals.last + 1; + + switch (state) { + case ATSAM_POWER_ON: + while (id != end) { + PMC_EnablePeripheral(id); + ++id; + } + break; + case ATSAM_POWER_OFF: + while (id != end) { + PMC_DisablePeripheral(id); + ++id; + } + break; + default: + break; + } +} + +void atsam_power_handler_sleep_mode(const atsam_power_control *control, atsam_power_state state) +{ + (void) control; + + switch (state) { + case ATSAM_POWER_OFF: + /* Enable Low Power Mode in the Fast Startup Mode Register */ + PMC->PMC_FSMR &= (uint32_t)~PMC_FSMR_LPM; + /* Do not set deep sleep, but "normal" sleep */ + SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP; + + __asm__ volatile ("wfi"); + break; + default: + break; + } +} -- cgit v1.2.3