summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAndre Guedes <andre.guedes@intel.com>2016-12-21 13:26:17 -0800
committerAnas Nashif <nashif@linux.intel.com>2017-01-24 13:36:55 +0000
commit6f141a5576ef70e3623ee0dc03a28bb83d89ceba (patch)
treec026ad0141a2e124ea2abdc10168e266c993637c /arch
parentcd35f06de8e1a7df5bb51a929da046b5151a7fa9 (diff)
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/arc/include/swap_macros.h4
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/Kbuild1
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/Makefile1
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/power.c23
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/soc.c4
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/soc.h6
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/soc_power.S46
-rw-r--r--arch/arc/soc/quark_se_c1000_ss/soc_power.h15
-rw-r--r--arch/x86/soc/intel_quark/quark_se/power.c13
-rw-r--r--arch/x86/soc/intel_quark/quark_se/soc.c5
-rw-r--r--arch/x86/soc/intel_quark/quark_se/soc.h4
-rw-r--r--arch/x86/soc/intel_quark/quark_se/soc_power.h22
12 files changed, 139 insertions, 5 deletions
diff --git a/arch/arc/include/swap_macros.h b/arch/arc/include/swap_macros.h
index f3e9f7b38..c8859d5ed 100644
--- a/arch/arc/include/swap_macros.h
+++ b/arch/arc/include/swap_macros.h
@@ -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.
diff --git a/arch/arc/soc/quark_se_c1000_ss/Kbuild b/arch/arc/soc/quark_se_c1000_ss/Kbuild
index 5ae78ce56..0945b739f 100644
--- a/arch/arc/soc/quark_se_c1000_ss/Kbuild
+++ b/arch/arc/soc/quark_se_c1000_ss/Kbuild
@@ -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
diff --git a/arch/arc/soc/quark_se_c1000_ss/Makefile b/arch/arc/soc/quark_se_c1000_ss/Makefile
index 09a05bc30..77df18711 100644
--- a/arch/arc/soc/quark_se_c1000_ss/Makefile
+++ b/arch/arc/soc/quark_se_c1000_ss/Makefile
@@ -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
diff --git a/arch/arc/soc/quark_se_c1000_ss/power.c b/arch/arc/soc/quark_se_c1000_ss/power.c
index 2f4df3def..3d70383bc 100644
--- a/arch/arc/soc/quark_se_c1000_ss/power.c
+++ b/arch/arc/soc/quark_se_c1000_ss/power.c
@@ -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;
}
diff --git a/arch/arc/soc/quark_se_c1000_ss/soc.c b/arch/arc/soc/quark_se_c1000_ss/soc.c
index 3986556f2..68a3df3de 100644
--- a/arch/arc/soc/quark_se_c1000_ss/soc.c
+++ b/arch/arc/soc/quark_se_c1000_ss/soc.c
@@ -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;
}
diff --git a/arch/arc/soc/quark_se_c1000_ss/soc.h b/arch/arc/soc/quark_se_c1000_ss/soc.h
index 3c35970bf..e6247f14b 100644
--- a/arch/arc/soc/quark_se_c1000_ss/soc.h
+++ b/arch/arc/soc/quark_se_c1000_ss/soc.h
@@ -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_ */
diff --git a/arch/arc/soc/quark_se_c1000_ss/soc_power.S b/arch/arc/soc/quark_se_c1000_ss/soc_power.S
index 0b9e33563..88720bf66 100644
--- a/arch/arc/soc/quark_se_c1000_ss/soc_power.S
+++ b/arch/arc/soc/quark_se_c1000_ss/soc_power.S
@@ -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]
diff --git a/arch/arc/soc/quark_se_c1000_ss/soc_power.h b/arch/arc/soc/quark_se_c1000_ss/soc_power.h
index 5c8e763cf..d7bc5440c 100644
--- a/arch/arc/soc/quark_se_c1000_ss/soc_power.h
+++ b/arch/arc/soc/quark_se_c1000_ss/soc_power.h
@@ -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):
* -----------------------------------------------------------
*
diff --git a/arch/x86/soc/intel_quark/quark_se/power.c b/arch/x86/soc/intel_quark/quark_se/power.c
index b158f0269..db328e67d 100644
--- a/arch/x86/soc/intel_quark/quark_se/power.c
+++ b/arch/x86/soc/intel_quark/quark_se/power.c
@@ -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;
+}
diff --git a/arch/x86/soc/intel_quark/quark_se/soc.c b/arch/x86/soc/intel_quark/quark_se/soc.c
index 15bf13192..557aef8ba 100644
--- a/arch/x86/soc/intel_quark/quark_se/soc.c
+++ b/arch/x86/soc/intel_quark/quark_se/soc.c
@@ -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*/
diff --git a/arch/x86/soc/intel_quark/quark_se/soc.h b/arch/x86/soc/intel_quark/quark_se/soc.h
index e3931dfbf..eb5305990 100644
--- a/arch/x86/soc/intel_quark/quark_se/soc.h
+++ b/arch/x86/soc/intel_quark/quark_se/soc.h
@@ -64,4 +64,8 @@
#endif /* _ASMLANGUAGE */
+#ifdef CONFIG_ARC_INIT
+int _arc_init(struct device *arg);
+#endif /* CONFIG_ARC_INIT */
+
#endif /* __SOC_H_ */
diff --git a/arch/x86/soc/intel_quark/quark_se/soc_power.h b/arch/x86/soc/intel_quark/quark_se/soc_power.h
index 1562d1ea0..acd0c8bc0 100644
--- a/arch/x86/soc/intel_quark/quark_se/soc_power.h
+++ b/arch/x86/soc/intel_quark/quark_se/soc_power.h
@@ -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