quark_se: PM: Add multicore support

This patch changes Quark SE power drivers to support multicore scenarios
e.g. both LMT and ARC core are enabled and manage power.

Handling LPS states in multicore scenarios are dead simple because LPS
states are core-specific states. It means that putting the LMT core in
LPS doesn't affect the ARC core, and vice-versa. DEEP_SLEEP state, on
the other hand, affects both cores since it turns power off from the SoC
and both cores are shutdown. It means that if LMT puts the system in
DEEP_SLEEP, ARC core is shutdown even if it is busy handling some task.

In order to support the multicore scenario, this patch introduces the
SYS_POWER_STATE_DEEP_SLEEP_2 state to both ARC and x86 power drivers.

On ARC, this state works as following:
1) Save ARC execution context;
2) Raise a flag to inform the x86 core that ARC is ready to enter in
   DEEP_SLEEP;
3) Enter in the lowest core-specific power state, which in this case is
   LPSS.

On x86, DEEP_SLEEP_2 is very similar to DEEP_SLEEP. The difference relies
in the post_ops() which calls _arc_init() in order to start ARC core so
it can restore its context.

This patch also adds the test/power/multicore/ directory which provides
sample application to x86 and ARC cores in order to easily verify the
multicore support. In test/power/multicore/README.rst you can find more
details regarding the applications.

Jira: ZEP-1103

Change-Id: Ie28ba6d193ea0e58fca69d38f8d3c38ca259a9ef
Signed-off-by: Andre Guedes <andre.guedes@intel.com>
This commit is contained in:
Andre Guedes 2016-12-21 13:26:17 -08:00 committed by Anas Nashif
parent cd35f06de8
commit 6f141a5576
21 changed files with 623 additions and 5 deletions

View file

@ -73,6 +73,10 @@ extern "C" {
.endm
.macro _discard_callee_saved_regs
add_s sp, sp, ___callee_saved_stack_t_SIZEOF
.endm
/*
* Must be called with interrupts locked or in P0.
* Upon exit, sp will be pointing to the stack frame.

View file

@ -1,4 +1,3 @@
ccflags-y +=-I$(srctree)/arch/x86/soc/intel_quark
ccflags-y +=-I$(srctree)/include
ccflags-y +=-I$(srctree)/include/drivers
ccflags-y +=-I$(srctree)/drivers

View file

@ -4,6 +4,7 @@ soc-cflags = $(call cc-option,-mcpu=quarkse_em) \
soc-aflags = $(soc-cflags)
soc-cxxflags = $(soc-cflags)
soc-cflags += -DQM_SENSOR=1
soc-cflags += -I$(srctree)/arch/x86/soc/intel_quark
## FIXME
SOC_SERIES = quark_se

View file

@ -11,6 +11,7 @@
#include <soc_power.h>
#include <init.h>
#include <kernel_structs.h>
#include <soc.h>
#include "power_states.h"
#include "ss_power_states.h"
@ -19,6 +20,7 @@
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
extern void _power_soc_sleep(void);
extern void _power_soc_deep_sleep(void);
extern void _power_soc_deep_sleep_2(void);
static void _deep_sleep(enum power_states state)
{
@ -75,6 +77,11 @@ void _sys_soc_set_power_state(enum power_states state)
case SYS_POWER_STATE_DEEP_SLEEP_1:
_deep_sleep(state);
break;
case SYS_POWER_STATE_DEEP_SLEEP_2:
ss_power_soc_lpss_enable();
power_soc_set_ss_restore_flag();
_power_soc_deep_sleep_2();
break;
#endif
default:
break;
@ -97,6 +104,22 @@ void _sys_soc_power_state_post_ops(enum power_states state)
case SYS_POWER_STATE_DEEP_SLEEP_1:
__builtin_arc_seti(0);
break;
case SYS_POWER_STATE_DEEP_SLEEP_2:
ss_power_soc_lpss_disable();
/* If flag is cleared it means the system entered in
* sleep state while we were in LPS. In that case, we
* must set ARC_READY flag so x86 core can continue
* its execution.
*/
if ((QM_SCSS_GP->gp0 & GP0_BIT_SLEEP_READY) == 0) {
_quark_se_ss_ready();
__builtin_arc_seti(0);
} else {
QM_SCSS_GP->gp0 &= ~GP0_BIT_SLEEP_READY;
QM_SCSS_GP->gps0 &= ~QM_GPS0_BIT_SENSOR_WAKEUP;
}
break;
default:
break;
}

