Newer
Older
arm-trusted-firmware / plat / st / stm32mp1 / stm32mp1_power_config.c
@Yann Gautier Yann Gautier on 27 Dec 2020 2 KB stm32mp1: add low power management
/*
 * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <errno.h>
#include <limits.h>

#include <libfdt.h>

#include <common/debug.h>
#include <dt-bindings/power/stm32mp1-power.h>

#include <stm32mp_dt.h>
#include <stm32mp1_power_config.h>

#define DT_PWR_COMPAT			"st,stm32mp1-pwr"
#define SYSTEM_SUSPEND_SUPPORTED_MODES	"system_suspend_supported_soc_modes"
#define SYSTEM_OFF_MODE			"system_off_soc_mode"

static uint32_t deepest_system_suspend_mode;
static uint32_t system_off_mode;
static uint8_t stm32mp1_supported_soc_modes[STM32_PM_MAX_SOC_MODE];

static int dt_get_pwr_node(void *fdt)
{
	return fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT);
}

static void save_supported_mode(void *fdt, int pwr_node)
{
	int len;
	uint32_t count;
	unsigned int i;
	uint32_t supported[ARRAY_SIZE(stm32mp1_supported_soc_modes)];
	const void *prop;

	prop = fdt_getprop(fdt, pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES, &len);
	if (prop == NULL) {
		panic();
	}

	count = (uint32_t)len / sizeof(uint32_t);
	if (count > STM32_PM_MAX_SOC_MODE) {
		panic();
	}

	if (fdt_read_uint32_array(pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES,
				  &supported[0], count) < 0) {
		ERROR("PWR DT\n");
		panic();
	}

	for (i = 0; i < count; i++) {
		if (supported[i] >= STM32_PM_MAX_SOC_MODE) {
			ERROR("Invalid mode\n");
			panic();
		}
		stm32mp1_supported_soc_modes[supported[i]] = 1U;
	}

	/* Initialize to deepest possible mode */
	for (i = STM32_PM_MAX_SOC_MODE - 1U; i != STM32_PM_CSLEEP_RUN; i--) {
		if (stm32mp1_supported_soc_modes[i] == 1U) {
			deepest_system_suspend_mode = i;
			break;
		}
	}
}

static int dt_fill_lp_state(uint32_t *lp_state_config, const char *lp_state)
{
	int pwr_node;
	void *fdt;
	const fdt32_t *cuint;

	if (fdt_get_address(&fdt) == 0) {
		return -ENOENT;
	}

	pwr_node = dt_get_pwr_node(fdt);
	if (pwr_node < 0) {
		return -FDT_ERR_NOTFOUND;
	}

	cuint = fdt_getprop(fdt, pwr_node, lp_state, NULL);
	if (cuint == NULL) {
		return -FDT_ERR_NOTFOUND;
	}

	*lp_state_config = fdt32_to_cpu(*cuint);

	save_supported_mode(fdt, pwr_node);

	return 0;
}

void stm32mp1_init_lp_states(void)
{
	if (dt_fill_lp_state(&system_off_mode, SYSTEM_OFF_MODE) < 0) {
		ERROR("Node %s not found\n", SYSTEM_OFF_MODE);
		panic();
	}
}

static bool is_allowed_mode(uint32_t soc_mode)
{
	assert(soc_mode < ARRAY_SIZE(stm32mp1_supported_soc_modes));

	return stm32mp1_supported_soc_modes[soc_mode] == 1U;
}

uint32_t stm32mp1_get_lp_soc_mode(uint32_t psci_mode)
{
	uint32_t mode;

	if (psci_mode == PSCI_MODE_SYSTEM_OFF) {
		return system_off_mode;
	}

	mode = deepest_system_suspend_mode;

	while ((mode > STM32_PM_CSLEEP_RUN) && !is_allowed_mode(mode)) {
		mode--;
	}

	return mode;
}