Commit 62dbe72c authored by Girisha Dengi's avatar Girisha Dengi Committed by Fabio Baltieri
Browse files

drivers: pm_cpu_ops: Add support for multiple PSCI versions



Each PSCI interface versions have different DT compatible strings
like arm,psci-0.2, arm,psci-1.1 and so on. However, the same driver
can be used for all the versions by adding #define DT_COMPAT for
required version and #undef DT_COMPAT for default version.

Add support for PSCI cold reset, warm reset and cpu-on function IDs.

Signed-off-by: default avatarGirisha Dengi <girisha.dengi@intel.com>
Signed-off-by: default avatarNavinkumar Balabakthan <navinkumar.balabakthan@intel.com>
parent f0ac2347
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -5,3 +5,5 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_PM_CPU_OPS pm_cpu_ops_weak_impl.c)

zephyr_library_sources_ifdef(CONFIG_PM_CPU_OPS_PSCI pm_cpu_ops_psci.c)

zephyr_library_sources_ifdef(CONFIG_PSCI_SHELL psci_shell.c)
+9 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ config PM_CPU_OPS_HAS_DRIVER
config PM_CPU_OPS_PSCI
	bool "Support for the ARM Power State Coordination Interface (PSCI)"
	default y
	depends on DT_HAS_ARM_PSCI_0_2_ENABLED
	depends on DT_HAS_ARM_PSCI_0_2_ENABLED || DT_HAS_ARM_PSCI_1_1_ENABLED
	select PM_CPU_OPS_HAS_DRIVER
	help
	  Say Y here if you want Zephyr to communicate with system firmware
@@ -29,4 +29,12 @@ config PM_CPU_OPS_PSCI
	  0022A ("Power State Coordination Interface System Software on
	  ARM processors").

config PSCI_SHELL
	bool "Support for PSCI interface shell commands"
	default y
	depends on SHELL && PM_CPU_OPS_PSCI
	help
	  Say Y here if you need to enable PSCI interface shell commands
	  like 'warm' and 'cold' reset commands.

endif
+74 −11
Original line number Diff line number Diff line
/*
 * Copyright 2020 Carlo Caione <ccaione@baylibre.com>
 *
 * Copyright (c) 2023, Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

@@ -19,7 +21,8 @@ LOG_MODULE_REGISTER(psci);
#include <zephyr/drivers/pm_cpu_ops.h>
#include "pm_cpu_ops_psci.h"

static struct psci psci_data;
/* PSCI data object. */
static struct psci_data_t psci_data;

static int psci_to_dev_err(int ret)
{
@@ -80,6 +83,43 @@ int pm_system_off(void)
	return psci_to_dev_err(ret);
}

/**
 * This function checks whether the given ID is supported or not, using
 * PSCI_FEATURES command.PSCI_FEATURES is supported from version 1.0 onwards.
 */
static int psci_features_check(unsigned long function_id)
{
	/* PSCI_FEATURES function ID is supported from PSCI 1.0 onwards. */
	if (!(PSCI_VERSION_MAJOR(psci_data.ver) >= 1)) {
		LOG_ERR("Function ID %lu not supported", function_id);
		return -ENOTSUP;
	}

	return psci_data.invoke_psci_fn(PSCI_FN_NATIVE(1_0, PSCI_FEATURES), function_id, 0, 0);
}

int pm_system_reset(unsigned char reset_type)
{
	int ret;

	if (psci_data.conduit == SMCCC_CONDUIT_NONE) {
		return -EINVAL;
	}

	if ((reset_type == SYS_WARM_RESET) &&
	    (!psci_features_check(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2)))) {
		ret = psci_data.invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0);
	} else if (reset_type == SYS_COLD_RESET) {
		ret = psci_data.invoke_psci_fn(PSCI_FN_NATIVE(0_2, SYSTEM_RESET), 0, 0, 0);
	} else {
		LOG_ERR("Invalid system reset type issued");
		return -EINVAL;
	}

	return psci_to_dev_err(ret);

}

static unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
					  unsigned long arg0,
					  unsigned long arg1,
