blob: ac2688f5a0296a5637f6a804454aa562d47f06b0 [file] [log] [blame]
/*
* 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>
#if defined(CONFIG_STDOUT_CONSOLE)
#include <stdio.h>
#define PRINT printf
#else
#include <misc/printk.h>
#define PRINT printk
#endif
#include <string.h>
#include <rtc.h>
#define SLEEPTICKS SECONDS(5)
#define P_LVL2 0xb0800504
static int pm_state; /* 1 = LPS; 2 = Device suspend only */
static void quark_low_power(void);
static struct device *rtc_dev;
static uint32_t start_time, end_time;
static void create_device_list(void);
static void suspend_devices(int pm_policy);
static void resume_devices(int pm_policy);
#define DEVICE_POLICY_MAX 10
static struct device *device_list;
static int device_count;
static char device_policy_list[DEVICE_POLICY_MAX];
static char device_retval[DEVICE_POLICY_MAX];
void main(void)
{
struct rtc_config config;
PRINT("Power Management Demo\n");
config.init_val = 0;
config.alarm_enable = 0;
config.alarm_val = RTC_ALARM_SECOND;
config.cb_fn = NULL;
rtc_dev = device_get_binding("RTC_0");
rtc_enable(rtc_dev);
rtc_set_config(rtc_dev, &config);
create_device_list();
while (1) {
task_sleep(SLEEPTICKS);
}
}
static int check_pm_policy(int32_t ticks)
{
static int policy;
/*
* Compare time available with wake latencies and select
* appropriate power saving policy
*
* For the demo we will alternate between following states
*
* 0 = no power saving operation
* 1 = low power state
* 2 = device suspend only
* 3 = deep sleep
*
*/
policy = (policy > 3 ? 0 : policy);
return policy++;
}
static int low_power_state_entry(int32_t ticks)
{
PRINT("\n\nLow power state policy entry!\n");
/* Turn off peripherals/clocks here */
suspend_devices(SYS_PM_LOW_POWER_STATE);
quark_low_power();
return SYS_PM_LOW_POWER_STATE;
}
static int device_suspend_only_entry(int32_t ticks)
{
PRINT("Device suspend only policy entry!\n");
/* Turn off peripherals/clocks here */
suspend_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
return SYS_PM_DEVICE_SUSPEND_ONLY;
}
int _sys_soc_suspend(int32_t ticks)
{
int ret = SYS_PM_NOT_HANDLED;
pm_state = check_pm_policy(ticks);
switch (pm_state) {
case 1:
start_time = rtc_read(rtc_dev);
ret = low_power_state_entry(ticks);
break;
case 2:
start_time = rtc_read(rtc_dev);
ret = device_suspend_only_entry(ticks);
break;
case 3:
/*
* 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 */
/* break; (fall through for now) */
}
default:
/* No PM operations */
ret = SYS_PM_NOT_HANDLED;
break;
}
return ret;
}
static void low_power_state_exit(void)
{
resume_devices(SYS_PM_LOW_POWER_STATE);
end_time = rtc_read(rtc_dev);
PRINT("\nLow power state policy exit!\n");
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
end_time - start_time);
}
static void device_suspend_only_exit(void)
{
resume_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
end_time = rtc_read(rtc_dev);
PRINT("\nDevice suspend only policy exit!\n");
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
end_time - start_time);
}
void _sys_soc_resume(void)
{
switch (pm_state) {
case 1:
low_power_state_exit();
break;
case 2:
device_suspend_only_exit();
break;
default:
break;
}
pm_state = 0;
}
static void quark_low_power(void)
{
__asm__ volatile (
"sti\n\t"
/*
* Atomically enable interrupts and enter LPS.
*
* Reading P_LVL2 causes C2 transition.
*/
"movl (%%eax), %%eax\n\t"
::"a"(P_LVL2));
}
static void suspend_devices(int pm_policy)
{
int i;
for (i = 0; i < device_count; i++) {
int idx = device_policy_list[i];
/* If necessary the policy manager can check if a specific
* device in the policy list is busy as shown below :
* if(device_busy_check(&device_list[idx])) {do something}
*/
device_retval[i] = device_suspend(&device_list[idx], pm_policy);
}
}
static void resume_devices(int pm_policy)
{
int i;
for (i = device_count - 1; i >= 0; i--) {
if (!device_retval[i]) {
int idx = device_policy_list[i];
device_resume(&device_list[idx], pm_policy);
}
}
}
static void create_device_list(void)
{
int count;
int i;
/*
* Create a list of devices that will be suspended.
* For the demo we will create a simple list containing
* gpio devices.
*/
device_list_get(&device_list, &count);
for (i = 0; (i < count) && (device_count < DEVICE_POLICY_MAX); i++) {
if (!strcmp(device_list[i].config->name, "GPIO_0") ||
!strcmp(device_list[i].config->name,
"GPIO_1")) {
device_policy_list[device_count++] = i;
}
}
}