View file

@ -14,7 +14,6 @@
#include <kernel.h>
#include "soc.h"
#include <init.h>
#include <quark_se/shared_mem.h>
/**
* @brief perform basic hardware initialization
@ -25,7 +24,8 @@ static int quark_se_arc_init(struct device *arg)
{
ARG_UNUSED(arg);
shared_data->flags |= ARC_READY;
_quark_se_ss_ready();
return 0;
}

View file

@ -106,6 +106,7 @@
#include <misc/util.h>
#include <drivers/rand32.h>
#include <quark_se/shared_mem.h>
#define INT_ENABLE_ARC ~(0x00000001 << 8)
#define INT_ENABLE_ARC_BIT_POS (8)
@ -184,6 +185,11 @@
#define SPI_DW_IRQ_FLAGS 0
static inline void _quark_se_ss_ready(void)
{
shared_data->flags |= ARC_READY;
}
#endif /* !_ASMLANGUAGE */
#endif /* _BOARD__H_ */

View file

@ -15,9 +15,14 @@ GTEXT(_sys_soc_resume_from_deep_sleep)
GTEXT(_power_restore_cpu_context)
GTEXT(_power_soc_sleep)
GTEXT(_power_soc_deep_sleep)
GTEXT(_power_soc_deep_sleep_2)
#define GPS0_REGISTER 0xb0800100
#define GP0_REGISTER 0xb0800114
#define GP0_BIT_SLEEP_READY 0
#define RESTORE_SS_BIT 2
#define SLEEP_INTR_ENABLED_BIT 4
#define SLEEP_MODE_RTC_ENABLED_BIT 5
SECTION_FUNC(TEXT, _sys_soc_resume_from_deep_sleep)
/* Check is this wakeup after sleep event. */
@ -68,6 +73,47 @@ SECTION_FUNC(TEXT, _power_soc_deep_sleep)
j @power_soc_deep_sleep
/* Does not return */
SECTION_FUNC(TEXT, _power_soc_deep_sleep_2)
/*
* Setup 'sleep' instruction operand.
*/
/* Get interrupt priority from status32 registers. */
lr r0, [_ARC_V2_STATUS32]
lsr r0, r0
and r0, r0, 0xF
/* Enable interrupts */
bset r0, r0, SLEEP_INTR_ENABLED_BIT
/* Set 'sleep' mode corresponding to SS2 state i.e. core disabled,
* timers disabled, RTC enabled.
*/
bset r0, r0, SLEEP_MODE_RTC_ENABLED_BIT
/*
* Save the return address.
* The restore function will pop this and jump
* back to the caller.
*/
push_s blink
jl @save_cpu_context
ld r1, [GP0_REGISTER]
bset r1, r1, GP0_BIT_SLEEP_READY
st r1, [GP0_REGISTER]
sleep r0
/* If we reach this code it means the x86 core didn't put the
* system in SYS_POWER_STATE_DEEP_SLEEP_2 state while we were
* in LPS. Then discard saved context.
*/
_discard_callee_saved_regs
pop_s blink
j_s [blink]
SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
mov_s r1, _kernel
ld_s r2, [r1, _kernel_offset_to_current]

View file

