power: Update Quark SE PM layer with QMSI 1.3
QMSI 1.3 natively supports restoring the SoC and peripherals after sleep. The Zephyr Power Management shim layer is updated in order to support QMSI functions. The following functions have been added: void _sys_soc_set_power_state(enum power_state); void _sys_soc_power_state_post_ops(void); In order to fully support deep sleep, the function _sys_soc_set_power_state now support saving and restoring CPU context and returns to the application. _sys_soc_set_power_state function also abstracts QMSI cpu states and enable the application to choose between C1/C2 or C2LP states. The QMSI power states are mapped as follows: SYS_SOC_POWER_STATE_CPU_LPS -> power_cpu_c2lp SYS_SOC_POWER_STATE_CPU_LPS_1 -> power_cpu_c2 SYS_SOC_POWER_STATE_CPU_LPS_2 -> power_cpu_c1 SYS_SOC_POWER_STATE_DEEP_SLEEP -> power_soc_deep_sleep SYS_SOC_POWER_STATE_DEEP_SLEEP_1 -> power_soc_sleep The following functions have been removed: void _sys_soc_set_power_policy(uint32_t pm_policy); int _sys_soc_get_power_policy(void); FUNC_NORETURN void _sys_soc_put_deep_sleep(void); void _sys_soc_put_low_power_state(void); void _sys_soc_deep_sleep_post_ops(void); Those changes are propagated to the samples. All calls to QMSI are removed. Jira: ZEP-1045, ZEP-993, ZEP-1047 Change-Id: I26822727985b63be0a310cc3590a3e71b8e72c8c Signed-off-by: Julien Delayen <julien.delayen@intel.com> Signed-off-by: Ramesh Thomas <ramesh.thomas@intel.com>
This commit is contained in:
parent
5e87553079
commit
fec01af64a
|
@ -20,51 +20,78 @@
|
|||
#include <power.h>
|
||||
#include <soc_power.h>
|
||||
|
||||
#define _DEEP_SLEEP_MODE 0xDEEBDEEB
|
||||
#define _LOW_POWER_MODE 0xD02ED02E
|
||||
|
||||
/* GPS1 is reserved for PM use */
|
||||
#define _GPS1 0xb0800104
|
||||
#include "power_states.h"
|
||||
|
||||
/* Variables used to save CPU state */
|
||||
uint64_t _pm_save_gdtr;
|
||||
uint64_t _pm_save_idtr;
|
||||
uint32_t _pm_save_esp;
|
||||
|
||||
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE) || \
|
||||
defined(CONFIG_SYS_POWER_DEEP_SLEEP) || \
|
||||
defined(CONFIG_DEVICE_POWER_MANAGEMENT))
|
||||
void _sys_soc_set_power_policy(uint32_t pm_policy)
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||
static uint32_t *__x86_restore_info = (uint32_t *)CONFIG_BSP_SHARED_RAM_ADDR;
|
||||
|
||||
static void _deep_sleep(enum power_states state)
|
||||
{
|
||||
switch (pm_policy) {
|
||||
case SYS_PM_DEEP_SLEEP:
|
||||
sys_write32(_DEEP_SLEEP_MODE, _GPS1);
|
||||
break;
|
||||
case SYS_PM_LOW_POWER_STATE:
|
||||
sys_write32(_LOW_POWER_MODE, _GPS1);
|
||||
break;
|
||||
case SYS_PM_ACTIVE_STATE:
|
||||
sys_write32(0, _GPS1);
|
||||
break;
|
||||
default:
|
||||
__ASSERT(0, "Unknown PM policy");
|
||||
break;
|
||||
int restore;
|
||||
|
||||
__asm__ volatile ("wbinvd");
|
||||
|
||||
/*
|
||||
* Setting resume vector inside the restore_cpu_context
|
||||
* function since we have nothing to do before cpu context
|
||||
* is restored. If necessary, it is possible to set the
|
||||
* resume vector to a location where additional processing
|
||||
* can be done before cpu context is restored and control
|
||||
* transferred to _sys_soc_suspend.
|
||||
*/
|
||||
qm_x86_set_resume_vector(_sys_soc_restore_cpu_context,
|
||||
*__x86_restore_info);
|
||||
|
||||
restore = _sys_soc_save_cpu_context();
|
||||
|
||||
if (!restore) {
|
||||
power_soc_set_x86_restore_flag();
|
||||
|
||||
switch (state) {
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
power_soc_sleep();
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
power_soc_deep_sleep();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _sys_soc_get_power_policy(void)
|
||||
{
|
||||
uint32_t mode;
|
||||
|
||||
mode = sys_read32(_GPS1);
|
||||
|
||||
switch (mode) {
|
||||
case _DEEP_SLEEP_MODE:
|
||||
return SYS_PM_DEEP_SLEEP;
|
||||
case _LOW_POWER_MODE:
|
||||
return SYS_PM_LOW_POWER_STATE;
|
||||
}
|
||||
|
||||
return SYS_PM_ACTIVE_STATE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void _sys_soc_set_power_state(enum power_states state)
|
||||
{
|
||||
switch (state) {
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
power_cpu_c2lp();
|
||||
break;
|
||||
case SYS_POWER_STATE_CPU_LPS_1:
|
||||
power_cpu_c2();
|
||||
break;
|
||||
case SYS_POWER_STATE_CPU_LPS_2:
|
||||
power_cpu_c1();
|
||||
break;
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
_deep_sleep(state);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _sys_soc_power_state_post_ops(enum power_states state)
|
||||
{
|
||||
if (state == SYS_POWER_STATE_CPU_LPS_2) {
|
||||
return;
|
||||
}
|
||||
|
||||
__asm__ volatile("sti");
|
||||
}
|
||||
|
|
|
@ -18,19 +18,6 @@
|
|||
|
||||
#include <arch/x86/asm.h>
|
||||
|
||||
#define _P_LVL2 0xb0800504
|
||||
#define _PM1C 0xb0800518
|
||||
#define _SLPEN 13
|
||||
|
||||
#ifdef CONFIG_SYS_POWER_LOW_POWER_STATE
|
||||
GTEXT(_sys_soc_put_low_power_state)
|
||||
|
||||
SECTION_FUNC(TEXT, _sys_soc_put_low_power_state)
|
||||
sti
|
||||
movl _P_LVL2, %eax /* reading P_LVL2 causes C2 */
|
||||
ret
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_POWER_DEEP_SLEEP
|
||||
GDATA(_pm_save_gdtr)
|
||||
GDATA(_pm_save_idtr)
|
||||
|
@ -38,8 +25,6 @@ GDATA(_pm_save_esp)
|
|||
|
||||
GTEXT(_sys_soc_save_cpu_context)
|
||||
GTEXT(_sys_soc_restore_cpu_context)
|
||||
GTEXT(_sys_soc_put_deep_sleep)
|
||||
GTEXT(_sys_soc_deep_sleep_post_ops)
|
||||
|
||||
SECTION_FUNC(TEXT, _sys_soc_save_cpu_context)
|
||||
movl %esp, %eax /* save ptr to return address */
|
||||
|
@ -79,27 +64,4 @@ SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
|
|||
xorl %eax, %eax
|
||||
incl %eax
|
||||
ret
|
||||
|
||||
SECTION_FUNC(TEXT, _sys_soc_put_deep_sleep)
|
||||
|
||||
wbinvd /* invalidate cache */
|
||||
|
||||
movl $_SLPEN, %eax /* setting SLPEN bit in PM1C */
|
||||
lock bts %eax, _PM1C /* triggers deep sleep */
|
||||
|
||||
ret /* code unreachable */
|
||||
|
||||
SECTION_FUNC(TEXT, _sys_soc_deep_sleep_post_ops)
|
||||
/*
|
||||
* At this point, it is resuming after wake up
|
||||
* from deep sleep.
|
||||
*
|
||||
* Interrupts are not enabled yet and needs to
|
||||
* be re-enabled here. This need to be done
|
||||
* only in deep sleep case.
|
||||
*
|
||||
* In future this will be moved to the kernel.
|
||||
*/
|
||||
sti
|
||||
ret
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,15 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
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_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Save CPU context
|
||||
*
|
||||
|
@ -51,51 +60,39 @@ int _sys_soc_save_cpu_context(void);
|
|||
FUNC_NORETURN void _sys_soc_restore_cpu_context(void);
|
||||
|
||||
/**
|
||||
* @brief Put SoC into deep sleep power state
|
||||
* @brief Put processor into low power state
|
||||
*
|
||||
* This function implements the SoC specific details necessary
|
||||
* to put the SoC into deep sleep state.
|
||||
* to put the processor into available power states.
|
||||
*
|
||||
* This function will not return;
|
||||
* Wake up considerations:
|
||||
* SYS_POWER_STATE_CPU_LPS_2: Any interrupt works as wake event.
|
||||
*
|
||||
* SYS_POWER_STATE_CPU_LPS_1: Any interrupt works as wake event except
|
||||
* if the core enters LPSS where SYS_POWER_STATE_DEEP_SLEEP wake events
|
||||
* applies.
|
||||
*
|
||||
* SYS_POWER_STATE_CPU_LPS: Any interrupt works as wake event except the
|
||||
* PIC timer which is gated. If the core enters LPSS only
|
||||
* SYS_POWER_STATE_DEEP_SLEEP wake events applies.
|
||||
*
|
||||
* SYS_POWER_STATE_DEEP_SLEEP: 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_1: Only Always-On peripherals can wake up
|
||||
* the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator.
|
||||
*/
|
||||
FUNC_NORETURN void _sys_soc_put_deep_sleep(void);
|
||||
void _sys_soc_set_power_state(enum power_states state);
|
||||
|
||||
/**
|
||||
* @brief Do any SoC or architecture specific post ops after deep sleep.
|
||||
* @brief Do any SoC or architecture specific post ops after low power states.
|
||||
*
|
||||
* This function is a place holder to do any operations that may
|
||||
* be needed to be done after deep sleep exits. Currently it enables
|
||||
* interrupts after resuming from deep sleep. In future, the enabling
|
||||
* of interrupts may be moved into the kernel.
|
||||
*/
|
||||
void _sys_soc_deep_sleep_post_ops(void);
|
||||
|
||||
/**
|
||||
* @brief Put processor into low power state
|
||||
*
|
||||
* This function implements the SoC specific details necessary
|
||||
* to put the processor into deep sleep state.
|
||||
*/
|
||||
void _sys_soc_put_low_power_state(void);
|
||||
|
||||
/**
|
||||
* @brief Save the current power policy
|
||||
*
|
||||
* This function implements the SoC specific details necessary
|
||||
* to save the current power policy. This should save the information
|
||||
* in a location that would be persistent across deep sleep if deep
|
||||
* sleep is one of the power policies supported.
|
||||
*/
|
||||
void _sys_soc_set_power_policy(uint32_t pm_policy);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the saved current power policy
|
||||
*
|
||||
* This function implements the SoC specific details necessary
|
||||
* to retrieve the power policy information saved by
|
||||
* _sys_soc_set_power_policy().
|
||||
*/
|
||||
int _sys_soc_get_power_policy(void);
|
||||
void _sys_soc_power_state_post_ops(enum power_states state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ static inline void _sys_soc_disable_wake_event_notification(void)
|
|||
* @note A dedicated function may be created in future to notify wake
|
||||
* events, instead of overloading this one.
|
||||
*/
|
||||
extern void _sys_soc_resume(void);
|
||||
void _sys_soc_resume(void);
|
||||
|
||||
/**
|
||||
* @brief Hook function to allow entry to low power state
|
||||
|
|
|
@ -239,6 +239,11 @@ unsigned char _sys_power_save_flag = 1;
|
|||
|
||||
#include <nanokernel.h>
|
||||
#include <microkernel/base_api.h>
|
||||
|
||||
void __attribute__((weak)) _sys_soc_resume(void)
|
||||
{
|
||||
}
|
||||
|
||||
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE) || \
|
||||
defined(CONFIG_SYS_POWER_DEEP_SLEEP) || \
|
||||
defined(CONFIG_DEVICE_POWER_MANAGEMENT))
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
#define GPIO_IN_PIN 16
|
||||
|
||||
static void create_device_list(void);
|
||||
static void suspend_devices(int pm_policy);
|
||||
static void resume_devices(int pm_policy);
|
||||
static void suspend_devices(void);
|
||||
static void resume_devices(void);
|
||||
|
||||
static struct device *device_list;
|
||||
static int device_count;
|
||||
|
@ -39,7 +39,7 @@ static int device_count;
|
|||
* device power policies would be executed.
|
||||
*/
|
||||
#define DEVICE_POLICY_MAX 15
|
||||
static char device_policy_list[DEVICE_POLICY_MAX];
|
||||
static char device_ordered_list[DEVICE_POLICY_MAX];
|
||||
static char device_retval[DEVICE_POLICY_MAX];
|
||||
|
||||
static struct device *gpio_dev;
|
||||
|
@ -51,14 +51,14 @@ static uint32_t start_time, end_time;
|
|||
static void setup_rtc(void);
|
||||
static void enable_wake_event(void);
|
||||
|
||||
static int test_started;
|
||||
int pm_state;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
printk("Power Management Demo\n");
|
||||
|
||||
if (setup_gpio()) {
|
||||
printk("Test aborted\n");
|
||||
printk("Demo aborted\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -75,15 +75,14 @@ void main(void)
|
|||
* 3) Disconnect from 3.3V. Test should start now.
|
||||
*
|
||||
*/
|
||||
printk("Toggle gpio pin 16 to start test\n");
|
||||
printk("Toggle gpio pin 16 to start demo\n");
|
||||
if (wait_gpio_low()) {
|
||||
printk("Test aborted\n");
|
||||
printk("Demo aborted\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("PM test started\n");
|
||||
printk("PM demo started\n");
|
||||
setup_rtc();
|
||||
test_started = 1;
|
||||
|
||||
create_device_list();
|
||||
|
||||
|
@ -95,12 +94,15 @@ void main(void)
|
|||
static int check_pm_policy(int32_t ticks)
|
||||
{
|
||||
static int policy;
|
||||
int power_states[] = {SYS_POWER_STATE_MAX, SYS_POWER_STATE_CPU_LPS,
|
||||
SYS_POWER_STATE_DEEP_SLEEP};
|
||||
|
||||
/*
|
||||
* Compare time available with wake latencies and select
|
||||
* appropriate power saving policy
|
||||
*
|
||||
* For the demo we will alternate between following states
|
||||
l
|
||||
* For the demo we will alternate between following policies
|
||||
*
|
||||
* 0 = no power saving operation
|
||||
* 1 = low power state
|
||||
|
@ -108,68 +110,80 @@ static int check_pm_policy(int32_t ticks)
|
|||
*
|
||||
*/
|
||||
|
||||
/* Set the max val to 1 if deep sleep is not supported */
|
||||
policy = (policy > 2 ? 0 : policy);
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||
policy = (++policy > 2 ? 0 : policy);
|
||||
/*
|
||||
* If deep sleep was selected, we need to check if any device is in
|
||||
* the middle of a transaction
|
||||
*
|
||||
* Use device_busy_check() to check specific devices
|
||||
*/
|
||||
if ((policy == 2) && device_any_busy_check()) {
|
||||
/* Devices are busy - do CPU LPS instead */
|
||||
policy = 1;
|
||||
}
|
||||
#else
|
||||
policy = (++policy > 1 ? 0 : policy);
|
||||
#endif
|
||||
|
||||
return policy++;
|
||||
return power_states[policy];
|
||||
}
|
||||
|
||||
static void low_power_state_exit(void)
|
||||
{
|
||||
resume_devices(SYS_PM_LOW_POWER_STATE);
|
||||
resume_devices();
|
||||
|
||||
end_time = rtc_read(rtc_dev);
|
||||
printk("\nLow power state policy exit!\n");
|
||||
printk("\nLow power state exit!\n");
|
||||
printk("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
|
||||
end_time - start_time);
|
||||
}
|
||||
|
||||
static void deep_sleep_exit(void)
|
||||
{
|
||||
resume_devices(SYS_PM_DEEP_SLEEP);
|
||||
resume_devices();
|
||||
|
||||
printk("Wake from Deep Sleep!\n");
|
||||
|
||||
end_time = rtc_read(rtc_dev);
|
||||
printk("\nDeep sleep policy exit!\n");
|
||||
printk("\nDeep sleep exit!\n");
|
||||
printk("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
|
||||
end_time - start_time);
|
||||
}
|
||||
|
||||
static int low_power_state_entry(int32_t ticks)
|
||||
{
|
||||
printk("\n\nLow power state policy entry!\n");
|
||||
printk("\n\nLow power state entry!\n");
|
||||
|
||||
start_time = rtc_read(rtc_dev);
|
||||
|
||||
/* Turn off peripherals/clocks here */
|
||||
suspend_devices(SYS_PM_LOW_POWER_STATE);
|
||||
suspend_devices();
|
||||
|
||||
_sys_soc_set_power_policy(SYS_PM_LOW_POWER_STATE);
|
||||
enable_wake_event();
|
||||
|
||||
_sys_soc_put_low_power_state();
|
||||
_sys_soc_set_power_state(SYS_POWER_STATE_CPU_LPS);
|
||||
|
||||
low_power_state_exit();
|
||||
|
||||
return SYS_PM_LOW_POWER_STATE;
|
||||
}
|
||||
|
||||
static int deep_sleep_entry(int32_t ticks)
|
||||
{
|
||||
printk("\n\nDeep sleep policy entry!\n");
|
||||
printk("\n\nDeep sleep entry!\n");
|
||||
|
||||
start_time = rtc_read(rtc_dev);
|
||||
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
/* Turn off peripherals/clocks here */
|
||||
suspend_devices(SYS_PM_DEEP_SLEEP);
|
||||
suspend_devices();
|
||||
|
||||
_sys_soc_set_power_policy(SYS_PM_DEEP_SLEEP);
|
||||
enable_wake_event();
|
||||
|
||||
/*
|
||||
* Returns 0 when context is saved.
|
||||
* Returns 1 when context was restored and control was
|
||||
* transferred to it during DS resume.
|
||||
*/
|
||||
if (!_sys_soc_save_cpu_context()) {
|
||||
_sys_soc_put_deep_sleep();
|
||||
}
|
||||
_sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP);
|
||||
|
||||
/*
|
||||
* At this point system has woken up from
|
||||
|
@ -178,47 +192,23 @@ static int deep_sleep_entry(int32_t ticks)
|
|||
|
||||
deep_sleep_exit();
|
||||
|
||||
/* Clear current power policy */
|
||||
_sys_soc_set_power_policy(SYS_PM_ACTIVE_STATE);
|
||||
|
||||
return SYS_PM_DEEP_SLEEP;
|
||||
}
|
||||
|
||||
int _sys_soc_suspend(int32_t ticks)
|
||||
{
|
||||
int pm_state;
|
||||
int ret = SYS_PM_NOT_HANDLED;
|
||||
|
||||
pm_state = check_pm_policy(ticks);
|
||||
|
||||
switch (pm_state) {
|
||||
case 1: /* CPU LPS */
|
||||
start_time = rtc_read(rtc_dev);
|
||||
enable_wake_event();
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
/* Do CPU LPS operations */
|
||||
ret = low_power_state_entry(ticks);
|
||||
break;
|
||||
case 2: /* Deep Sleep */
|
||||
/*
|
||||
* if the policy manager chooses to go to deep sleep, we need to
|
||||
* check if any device is in the middle of a transaction
|
||||
*/
|
||||
if (!device_any_busy_check()) {
|
||||
/* Do deep sleep operations */
|
||||
start_time = rtc_read(rtc_dev);
|
||||
enable_wake_event();
|
||||
ret = deep_sleep_entry(ticks);
|
||||
if (ret == SYS_PM_DEEP_SLEEP) {
|
||||
/*
|
||||
* Do any arch or soc specific post
|
||||
* operations specific to deep sleep.
|
||||
*
|
||||
* This would enable interrupts so
|
||||
* it should be done right before
|
||||
* function return
|
||||
*/
|
||||
_sys_soc_deep_sleep_post_ops();
|
||||
}
|
||||
}
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
/* Do deep sleep operations */
|
||||
ret = deep_sleep_entry(ticks);
|
||||
break;
|
||||
default:
|
||||
/* No PM operations */
|
||||
|
@ -226,49 +216,50 @@ int _sys_soc_suspend(int32_t ticks)
|
|||
break;
|
||||
}
|
||||
|
||||
if (ret != SYS_PM_NOT_HANDLED) {
|
||||
/*
|
||||
* Do any arch or soc specific post operations specific to the
|
||||
* power state.
|
||||
*
|
||||
* If this enables interrupts, then it should be done
|
||||
* right before function return.
|
||||
*/
|
||||
_sys_soc_power_state_post_ops(pm_state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _sys_soc_resume(void)
|
||||
{
|
||||
uint32_t pm_policy;
|
||||
/*
|
||||
* Nothing to do in this example
|
||||
*
|
||||
* low_power_state_exit() was called inside low_power_state_entry
|
||||
* after exiting the CPU LPS states.
|
||||
|
||||
pm_policy = _sys_soc_get_power_policy();
|
||||
|
||||
/* Clear current power policy */
|
||||
_sys_soc_set_power_policy(SYS_PM_ACTIVE_STATE);
|
||||
|
||||
switch (pm_policy) {
|
||||
case SYS_PM_DEEP_SLEEP:
|
||||
/*
|
||||
* This should transfer control to the point
|
||||
* where CPU context was saved. Context was
|
||||
* saved in _sys_power_save_cpu_context(), which
|
||||
* was called in deep_sleep_entry();
|
||||
*
|
||||
* deep_sleep_exit() will be called at the point
|
||||
* of resume inside deep_sleep_entry().
|
||||
*/
|
||||
_sys_soc_restore_cpu_context();
|
||||
break;
|
||||
case SYS_PM_LOW_POWER_STATE:
|
||||
low_power_state_exit();
|
||||
break;
|
||||
default:
|
||||
/* cold boot */
|
||||
break;
|
||||
}
|
||||
* Some CPU LPS power states require enabling of interrupts
|
||||
* atomically when entering those states. The wake up from
|
||||
* such a state first executes code in the ISR of the interrupt
|
||||
* that caused the wake. This hook will be called from the ISR.
|
||||
* For such CPU LPS states, call low_power_state_exit() from here
|
||||
* so that restores are completed before scheduler swithes to other
|
||||
* tasks.
|
||||
*
|
||||
* Call _sys_soc_disable_wake_event_notification() if this
|
||||
* notification is not required.
|
||||
*/
|
||||
}
|
||||
|
||||
static void suspend_devices(int pm_policy)
|
||||
static void suspend_devices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = device_count - 1; i >= 0; i--) {
|
||||
int idx = device_policy_list[i];
|
||||
int idx = device_ordered_list[i];
|
||||
|
||||
/* If necessary the policy manager can check if a specific
|
||||
* device in the policy list is busy as shown below :
|
||||
* device in the device list is busy as shown below :
|
||||
* if(device_busy_check(&device_list[idx])) {do something}
|
||||
*/
|
||||
device_retval[i] = device_set_power_state(&device_list[idx],
|
||||
|
@ -276,13 +267,13 @@ static void suspend_devices(int pm_policy)
|
|||
}
|
||||
}
|
||||
|
||||
static void resume_devices(int pm_policy)
|
||||
static void resume_devices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < device_count; i++) {
|
||||
if (!device_retval[i]) {
|
||||
int idx = device_policy_list[i];
|
||||
int idx = device_ordered_list[i];
|
||||
|
||||
device_set_power_state(&device_list[idx],
|
||||
DEVICE_PM_ACTIVE_STATE);
|
||||
|
@ -304,7 +295,7 @@ static void create_device_list(void)
|
|||
* in the beginning of the list will be resumed first.
|
||||
*
|
||||
* Other devices depend on APICs so ioapic and loapic devices
|
||||
* will be placed first in the device policy list. Move any
|
||||
* will be placed first in the device ordered list. Move any
|
||||
* other devices to the beginning as necessary. e.g. uart
|
||||
* is useful to enable early prints.
|
||||
*/
|
||||
|
@ -314,13 +305,13 @@ static void create_device_list(void)
|
|||
|
||||
for (i = 0; (i < count) && (device_count < DEVICE_POLICY_MAX); i++) {
|
||||
if (!strcmp(device_list[i].config->name, "loapic")) {
|
||||
device_policy_list[0] = i;
|
||||
device_ordered_list[0] = i;
|
||||
} else if (!strcmp(device_list[i].config->name, "ioapic")) {
|
||||
device_policy_list[1] = i;
|
||||
device_ordered_list[1] = i;
|
||||
} else if (!strcmp(device_list[i].config->name, "UART_0")) {
|
||||
device_policy_list[2] = i;
|
||||
device_ordered_list[2] = i;
|
||||
} else {
|
||||
device_policy_list[device_count++] = i;
|
||||
device_ordered_list[device_count++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
CONFIG_ARC_INIT=n
|
||||
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_RTC=y
|
|
@ -1 +0,0 @@
|
|||
obj-y = main.o resume.o
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
* 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 <zephyr.h>
|
||||
#include <power.h>
|
||||
#include <misc/printk.h>
|
||||
#include <rtc.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "power_states.h"
|
||||
|
||||
enum power_states {
|
||||
POWER_STATE_CPU_C1,
|
||||
POWER_STATE_CPU_C2,
|
||||
POWER_STATE_CPU_C2LP,
|
||||
POWER_STATE_SOC_SLEEP,
|
||||
POWER_STATE_SOC_DEEP_SLEEP,
|
||||
POWER_STATE_MAX
|
||||
};
|
||||
|
||||
#define TIMEOUT 5 /* in seconds */
|
||||
#define MAX_SUSPEND_DEVICE_COUNT 15
|
||||
|
||||
static struct device *rtc_dev;
|
||||
static struct device *suspend_devices[MAX_SUSPEND_DEVICE_COUNT];
|
||||
static int suspend_device_count;
|
||||
static enum power_states last_state = POWER_STATE_SOC_DEEP_SLEEP;
|
||||
|
||||
static enum power_states get_next_state(void)
|
||||
{
|
||||
last_state = (last_state + 1) % POWER_STATE_MAX;
|
||||
return last_state;
|
||||
}
|
||||
|
||||
static const char *state_to_string(int state)
|
||||
{
|
||||
switch (state) {
|
||||
case POWER_STATE_CPU_C1:
|
||||
return "CPU_C1";
|
||||
case POWER_STATE_CPU_C2:
|
||||
return "CPU_C2";
|
||||
case POWER_STATE_CPU_C2LP:
|
||||
return "CPU_C2LP";
|
||||
case POWER_STATE_SOC_SLEEP:
|
||||
return "SOC_SLEEP";
|
||||
case POWER_STATE_SOC_DEEP_SLEEP:
|
||||
return "SOC_DEEP_SLEEP";
|
||||
default:
|
||||
return "Unknown state";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This helper function handle both 'sleep' and 'deep sleep' states.
|
||||
*
|
||||
* In those states, the core voltage rail is turned off and the execution
|
||||
* context is lost. The WakeUp event will turn on the core voltage rail
|
||||
* and the x86 core will jump the its reset vector.
|
||||
*
|
||||
* In order to be able to continue the program execution from the point
|
||||
* it was before entering in Sleep states, we have to save the execution
|
||||
* context and restore it during system startup.
|
||||
*
|
||||
* The execution context is restored by the 'restore_trap' routine which
|
||||
* is called during system startup by _sys_soc_resume hook.
|
||||
*
|
||||
* In future, this save and restore functionality will be provided by QMSI
|
||||
* and we won't need to do it in Zephyr side.
|
||||
*/
|
||||
static void __do_soc_sleep(int deep)
|
||||
{
|
||||
uint64_t saved_idt = 0;
|
||||
uint64_t saved_gdt = 0;
|
||||
|
||||
/* Save execution context. This routine saves 'idtr', 'gdtr',
|
||||
* EFLAGS and general purpose registers onto the stack, and
|
||||
* current ESP register in GPS1 register.
|
||||
*/
|
||||
__asm__ __volatile__("sidt %[idt]\n\t"
|
||||
"sgdt %[gdt]\n\t"
|
||||
"pushfl\n\t"
|
||||
"pushal\n\t"
|
||||
"movl %%esp, %[gps1]\n\t"
|
||||
: /* Output operands. */
|
||||
[idt] "=m"(saved_idt),
|
||||
[gdt] "=m"(saved_gdt),
|
||||
[gps1] "=m"(QM_SCSS_GP->gps1)
|
||||
: /* Input operands. */
|
||||
: /* Clobbered registers list. */
|
||||
);
|
||||
|
||||
if (deep) {
|
||||
power_soc_deep_sleep();
|
||||
} else {
|
||||
power_soc_sleep();
|
||||
}
|
||||
|
||||
/* Restore trap. This routine is called during system initialization
|
||||
* to restore the execution context from this function.
|
||||
*/
|
||||
__asm__ __volatile__(".globl restore_trap\n\t"
|
||||
"restore_trap:\n\t"
|
||||
" movl %[gps1], %%esp\n\t"
|
||||
" movl $0x00, %[gps1]\n\t"
|
||||
" popal\n\t"
|
||||
" popfl\n\t"
|
||||
" lgdt %[gdt]\n\t"
|
||||
" lidt %[idt]\n\t"
|
||||
: /* Output operands. */
|
||||
: /* Input operands. */
|
||||
[gps1] "m"(QM_SCSS_GP->gps1),
|
||||
[idt] "m"(saved_idt),
|
||||
[gdt] "m"(saved_gdt)
|
||||
: /* Clobbered registers list. */
|
||||
);
|
||||
}
|
||||
|
||||
static void set_rtc_alarm(void)
|
||||
{
|
||||
uint32_t now = rtc_read(rtc_dev);
|
||||
uint32_t alarm = now + (RTC_ALARM_SECOND * TIMEOUT);
|
||||
|
||||
rtc_set_alarm(rtc_dev, alarm);
|
||||
|
||||
/* Wait a few ticks to ensure the 'Counter Match Register' was loaded
|
||||
* with the 'alarm' value.
|
||||
* Refer to the documentation in qm_rtc.h for more details.
|
||||
*/
|
||||
while (rtc_read(rtc_dev) < now + 5)
|
||||
;
|
||||
}
|
||||
|
||||
static void do_soc_sleep(int deep)
|
||||
{
|
||||
int i, devices_retval[suspend_device_count];
|
||||
|
||||
/* Host processor will be turned off so we set up an RTC
|
||||
* interrupt to wake up the SoC.
|
||||
*/
|
||||
set_rtc_alarm();
|
||||
|
||||
for (i = suspend_device_count - 1; i >= 0; i--) {
|
||||
devices_retval[i] = device_set_power_state(suspend_devices[i],
|
||||
DEVICE_PM_SUSPEND_STATE);
|
||||
}
|
||||
|
||||
__do_soc_sleep(deep);
|
||||
|
||||
for (i = 0; i < suspend_device_count; i++) {
|
||||
if (!devices_retval[i]) {
|
||||
device_set_power_state(suspend_devices[i],
|
||||
DEVICE_PM_ACTIVE_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _sys_soc_suspend(int32_t ticks)
|
||||
{
|
||||
int state = get_next_state();
|
||||
int pm_operation = SYS_PM_NOT_HANDLED;
|
||||
|
||||
printk("Entering %s state\n", state_to_string(state));
|
||||
|
||||
switch (state) {
|
||||
case POWER_STATE_CPU_C1:
|
||||
/* QMSI provides the power_cpu_c1() function to enter this
|
||||
* state, but that just means halting the processor.
|
||||
* Since Zephyr will do exactly that when the PMA doesn't
|
||||
* handle suspend in any other way, there's no need to do
|
||||
* so explicitly here.
|
||||
*/
|
||||
break;
|
||||
case POWER_STATE_CPU_C2:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_LOW_POWER_STATE;
|
||||
/* Any interrupt works for taking the core out of plain C2,
|
||||
* but if the ARC core is set to SS2, by going to C2 here
|
||||
* we may enter LPSS mode, and then only a 'wake event' can
|
||||
* bring us back, so set up the RTC to fire to be safe.
|
||||
*/
|
||||
set_rtc_alarm();
|
||||
power_cpu_c2();
|
||||
break;
|
||||
case POWER_STATE_CPU_C2LP:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_LOW_POWER_STATE;
|
||||
/* Local APIC interrupts are not delivered in C2LP state so
|
||||
* we set up the RTC interrupt as 'wake event'.
|
||||
*/
|
||||
set_rtc_alarm();
|
||||
power_cpu_c2lp();
|
||||
break;
|
||||
case POWER_STATE_SOC_SLEEP:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_DEEP_SLEEP;
|
||||
do_soc_sleep(0);
|
||||
break;
|
||||
case POWER_STATE_SOC_DEEP_SLEEP:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_DEEP_SLEEP;
|
||||
do_soc_sleep(1);
|
||||
break;
|
||||
default:
|
||||
printk("State not supported\n");
|
||||
break;
|
||||
}
|
||||
|
||||
last_state = state;
|
||||
|
||||
if (pm_operation != SYS_PM_NOT_HANDLED) {
|
||||
/* We need to re-enable interrupts after we get out of the
|
||||
* C2 states to ensure that they are serviced.
|
||||
* This will eventually be moved into the kernel.
|
||||
*/
|
||||
__asm__ __volatile__ ("sti");
|
||||
}
|
||||
|
||||
printk("Exiting %s state\n", state_to_string(state));
|
||||
|
||||
return pm_operation;
|
||||
}
|
||||
|
||||
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")) {
|
||||
suspend_devices[0] = &devices[i];
|
||||
} else if (!strcmp(devices[i].config->name, "ioapic")) {
|
||||
suspend_devices[1] = &devices[i];
|
||||
} else if (!strcmp(devices[i].config->name,
|
||||
CONFIG_UART_CONSOLE_ON_DEV_NAME)) {
|
||||
suspend_devices[2] = &devices[i];
|
||||
} else {
|
||||
suspend_devices[suspend_device_count++] = &devices[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
struct rtc_config cfg;
|
||||
|
||||
printk("Quark SE: Power Management sample application\n");
|
||||
|
||||
/* Configure RTC device. RTC interrupt is used as 'wake event' when we
|
||||
* are in C2LP state.
|
||||
*/
|
||||
cfg.init_val = 0;
|
||||
cfg.alarm_enable = 0;
|
||||
cfg.alarm_val = 0;
|
||||
cfg.cb_fn = NULL;
|
||||
rtc_dev = device_get_binding(CONFIG_RTC_0_NAME);
|
||||
rtc_enable(rtc_dev);
|
||||
rtc_set_config(rtc_dev, &cfg);
|
||||
|
||||
build_suspend_device_list();
|
||||
|
||||
/* All our application does is putting the task to sleep so the kernel
|
||||
* triggers the suspend operation.
|
||||
*/
|
||||
while (1) {
|
||||
task_sleep(SECONDS(TIMEOUT));
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define _ASMLANGUAGE
|
||||
|
||||
#include <arch/x86/asm.h>
|
||||
|
||||
GTEXT(restore_trap)
|
||||
GTEXT(_sys_soc_resume)
|
||||
|
||||
SECTION_FUNC(TEXT, _sys_soc_resume)
|
||||
|
||||
/* Check GPS1 register. If it is zeroed out it means this
|
||||
* is a regular initialization so we simply return. Otherwise
|
||||
* we jump to the 'restore_trap' which will restore the
|
||||
* execution context we had before entering in Sleep state.
|
||||
*/
|
||||
|
||||
movl $0xb0800104, %eax
|
||||
cmpl $0x00, (%eax)
|
||||
je regular_boot
|
||||
jmp restore_trap
|
||||
|
||||
regular_boot: ret
|
22
tests/power/power_states/prj.conf
Normal file
22
tests/power/power_states/prj.conf
Normal file
|
@ -0,0 +1,22 @@
|
|||
CONFIG_ARC_INIT=n
|
||||
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
|
||||
|
||||
# RTC Wake Event
|
||||
CONFIG_RTC=y
|
||||
|
||||
# COUNTER Wake Event
|
||||
CONFIG_COUNTER=n
|
||||
|
||||
# GPIO 1 Wake Event
|
||||
CONFIG_GPIO=n
|
||||
CONFIG_GPIO_QMSI=n
|
||||
CONFIG_GPIO_QMSI_1=n
|
||||
CONFIG_GPIO_QMSI_1_NAME="GPIO_1"
|
||||
CONFIG_GPIO_QMSI_1_PRI=2
|
||||
|
||||
# Comparator Wake Event
|
||||
CONFIG_AIO_COMPARATOR=n
|
1
tests/power/power_states/src/Makefile
Normal file
1
tests/power/power_states/src/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y = main.o
|
333
tests/power/power_states/src/main.c
Normal file
333
tests/power/power_states/src/main.c
Normal file
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* 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 <zephyr.h>
|
||||
#include <power.h>
|
||||
#include <misc/printk.h>
|
||||
#include <rtc.h>
|
||||
#include <gpio.h>
|
||||
#include <counter.h>
|
||||
#include <aio_comparator.h>
|
||||
|
||||
#include <power.h>
|
||||
#include <soc_power.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TIMEOUT 5 /* in seconds */
|
||||
#define MAX_SUSPEND_DEVICE_COUNT 15
|
||||
|
||||
static struct device *suspend_devices[MAX_SUSPEND_DEVICE_COUNT];
|
||||
static int suspend_device_count;
|
||||
static enum power_states last_state = SYS_POWER_STATE_DEEP_SLEEP_1;
|
||||
|
||||
static enum power_states get_next_state(void)
|
||||
{
|
||||
last_state = (last_state + 1) % SYS_POWER_STATE_MAX;
|
||||
return last_state;
|
||||
}
|
||||
|
||||
static const char *state_to_string(int state)
|
||||
{
|
||||
switch (state) {
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
return "SYS_POWER_STATE_CPU_LPS";
|
||||
case SYS_POWER_STATE_CPU_LPS_1:
|
||||
return "SYS_POWER_STATE_CPU_LPS_1";
|
||||
case SYS_POWER_STATE_CPU_LPS_2:
|
||||
return "SYS_POWER_STATE_CPU_LPS_2";
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
return "SYS_POWER_STATE_DEEP_SLEEP";
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
return "SYS_POWER_STATE_DEEP_SLEEP_1";
|
||||
default:
|
||||
return "Unknown state";
|
||||
}
|
||||
}
|
||||
|
||||
#if (CONFIG_RTC)
|
||||
static struct device *rtc_dev;
|
||||
|
||||
static void setup_rtc(void)
|
||||
{
|
||||
struct rtc_config cfg;
|
||||
|
||||
/* Configure RTC device. RTC interrupt is used as 'wake event' when we
|
||||
* are in C2LP state.
|
||||
*/
|
||||
cfg.init_val = 0;
|
||||
cfg.alarm_enable = 0;
|
||||
cfg.alarm_val = 0;
|
||||
cfg.cb_fn = NULL;
|
||||
rtc_dev = device_get_binding(CONFIG_RTC_0_NAME);
|
||||
rtc_enable(rtc_dev);
|
||||
rtc_set_config(rtc_dev, &cfg);
|
||||
}
|
||||
|
||||
static void set_rtc_alarm(void)
|
||||
{
|
||||
uint32_t now = rtc_read(rtc_dev);
|
||||
uint32_t alarm = now + (RTC_ALARM_SECOND * TIMEOUT);
|
||||
|
||||
rtc_set_alarm(rtc_dev, alarm);
|
||||
|
||||
/* Wait a few ticks to ensure the 'Counter Match Register' was loaded
|
||||
* with the 'alarm' value.
|
||||
* Refer to the documentation in qm_rtc.h for more details.
|
||||
*/
|
||||
while (rtc_read(rtc_dev) < now + 5)
|
||||
;
|
||||
}
|
||||
#elif (CONFIG_COUNTER)
|
||||
static struct device *counter_dev;
|
||||
|
||||
static void setup_counter(void)
|
||||
{
|
||||
volatile uint32_t delay = 0;
|
||||
|
||||
counter_dev = device_get_binding("AON_TIMER");
|
||||
|
||||
if (!counter_dev) {
|
||||
printk("Timer device not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
counter_start(counter_dev);
|
||||
|
||||
/* The AON timer runs from the RTC clock at 32KHz (rather than
|
||||
* the system clock which is 32MHz) so we need to spin for a few cycles
|
||||
* to allow the register change to propagate.
|
||||
*/
|
||||
for (delay = 5000; delay--;) {
|
||||
}
|
||||
}
|
||||
|
||||
static void set_counter_alarm(void)
|
||||
{
|
||||
uint32_t timer_initial_value = (RTC_ALARM_SECOND * TIMEOUT);
|
||||
|
||||
if (counter_set_alarm(counter_dev, NULL,
|
||||
timer_initial_value, NULL)
|
||||
!= 0) {
|
||||
printk("Periodic Timer was not started yet\n");
|
||||
}
|
||||
}
|
||||
#elif (CONFIG_GPIO_QMSI_1)
|
||||
static struct device *gpio_dev;
|
||||
#define GPIO_INTERRUPT_PIN 4
|
||||
|
||||
static void setup_aon_gpio(void)
|
||||
{
|
||||
gpio_dev = device_get_binding("GPIO_1");
|
||||
if (!gpio_dev) {
|
||||
printk("gpio device not found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_pin_configure(gpio_dev, GPIO_INTERRUPT_PIN,
|
||||
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
|
||||
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
|
||||
}
|
||||
#elif (CONFIG_AIO_COMPARATOR)
|
||||
static struct device *cmp_dev;
|
||||
#define CMP_INTERRUPT_PIN 13
|
||||
|
||||
static void setup_aon_comparator(void)
|
||||
{
|
||||
volatile uint32_t delay = 0;
|
||||
|
||||
cmp_dev = device_get_binding("AIO_CMP_0");
|
||||
if (!cmp_dev) {
|
||||
printk("comparator device not found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait for the comparator to be grounded. */
|
||||
printk("USER_ACTION: Ground the comparator pin.\n");
|
||||
for (delay = 0; delay < 5000000; delay++) {
|
||||
}
|
||||
|
||||
aio_cmp_configure(cmp_dev, CMP_INTERRUPT_PIN,
|
||||
AIO_CMP_POL_RISE, 0,
|
||||
NULL, NULL);
|
||||
|
||||
printk("USER_ACTION: Set the comparator pin to 3.3V/1.8V.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void setup_wake_event(void)
|
||||
{
|
||||
#if (CONFIG_RTC)
|
||||
set_rtc_alarm();
|
||||
#elif (CONFIG_COUNTER)
|
||||
set_counter_alarm();
|
||||
#elif (CONFIG_GPIO_QMSI_1)
|
||||
printk("USER_ACTION: Press AON_GPIO 4.\n");
|
||||
#elif (CONFIG_AIO_COMPARATOR)
|
||||
setup_aon_comparator();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void do_soc_sleep(enum power_states state)
|
||||
{
|
||||
int wake_rtc = 0, wake_counter = 0, wake_gpio = 0, wake_cmp = 0;
|
||||
int i, devices_retval[suspend_device_count];
|
||||
|
||||
setup_wake_event();
|
||||
|
||||
for (i = suspend_device_count - 1; i >= 0; i--) {
|
||||
devices_retval[i] = device_set_power_state(suspend_devices[i],
|
||||
DEVICE_PM_SUSPEND_STATE);
|
||||
}
|
||||
|
||||
_sys_soc_set_power_state(state);
|
||||
|
||||
/*
|
||||
* Before enabling the interrupts, check the wake source
|
||||
* as it will get cleared after.
|
||||
*/
|
||||
#if (CONFIG_RTC)
|
||||
wake_rtc = rtc_get_pending_int(rtc_dev);
|
||||
#elif (CONFIG_COUNTER)
|
||||
wake_counter = counter_get_pending_int(counter_dev);
|
||||
#elif (CONFIG_GPIO_QMSI_1)
|
||||
wake_gpio = gpio_get_pending_int(gpio_dev);
|
||||
#elif (CONFIG_AIO_COMPARATOR)
|
||||
wake_cmp = aio_cmp_get_pending_int(cmp_dev);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < suspend_device_count; i++) {
|
||||
if (!devices_retval[i]) {
|
||||
device_set_power_state(suspend_devices[i],
|
||||
DEVICE_PM_ACTIVE_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (wake_rtc) {
|
||||
printk("Woke up with the rtc\n");
|
||||
}
|
||||
if (wake_counter) {
|
||||
printk("Woke up with the counter\n");
|
||||
}
|
||||
if (wake_gpio) {
|
||||
printk("Woke up with the aon gpio (pin:%x)\n",
|
||||
wake_gpio);
|
||||
}
|
||||
if (wake_cmp) {
|
||||
printk("Woke up with the aon cmp (pin:%x)\n",
|
||||
wake_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
int _sys_soc_suspend(int32_t ticks)
|
||||
{
|
||||
int state = get_next_state();
|
||||
int pm_operation = SYS_PM_NOT_HANDLED;
|
||||
|
||||
printk("Entering %s state\n", state_to_string(state));
|
||||
|
||||
switch (state) {
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
setup_wake_event();
|
||||
case SYS_POWER_STATE_CPU_LPS_1:
|
||||
/*
|
||||
* If the ARC enables LPSS, the PIC timer will
|
||||
* not wake us up from SYS_POWER_STATE_CPU_LPS_1
|
||||
* which is mapped to C2.
|
||||
* In that case another wake source will need to be set up.
|
||||
* As the ARC enables LPSS, it should as well take care of
|
||||
* setting up the relevant wake event or communicate
|
||||
* to the x86 that information.
|
||||
*/
|
||||
case SYS_POWER_STATE_CPU_LPS_2:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_LOW_POWER_STATE;
|
||||
_sys_soc_set_power_state(state);
|
||||
break;
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
/* Don't need wake event notification */
|
||||
_sys_soc_disable_wake_event_notification();
|
||||
|
||||
pm_operation = SYS_PM_DEEP_SLEEP;
|
||||
do_soc_sleep(state);
|
||||
break;
|
||||
default:
|
||||
printk("State not supported\n");
|
||||
break;
|
||||
}
|
||||
|
||||
printk("Exiting %s state\n", state_to_string(state));
|
||||
|
||||
if (pm_operation != SYS_PM_NOT_HANDLED) {
|
||||
_sys_soc_power_state_post_ops(state);
|
||||
}
|
||||
|
||||
last_state = state;
|
||||
|
||||
return pm_operation;
|
||||
}
|
||||
|
||||
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")) {
|
||||
suspend_devices[0] = &devices[i];
|
||||
} else if (!strcmp(devices[i].config->name, "ioapic")) {
|
||||
suspend_devices[1] = &devices[i];
|
||||
} else if (!strcmp(devices[i].config->name,
|
||||
CONFIG_UART_CONSOLE_ON_DEV_NAME)) {
|
||||
suspend_devices[2] = &devices[i];
|
||||
} else {
|
||||
suspend_devices[suspend_device_count++] = &devices[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
printk("Quark SE: Power Management sample application\n");
|
||||
|
||||
#if (CONFIG_RTC)
|
||||
setup_rtc();
|
||||
#elif (CONFIG_COUNTER)
|
||||
setup_counter();
|
||||
#elif (CONFIG_GPIO_QMSI_1)
|
||||
setup_aon_gpio();
|
||||
#endif
|
||||
|
||||
build_suspend_device_list();
|
||||
|
||||
/* All our application does is putting the task to sleep so the kernel
|
||||
* triggers the suspend operation.
|
||||
*/
|
||||
while (1) {
|
||||
task_sleep(SECONDS(TIMEOUT));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue