Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#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(s32_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;
	u32_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);

	/* In QMSI, in order to save the alarm callback we must set
	 * 'alarm_enable = 1' during configuration. However, this
	 * automatically triggers the alarm underneath. So, to avoid
	 * the alarm being fired any time soon, we set the 'init_val'
	 * to 1 and the 'alarm_val' to 0.
	 */
	config.init_val = 1;
	config.alarm_val = 0;
	config.alarm_enable = 1;
	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);
	}
}