@ -11,12 +11,24 @@
extern "C" {
#endif
/*
* Bit 0 from GP0 register is used internally by the kernel
* to handle PM multicore support. Any change on QMSI and/or
* bootloader which affects this bit should take it in
* consideration.
*/
#define GP0_BIT_SLEEP_READY BIT(0)
enum power_states {
SYS_POWER_STATE_CPU_LPS, /* SS2 with LPSS enabled state */
SYS_POWER_STATE_CPU_LPS_1, /* SS2 state */
SYS_POWER_STATE_CPU_LPS_2, /* SS1 state with Timer ON */
SYS_POWER_STATE_DEEP_SLEEP, /* DEEP SLEEP state */
SYS_POWER_STATE_DEEP_SLEEP_1, /* SLEEP state */
SYS_POWER_STATE_DEEP_SLEEP_2, /* Multicore DEEP SLEEP state.
* Execution context is saved
* and core enters in LPS state.
*/
SYS_POWER_STATE_MAX
};
@ -42,6 +54,9 @@ enum power_states {
* SYS_POWER_STATE_DEEP_SLEEP_1: Only Always-On peripherals can wake up
* the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator.
*
* SYS_POWER_STATE_DEEP_SLEEP_2: Only Always-On peripherals can wake up
* the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator.
*
* Considerations around SYS_POWER_STATE_CPU_LPS (LPSS state):
* -----------------------------------------------------------
*

View file

@ -9,6 +9,7 @@
#include <misc/__assert.h>
#include <power.h>
#include <soc_power.h>
#include <soc.h>
#include "power_states.h"
@ -47,6 +48,7 @@ static void _deep_sleep(enum power_states state)
_power_soc_sleep();
break;
case SYS_POWER_STATE_DEEP_SLEEP:
case SYS_POWER_STATE_DEEP_SLEEP_2:
_power_soc_deep_sleep();
break;
default:
@ -70,6 +72,7 @@ void _sys_soc_set_power_state(enum power_states state)
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
case SYS_POWER_STATE_DEEP_SLEEP:
case SYS_POWER_STATE_DEEP_SLEEP_1:
case SYS_POWER_STATE_DEEP_SLEEP_2:
_deep_sleep(state);
break;
#endif
@ -87,6 +90,11 @@ void _sys_soc_power_state_post_ops(enum power_states state)
__asm__ volatile("sti");
break;
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
case SYS_POWER_STATE_DEEP_SLEEP_2:
#ifdef CONFIG_ARC_INIT
_arc_init(NULL);
#endif /* CONFIG_ARC_INIT */
/* Fallthrough */
case SYS_POWER_STATE_DEEP_SLEEP:
case SYS_POWER_STATE_DEEP_SLEEP_1:
__asm__ volatile("sti");
@ -96,3 +104,8 @@ void _sys_soc_power_state_post_ops(enum power_states state)
break;
}
}
bool _sys_soc_power_state_is_arc_ready(void)
{
return QM_SCSS_GP->gp0 & GP0_BIT_SLEEP_READY ? true : false;
}

View file

