Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

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

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2019 Linaro Limited.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <kernel.h>
#include <sys/__assert.h>
#include "pm_policy.h"

#include <ti/devices/cc13x2_cc26x2/driverlib/sys_ctrl.h>

#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26X2.h>

#include <ti/drivers/dpl/ClockP.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SwiP.h>

#define LOG_LEVEL CONFIG_SYS_PM_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_DECLARE(power);

#define SECS_TO_TICKS		CONFIG_SYS_CLOCK_TICKS_PER_SEC

/* Wakeup delay from standby in microseconds */
#define WAKEDELAYSTANDBY    240

extern PowerCC26X2_ModuleState PowerCC26X2_module;

/* PM Policy based on SoC/Platform residency requirements */
static const unsigned int pm_min_residency[] = {
#ifdef CONFIG_SYS_POWER_SLEEP_STATES
	CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_1 * SECS_TO_TICKS / MSEC_PER_SEC,
	CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 * SECS_TO_TICKS / MSEC_PER_SEC,
#endif /* CONFIG_SYS_POWER_SLEEP_STATES */
};

enum power_states sys_pm_policy_next_state(int32_t ticks)
{
	uint32_t constraints;
	bool disallowed = false;
	int i;

	/* check operating conditions, optimally choose DCDC versus GLDO */
	SysCtrl_DCDC_VoltageConditionalControl();

	/* query the declared constraints */
	constraints = Power_getConstraintMask();

	if ((ticks != K_TICKS_FOREVER) && (ticks < pm_min_residency[0])) {
		LOG_DBG("Not enough time for PM operations: %d", ticks);
		return SYS_POWER_STATE_ACTIVE;
	}

	for (i = ARRAY_SIZE(pm_min_residency) - 1; i >= 0; i--) {
#ifdef CONFIG_SYS_PM_STATE_LOCK
		if (!sys_pm_ctrl_is_state_enabled((enum power_states)(i))) {
			continue;
		}
#endif
		if ((ticks == K_TICKS_FOREVER) ||
		    (ticks >= pm_min_residency[i])) {
			/* Verify if Power module has constraints set to
			 * disallow a state
			 */
			switch (i) {
			case 0: /* Idle mode */
				if ((constraints & (1 <<
				    PowerCC26XX_DISALLOW_IDLE)) != 0) {
					disallowed = true;
				}
				break;
			case 1: /* Standby mode */
				if ((constraints & (1 <<
				    PowerCC26XX_DISALLOW_STANDBY)) != 0) {
					disallowed = true;
				}
				/* Set timeout for wakeup event */
				__ASSERT(
				    CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 > 1,
				    "CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 must "
				    "be greater than 1.");
				if (ticks != K_TICKS_FOREVER) {
					/* NOTE: Ideally we'd like to set a
					 * timer to wake up just a little
					 * earlier to take care of the wakeup
					 * sequence, ie. by WAKEDELAYSTANDBY
					 * microsecs. However, given
					 * k_timer_start (called later by
					 * ClockP_start) does not currently
					 * have sub-millisecond accuracy, wakeup
					 * would be at up to (WAKEDELAYSTANDBY
					 * + 1 ms) ahead of the next timeout.
					 * This also has the implication that
					 * SYS_PM_MIN_RESIDENCY_SLEEP_2
					 * must be greater than 1.
					 */
					ticks -= (WAKEDELAYSTANDBY *
						CONFIG_SYS_CLOCK_TICKS_PER_SEC
						+ 1000000) / 1000000;
#if (CONFIG_SYS_CLOCK_TICKS_PER_SEC <= 1000)
					/*
					 * ClockP_setTimeout cannot handle any
					 * more ticks
					 */
					ticks = MIN(ticks, UINT32_MAX / 1000 *
						CONFIG_SYS_CLOCK_TICKS_PER_SEC);
#endif
					ClockP_setTimeout(ClockP_handle(
						(ClockP_Struct *)
						&PowerCC26X2_module.clockObj),
						ticks);
				}
				break;
			default:
				/* This should never be reached */
				LOG_ERR("Invalid sleep state detected\n");
			}

			if (disallowed) {
				disallowed = false;
				continue;
			}

			LOG_DBG("Selected power state %d "
				"(ticks: %d, min_residency: %u)",
				i, ticks, pm_min_residency[i]);
			return (enum power_states)(i);
		}
	}

	LOG_DBG("No suitable power state found!");
	return SYS_POWER_STATE_ACTIVE;
}

__weak bool sys_pm_policy_low_power_devices(enum power_states pm_state)
{
#ifdef CONFIG_SYS_POWER_SLEEP_STATES
	return (pm_state == SYS_POWER_STATE_SLEEP_2);
#else
	return false;
#endif
}