[rtems commit] bsp/atsam: Add power support

Sebastian Huber sebh at rtems.org
Mon Aug 8 11:56:53 UTC 2016


Module:    rtems
Branch:    master
Commit:    5d0f0de4aa3791b32b4b48e5f97a1e68f07641e3
Changeset: http://git.rtems.org/rtems/commit/?id=5d0f0de4aa3791b32b4b48e5f97a1e68f07641e3

Author:    Alexander Krutwig <alexander.krutwig at embedded-brains.de>
Date:      Thu Jul 28 13:33:50 2016 +0200

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(+)

diff --git a/c/src/lib/libbsp/arm/atsam/Makefile.am b/c/src/lib/libbsp/arm/atsam/Makefile.am
index 68bc075..2cfd227 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 2c1992d..89968e9 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 0000000..a352386
--- /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
+ *  <rtems at embedded-brains.de>
+ *
+ * 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 <sys/types.h>
+
+#include <stdint.h>
+
+#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 <rtems.h>
+ * #include <pthread.h>
+ *
+ * #include <bsp/power.h>
+ *
+ * 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 d9494fb..9335a9d 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 0000000..e049332
--- /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
+ *  <rtems at embedded-brains.de>
+ *
+ * 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 <bsp.h>
+#include <bsp/power.h>
+#include <bsp/irq.h>
+
+#include <libchip/chip.h>
+
+#include <rtems/score/armv7m.h>
+
+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 0000000..b60235a
--- /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
+ *  <rtems at embedded-brains.de>
+ *
+ * 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 <bsp.h>
+#include <bsp/power.h>
+#include <bsp/irq.h>
+
+#include <libchip/chip.h>
+
+#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 0000000..f9b5a39
--- /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
+ *  <rtems at embedded-brains.de>
+ *
+ * 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 <bsp.h>
+#include <bsp/power.h>
+#include <bsp/irq.h>
+
+#include <libchip/chip.h>
+
+/* 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;
+	}
+}



More information about the vc mailing list