@ -37,7 +37,8 @@
* starts the ARC processor.
* @return N/A
*/
static int arc_init(struct device *arg)
/* This function is also called at deep sleep resume. */
int _arc_init(struct device *arg)
{
uint32_t *reset_vector;
@ -83,7 +84,7 @@ skip_arc_init:
return 0;
}
SYS_INIT(arc_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
SYS_INIT(_arc_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
#endif /*CONFIG_ARC_INIT*/

View file

@ -64,4 +64,8 @@
#endif /* _ASMLANGUAGE */
#ifdef CONFIG_ARC_INIT
int _arc_init(struct device *arg);
#endif /* CONFIG_ARC_INIT */
#endif /* __SOC_H_ */

View file

@ -11,12 +11,23 @@
extern "C" {
#endif
/*
* Bit 0 from GP0 register is used internally by the kernel
* to handle PM multicore support. Any change on QMSI and/or
* bootloader which affects this bit should take it in
* consideration.
*/
#define GP0_BIT_SLEEP_READY BIT(0)
enum power_states {
SYS_POWER_STATE_CPU_LPS, /* C2LP state */
SYS_POWER_STATE_CPU_LPS_1, /* C2 state */
SYS_POWER_STATE_CPU_LPS_2, /* C1 state */
SYS_POWER_STATE_DEEP_SLEEP, /* DEEP SLEEP state */
SYS_POWER_STATE_DEEP_SLEEP_1, /* SLEEP state */
SYS_POWER_STATE_DEEP_SLEEP_2, /* Multicore support for
* DEEP_SLEEP state.
*/
SYS_POWER_STATE_MAX
};
@ -42,6 +53,9 @@ enum power_states {
*
* SYS_POWER_STATE_DEEP_SLEEP_1: Only Always-On peripherals can wake up
* the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator.
*
* SYS_POWER_STATE_DEEP_SLEEP_2: Only Always-On peripherals can wake up
* the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator.
*/
void _sys_soc_set_power_state(enum power_states state);
@ -55,6 +69,14 @@ void _sys_soc_set_power_state(enum power_states state);
*/
void _sys_soc_power_state_post_ops(enum power_states state);
/**
* @brief Check if ARC core is ready to enter in DEEP_SLEEP states.
*
* @retval true If ARC is ready.
* @retval false Otherwise.
*/
bool _sys_soc_power_state_is_arc_ready(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,140 @@
Multicore Test App
##################
These applications aim to provide an easy way to test the PM multicore support
from Quark SE. It implements two common flows: 1) LMT core is idle while ARC
core is busy so the system is not put on DEEP SLEEP mode, and 2) LMT core is
idle when ARC core is also idle so the system enters in DEEP SLEEP mode.
These applications implement a master/slave approach where LMT application
plays the master role while ARC application plays the slave. The master is the
one which controls the wakeup device (in this example it is the RTC) and
actually puts the system in deep sleep mode.
To keep the synchronization logic simple and demonstrate the multi-core
coordination, we do not handle any events in ARC other than the notification
IPM from LMT. Handling events in ARC itself may need more complex communication
and synchronization logic between the applications of the the 2 cores. For
example, such an implementation should be able to handle the corner case of
ARC getting woken up by some event when LMT is in the process of putting the
SOC in deep sleep.
In the following section the working mechanism from both ARC and LMT
applications are described, and building and wiring instructions are
provided.
test/power/multicore/arc
************************
The ARC application is very simple, it keeps the system bouncing between 'busy'
and 'idle' states. The application keeps the system busy for 10 seconds and
then idle until woken up by IPM event from LMT. When system goes into idle,
the application puts the system in SYS_POWER_STATE_DEEP_SLEEP_2 state which
allows LMT core to put the system in sleep mode.
The application uses UART_0 as console output device so, in order to be able
to see ARC output messages, make sure you have attached a serial cable to
UART_0. In 'quark_se_c1000_ss_devboard', UART_0 pins are in J14 header. The
table below shows the wiring instructions.
+---------+------------------+
| J14 PIN | SERIAL CABLE PIN |
+=========+==================+
| 3 | RXD |
+---------+------------------+
| 5 | TXD |
+---------+------------------+
| 11 | GND |
+---------+------------------+
If your wiring is correct, you should see the following output on console:
::
ARC: Quark SE PM Multicore Demo
ARC: busy
ARC: idle
ARC: busy
ARC: idle
ARC: busy
ARC: idle
ARC: busy
...
To build the ARC application, run the following commands:
::
$ cd tests/power/multicore/arc/
$ make
test/power/multicore/lmt
************************
The LMT application is very simple and also keeps the system bouncing between
'busy' and 'idle'. When the system goes into idle, the application tries to
put the system in DEEP_SLEEP state. If ARC core is busy, it fails. If ARC core
is idle, it succeeds.
When 'TEST_CASE=sleep-success', the application will be busy for 15 seconds
and idle for 5. This means that ARC core will be idle when LMT core tries to
enter in DEEP_SLEEP, and it will succeed. In this case, the output on your
console should look like this:
::
LMT: Quark SE PM Multicore Demo
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Succeed.
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Succeed.
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Succeed.
...
To build the LMT application which tests the "success" path, run the following
commands:
::
$ cd tests/power/multicore/lmt TEST_CASE=sleep-success
$ make
When 'TEST_CASE=sleep-fail', application is busy for 5 seconds and idle for 15
seconds. This means that ARC core will be busy when LMT core tries to enter in
DEEP_SLEEP, and it will fail. In this case the output on your console should
look like this:
::
LMT: Quark SE PM Multicore Demo
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Failed. ARC is busy.
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Failed. ARC is busy.
LMT: busy
LMT: idle
LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state
LMT: Failed. ARC is busy.
...
To build the LMT application which tests the "failure" path, run the following
commands:
::
$ cd tests/power/multicore/lmt
$ make TEST_CASE=sleep-fail
The application uses UART_1 device as console output device, which is the
default console device.

View file

@ -0,0 +1,4 @@
BOARD = quark_se_c1000_ss_devboard
CONF_FILE = prj.conf
include ${ZEPHYR_BASE}/Makefile.inc

View file

@ -0,0 +1,9 @@
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_DEEP_SLEEP=y
CONFIG_SYS_POWER_LOW_POWER_STATE=y
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_TICKLESS_IDLE=y
CONFIG_UART_1=n
CONFIG_UART_CONSOLE_ON_DEV_NAME="UART_0"
CONFIG_IPM=y
CONFIG_IPM_QUARK_SE=y

View file

@ -0,0 +1,3 @@
ccflags-y += -I$(ZEPHYR_BASE)/drivers
obj-y = main.o

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* 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 <string.h>
#include <zephyr.h>
#include <kernel.h>
#include <power.h>
#include <device.h>
#include <misc/printk.h>
#include <power.h>
#include <soc_power.h>
#include <ipm.h>
#include <ipm/ipm_quark_se.h>
#define TASK_TIME_IN_SEC 10
#define MAX_SUSPEND_DEVICE_COUNT 15
static struct device *suspended_devices[MAX_SUSPEND_DEVICE_COUNT];
static int suspend_device_count;
static struct k_fifo fifo;
static int post_ops_done;
QUARK_SE_IPM_DEFINE(alarm_notification, 0, QUARK_SE_IPM_INBOUND);
static void suspend_devices(void)
{
for (int i = suspend_device_count - 1; i >= 0; i--) {
device_set_power_state(suspended_devices[i],
DEVICE_PM_SUSPEND_STATE);
}
}
static void resume_devices(void)
{
for (int i = 0; i < suspend_device_count; i++) {
device_set_power_state(suspended_devices[i],
DEVICE_PM_ACTIVE_STATE);
}
}
int _sys_soc_suspend(int32_t ticks)
{
post_ops_done = 0;
suspend_devices();
_sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_2);
if (!post_ops_done) {
post_ops_done = 1;
resume_devices();
_sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2);
}
return SYS_PM_DEEP_SLEEP;
}
void _sys_soc_resume(void)
{
if (!post_ops_done) {
post_ops_done = 1;
_sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2);
resume_devices();
}
}
static void build_suspend_device_list(void)
{
int i, devcount;
struct device *devices;
device_list_get(&devices, &devcount);
if (devcount > MAX_SUSPEND_DEVICE_COUNT) {
printk("Error: List of devices exceeds what we can track "
"for suspend. Built: %d, Max: %d\n",
devcount, MAX_SUSPEND_DEVICE_COUNT);
return;
}
suspend_device_count = 2;
for (i = 0; i < devcount; i++) {
if (!strcmp(devices[i].config->name, "arc_v2_irq_unit")) {
suspended_devices[0] = &devices[i];
} else if (!strcmp(devices[i].config->name, "sys_clock")) {
suspended_devices[1] = &devices[i];
} else {
suspended_devices[suspend_device_count++] = &devices[i];
}
}
}
void alarm_notification_handler(void *context, uint32_t id,
volatile void *data)
{
/* Unblock ARC application thread. */
k_fifo_put(&fifo, NULL);
}
void main(void)
{
struct device *ipm;
printk("ARC: Quark SE PM Multicore Demo\n");
build_suspend_device_list();
k_fifo_init(&fifo);
ipm = device_get_binding("alarm_notification");
ipm_register_callback(ipm, alarm_notification_handler, NULL);
ipm_set_enabled(ipm, 1);
while (1) {
/* Simulate some task handling by busy waiting. */
printk("ARC: busy\n");
k_busy_wait(TASK_TIME_IN_SEC * 1000 * 1000);
printk("ARC: idle\n");
k_fifo_get(&fifo, K_FOREVER);
}
}