@@ -107,16 +147,14 @@ static uint32_t psci_get_version(void)
	return psci_data.invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
}

static int set_conduit_method(void)
static int set_conduit_method(const struct device *dev)
{
	const char *method;
	const struct psci_config_t *dev_config = (const struct psci_config_t *)dev->config;

	method = DT_PROP(DT_INST(0, DT_DRV_COMPAT), method);

	if (!strcmp("hvc", method)) {
	if (!strcmp("hvc", dev_config->method)) {
		psci_data.conduit = SMCCC_CONDUIT_HVC;
		psci_data.invoke_psci_fn = __invoke_psci_fn_hvc;
	} else if (!strcmp("smc", method)) {
	} else if (!strcmp("smc", dev_config->method)) {
		psci_data.conduit = SMCCC_CONDUIT_SMC;
		psci_data.invoke_psci_fn = __invoke_psci_fn_smc;
	} else {
@@ -154,13 +192,38 @@ static int psci_init(const struct device *dev)
{
	psci_data.conduit = SMCCC_CONDUIT_NONE;

	if (set_conduit_method()) {
	if (set_conduit_method(dev)) {
		return -ENOTSUP;
	}

	return psci_detect();
}

DEVICE_DT_INST_DEFINE(0, psci_init, NULL,
	&psci_data, NULL, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
/**
 * Each PSCI interface versions have different DT compatible strings like arm,psci-0.2,
 * arm,psci-1.1 and so on. However, the same driver can be used for all the versions with
 * the below mentioned DT method where we need to #undef the default version arm,psci-0.2
 * and #define the required version like arm,psci-1.0 or arm,psci-1.1.
 */
#define PSCI_DEFINE(inst, ver)						\
	static const struct psci_config_t psci_config_##inst##ver = {	\
		.method = DT_PROP(DT_DRV_INST(inst), method)		\
	};								\
	DEVICE_DT_INST_DEFINE(inst,					\
			&psci_init,					\
			NULL,						\
			&psci_data,					\
			&psci_config_##inst##ver,				\
			PRE_KERNEL_1,					\
			CONFIG_KERNEL_INIT_PRIORITY_DEVICE,		\
			NULL);

#define PSCI_0_2_INIT(n) PSCI_DEFINE(n, PSCI_0_2)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT arm_psci_0_2
DT_INST_FOREACH_STATUS_OKAY(PSCI_0_2_INIT)

#define PSCI_1_1_INIT(n) PSCI_DEFINE(n, PSCI_1_1)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT arm_psci_1_1
DT_INST_FOREACH_STATUS_OKAY(PSCI_1_1_INIT)
+91 −34
Original line number Diff line number Diff line
@@ -19,10 +19,8 @@
#define PSCI_0_2_FN_BASE                  0x84000000
#define PSCI_0_2_FN(n)                    (PSCI_0_2_FN_BASE + (n))
#define PSCI_0_2_64BIT                    0x40000000
#define PSCI_0_2_FN64_BASE			\
					(PSCI_0_2_FN_BASE + PSCI_0_2_64BIT)
#define PSCI_0_2_FN64_BASE                (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT)
#define PSCI_0_2_FN64(n)                  (PSCI_0_2_FN64_BASE + (n))

#define PSCI_0_2_FN_PSCI_VERSION          PSCI_0_2_FN(0)
#define PSCI_0_2_FN_CPU_SUSPEND           PSCI_0_2_FN(1)
#define PSCI_0_2_FN_CPU_OFF               PSCI_0_2_FN(2)
@@ -33,12 +31,66 @@
#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU   PSCI_0_2_FN(7)
#define PSCI_0_2_FN_SYSTEM_OFF            PSCI_0_2_FN(8)
#define PSCI_0_2_FN_SYSTEM_RESET          PSCI_0_2_FN(9)

#define PSCI_0_2_FN64_CPU_SUSPEND         PSCI_0_2_FN64(1)
#define PSCI_0_2_FN64_CPU_ON              PSCI_0_2_FN64(3)
#define PSCI_0_2_FN64_AFFINITY_INFO       PSCI_0_2_FN64(4)
#define PSCI_0_2_FN64_MIGRATE             PSCI_0_2_FN64(5)
#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7)
#define PSCI_0_2_FN64_SYSTEM_RESET        PSCI_0_2_FN(9)

/* PSCI v1.0 interface */
#define PSCI_1_0_FN_BASE                  (0x84000000U)
#define PSCI_1_0_64BIT                    (0x40000000U)
#define PSCI_1_0_FN64_BASE                (PSCI_1_0_FN_BASE + PSCI_1_0_64BIT)
#define PSCI_1_0_FN(n)                    (PSCI_1_0_FN_BASE + (n))
#define PSCI_1_0_FN64(n)                  (PSCI_1_0_FN64_BASE + (n))
#define PSCI_1_0_FN_PSCI_VERSION          PSCI_1_0_FN(0)
#define PSCI_1_0_FN_CPU_SUSPEND           PSCI_1_0_FN(1)
#define PSCI_1_0_FN_CPU_OFF               PSCI_1_0_FN(2)
#define PSCI_1_0_FN_CPU_ON                PSCI_1_0_FN(3)
#define PSCI_1_0_FN_AFFINITY_INFO         PSCI_1_0_FN(4)
#define PSCI_1_0_FN_MIGRATE               PSCI_1_0_FN(5)
#define PSCI_1_0_FN_MIGRATE_INFO_TYPE     PSCI_1_0_FN(6)
#define PSCI_1_0_FN_MIGRATE_INFO_UP_CPU   PSCI_1_0_FN(7)
#define PSCI_1_0_FN_SYSTEM_OFF            PSCI_1_0_FN(8)
#define PSCI_1_0_FN_SYSTEM_RESET          PSCI_1_0_FN(9)
#define PSCI_1_0_FN_PSCI_FEATURES         PSCI_1_0_FN(10)
#define PSCI_1_0_FN64_CPU_SUSPEND         PSCI_1_0_FN64(1)
#define PSCI_1_0_FN64_CPU_ON              PSCI_1_0_FN64(3)
#define PSCI_1_0_FN64_AFFINITY_INFO       PSCI_1_0_FN64(4)
#define PSCI_1_0_FN64_MIGRATE             PSCI_1_0_FN64(5)
#define PSCI_1_0_FN64_MIGRATE_INFO_UP_CPU PSCI_1_0_FN64(7)
/* PSCI function ID is same for both 32 and 64 bit.*/
#define PSCI_1_0_FN64_SYSTEM_RESET        PSCI_1_0_FN(9)
#define PSCI_1_0_FN64_PSCI_FEATURES       PSCI_1_0_FN(10)

/* PSCI v1.1 interface. */
#define PSCI_1_1_FN_BASE                  (0x84000000U)
#define PSCI_1_1_64BIT                    (0x40000000U)
#define PSCI_1_1_FN64_BASE                (PSCI_1_1_FN_BASE + PSCI_1_1_64BIT)
#define PSCI_1_1_FN(n)                    (PSCI_1_1_FN_BASE + (n))
#define PSCI_1_1_FN64(n)                  (PSCI_1_1_FN64_BASE + (n))
#define PSCI_1_1_FN_PSCI_VERSION          PSCI_1_1_FN(0)
#define PSCI_1_1_FN_CPU_SUSPEND           PSCI_1_1_FN(1)
#define PSCI_1_1_FN_CPU_OFF               PSCI_1_1_FN(2)
#define PSCI_1_1_FN_CPU_ON                PSCI_1_1_FN(3)
#define PSCI_1_1_FN_AFFINITY_INFO         PSCI_1_1_FN(4)
#define PSCI_1_1_FN_MIGRATE               PSCI_1_1_FN(5)
#define PSCI_1_1_FN_MIGRATE_INFO_TYPE     PSCI_1_1_FN(6)
#define PSCI_1_1_FN_MIGRATE_INFO_UP_CPU   PSCI_1_1_FN(7)
#define PSCI_1_1_FN_SYSTEM_OFF            PSCI_1_1_FN(8)
#define PSCI_1_1_FN_SYSTEM_RESET          PSCI_1_1_FN(9)
#define PSCI_1_1_FN_PSCI_FEATURES         PSCI_1_1_FN(10)
#define PSCI_1_1_FN_SYSTEM_RESET2         PSCI_1_1_FN(18)
#define PSCI_1_1_FN64_CPU_SUSPEND         PSCI_1_1_FN64(1)
#define PSCI_1_1_FN64_CPU_ON              PSCI_1_1_FN64(3)
#define PSCI_1_1_FN64_AFFINITY_INFO       PSCI_1_1_FN64(4)
#define PSCI_1_1_FN64_MIGRATE             PSCI_1_1_FN64(5)
#define PSCI_1_1_FN64_MIGRATE_INFO_UP_CPU PSCI_1_1_FN64(7)
/* PSCI function ID is same for both 32 and 64 bit.*/
#define PSCI_1_1_FN64_SYSTEM_RESET        PSCI_1_1_FN(9)
#define PSCI_1_1_FN64_PSCI_FEATURES       PSCI_1_1_FN(10)
#define PSCI_1_1_FN64_SYSTEM_RESET2       PSCI_1_1_FN64(18)

/* PSCI return values (inclusive of all PSCI versions) */
#define PSCI_RET_SUCCESS          0
@@ -55,10 +107,15 @@
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
				unsigned long, unsigned long);

struct psci {
struct psci_data_t {
	enum arm_smccc_conduit conduit;
	psci_fn *invoke_psci_fn;
	uint32_t ver;
};

/* PSCI configuration data. */
struct psci_config_t {
	const char *method;
};

#endif /* ZEPHYR_DRIVERS_PSCI_PSCI_H_ */
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2023 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/shell/shell.h>
#include <zephyr/drivers/pm_cpu_ops.h>

/* Zephyr kernel start address. */
extern void __start(void);

static int cmd_reboot_warm(const struct shell *shctx, size_t argc, char **argv)
{
	ARG_UNUSED(shctx);
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);
	int ret;

	ret = pm_system_reset(SYS_WARM_RESET);
	if (ret != 0) {
		shell_error(shctx, "Failed to perform system warm reset");
	}

	return ret;
}

static int cmd_reboot_cold(const struct shell *shctx, size_t argc, char **argv)
{
	ARG_UNUSED(shctx);
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);
	int ret;

	ret = pm_system_reset(SYS_COLD_RESET);
	if (ret != 0) {
		shell_error(shctx, "Failed to perform system cold reset");
	}

	return ret;
}

static int cmd_psci_cpuon(const struct shell *shctx, size_t argc, char **argv)
{
	ARG_UNUSED(shctx);
	ARG_UNUSED(argc);
	long cpu_id;
	int result;

	errno = 0;
	cpu_id = strtol(argv[1], NULL, 10);
	if (cpu_id == 0 || cpu_id == LONG_MIN || cpu_id == LONG_MAX) {
		if (errno != 0) {
			shell_error(shctx, "psci: invalid input:%ld", cpu_id);
			return -EINVAL;
		}
	}

	result = pm_cpu_on((unsigned long)cpu_id, (uintptr_t)&__start);

	return result;
}

SHELL_STATIC_SUBCMD_SET_CREATE(
	sub_reboot,
	SHELL_CMD_ARG(warm, NULL, "System warm reset. Usage: <psci warm>", cmd_reboot_warm, 1, 0),
	SHELL_CMD_ARG(cold, NULL, "System cold reset. Usage: <psci cold>", cmd_reboot_cold, 1, 0),
	SHELL_CMD_ARG(cpuon, NULL, "Power-up the secondary CPU. Usage: <psci cpuon <cpuid>>",
		      cmd_psci_cpuon, 2, 0),
	SHELL_SUBCMD_SET_END /* Array terminated. */
);

SHELL_CMD_REGISTER(psci, &sub_reboot, "ARM PSCI interface commands", NULL);
Loading