View file

@ -0,0 +1,4 @@
BOARD = quark_se_c1000_devboard
CONF_FILE = prj.conf
include ${ZEPHYR_BASE}/Makefile.inc

View file

@ -0,0 +1,12 @@
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_DEEP_SLEEP=y
CONFIG_SYS_POWER_LOW_POWER_STATE=y
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_TICKLESS_IDLE=y
CONFIG_ARC_INIT=y
CONFIG_UART_0=n
CONFIG_RTC=y
CONFIG_IPM=y
CONFIG_IPM_QUARK_SE=y
CONFIG_IPM_QUARK_SE_MASTER=y

View file

@ -0,0 +1,9 @@
TEST_CASE ?= sleep-success
ifeq ($(TEST_CASE), sleep-success)
ccflags-y += -DTEST_CASE_SLEEP_SUCCESS
endif
ccflags-y +=-I$(ZEPHYR_BASE)/drivers
obj-y = main.o

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* 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 <string.h>
#include <zephyr.h>
#include <kernel.h>
#include <power.h>
#include <misc/printk.h>
#include <power.h>
#include <soc_power.h>
#include <rtc.h>
#include <ipm.h>
#include <ipm/ipm_quark_se.h>
#ifdef TEST_CASE_SLEEP_SUCCESS
#define TASK_TIME_IN_SEC 15
#define IDLE_TIME_IN_SEC 5
#else
#define TASK_TIME_IN_SEC 5
#define IDLE_TIME_IN_SEC 15
#endif /* TEST_CASE_SLEEP_SUCCESS */
#define MAX_SUSPEND_DEVICE_COUNT 15
static struct device *suspended_devices[MAX_SUSPEND_DEVICE_COUNT];
static int suspend_device_count;
static struct k_fifo fifo;
static struct device *ipm;
QUARK_SE_IPM_DEFINE(alarm_notification, 0, QUARK_SE_IPM_OUTBOUND);
static void suspend_devices(void)
{
for (int i = suspend_device_count - 1; i >= 0; i--) {
device_set_power_state(suspended_devices[i],
DEVICE_PM_SUSPEND_STATE);
}
}
static void resume_devices(void)
{
for (int i = 0; i < suspend_device_count; i++) {
device_set_power_state(suspended_devices[i],
DEVICE_PM_ACTIVE_STATE);
}
}
int _sys_soc_suspend(int32_t ticks)
{
printk("LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2"
" state\n");
if (!_sys_soc_power_state_is_arc_ready()) {
printk("LMT: Failed. ARC is busy.\n");
return SYS_PM_NOT_HANDLED;
}
suspend_devices();
_sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_2);
resume_devices();
printk("LMT: Succeed.\n");
_sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2);
return SYS_PM_DEEP_SLEEP;
}
static void build_suspend_device_list(void)
{
int i, devcount;
struct device *devices;
device_list_get(&devices, &devcount);
if (devcount > MAX_SUSPEND_DEVICE_COUNT) {
printk("Error: List of devices exceeds what we can track "
"for suspend. Built: %d, Max: %d\n",
devcount, MAX_SUSPEND_DEVICE_COUNT);
return;
}
suspend_device_count = 3;
for (i = 0; i < devcount; i++) {
if (!strcmp(devices[i].config->name, "loapic")) {
suspended_devices[0] = &devices[i];
} else if (!strcmp(devices[i].config->name, "ioapic")) {
suspended_devices[1] = &devices[i];
} else if (!strcmp(devices[i].config->name,
CONFIG_UART_CONSOLE_ON_DEV_NAME)) {
suspended_devices[2] = &devices[i];
} else {
suspended_devices[suspend_device_count++] = &devices[i];
}
}
}
static void alarm_handler(struct device *dev)
{
/* Unblock LMT application thread. */
k_fifo_put(&fifo, NULL);
/* Send a dummy message to ARC so the ARC application
* thread can be unblocked.
*/
ipm_send(ipm, 0, 0, NULL, 0);
}
void main(void)
{
struct device *rtc_dev;
struct rtc_config config;
uint32_t now;
printk("LMT: Quark SE PM Multicore Demo\n");
k_fifo_init(&fifo);
build_suspend_device_list();
ipm = device_get_binding("alarm_notification");
if (!ipm) {
printk("Error: Failed to get IPM device\n");
return;
}
rtc_dev = device_get_binding("RTC_0");
if (!rtc_dev) {
printk("Error: Failed to get RTC device\n");
return;
}
rtc_enable(rtc_dev);
config.init_val = 0;
config.alarm_enable = 0;
config.alarm_val = 0;
config.cb_fn = alarm_handler;
rtc_set_config(rtc_dev, &config);
while (1) {
/* Simulate some task handling by busy waiting. */
printk("LMT: busy\n");
k_busy_wait(TASK_TIME_IN_SEC * 1000 * 1000);
now = rtc_read(rtc_dev);
rtc_set_alarm(rtc_dev,
now + (RTC_ALARM_SECOND * IDLE_TIME_IN_SEC));
printk("LMT: idle\n");
k_fifo_get(&fifo, K_FOREVER);
}
}