Firmware implementing the PSCI functions described in ARM document number ARM DEN 0022A ("Power State Coordination Interface System Software on ARM processors") can be used by Zephyr to initiate various CPU-centric power operations. It is needed for virtualization, it is used to coordinate OSes and hypervisors and it provides the functions used for SMP bring-up such as CPU_ON and CPU_OFF. A new PSCI driver is introduced to setup a proper subsystem used to communicate with the PSCI firmware, implementing the basic operations: get_version, cpu_on, cpu_off and affinity_info. The current implementation only supports PSCI 0.2 and PSCI 1.0 The PSCI conduit (SMC or HVC) is setup reading the corresponding property in the DTS node. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
160 lines
4.4 KiB
C
160 lines
4.4 KiB
C
/*
|
|
* Copyright 2020 Carlo Caione <ccaione@baylibre.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_INCLUDE_DRIVERS_PSCI_H_
|
|
#define ZEPHYR_INCLUDE_DRIVERS_PSCI_H_
|
|
|
|
/**
|
|
* @file
|
|
* @brief Public API for ARM PSCI
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <arch/arm/arm-smccc.h>
|
|
#include <stddef.h>
|
|
#include <device.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* PSCI version decoding (independent of PSCI version) */
|
|
#define PSCI_VERSION_MAJOR_SHIFT 16
|
|
#define PSCI_VERSION_MINOR_MASK \
|
|
((1U << PSCI_VERSION_MAJOR_SHIFT) - 1)
|
|
#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK
|
|
|
|
#define PSCI_VERSION_MAJOR(ver) \
|
|
(((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
|
|
#define PSCI_VERSION_MINOR(ver) \
|
|
((ver) & PSCI_VERSION_MINOR_MASK)
|
|
|
|
/**
|
|
* @brief ARM PSCI Driver API
|
|
* @defgroup arm_psci ARM PSCI Driver API
|
|
* @{
|
|
*/
|
|
|
|
typedef uint32_t (*psci_get_version_f)(const struct device *dev);
|
|
|
|
typedef int (*psci_cpu_off_f)(const struct device *dev, uint32_t state);
|
|
|
|
typedef int (*psci_cpu_on_f)(const struct device *dev, unsigned long cpuid,
|
|
unsigned long entry_point);
|
|
|
|
typedef int (*psci_affinity_info_f)(const struct device *dev,
|
|
unsigned long target_affinity,
|
|
unsigned long lowest_affinity_level);
|
|
|
|
__subsystem struct psci_driver_api {
|
|
psci_get_version_f get_version;
|
|
psci_cpu_off_f cpu_off;
|
|
psci_cpu_on_f cpu_on;
|
|
psci_affinity_info_f affinity_info;
|
|
};
|
|
|
|
/**
|
|
* @brief Return the version of PSCI implemented
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
*
|
|
* @return The PSCI version
|
|
*/
|
|
__syscall uint32_t psci_get_version(const struct device *dev);
|
|
|
|
static inline uint32_t z_impl_psci_get_version(const struct device *dev)
|
|
{
|
|
const struct psci_driver_api *api =
|
|
(const struct psci_driver_api *)dev->api;
|
|
|
|
return api->get_version(dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Power down the calling core
|
|
*
|
|
* This call is intended foruse in hotplug. A core that is powered down by
|
|
* CPU_OFF can only be powered up again in response to a CPU_ON
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param state Not used
|
|
*
|
|
* @return The call does not return when successful
|
|
*/
|
|
__syscall int psci_cpu_off(const struct device *dev, uint32_t state);
|
|
|
|
static inline int z_impl_psci_cpu_off(const struct device *dev, uint32_t state)
|
|
{
|
|
const struct psci_driver_api *api =
|
|
(const struct psci_driver_api *)dev->api;
|
|
|
|
return api->cpu_off(dev, state);
|
|
}
|
|
|
|
/**
|
|
* @brief Power up a core
|
|
*
|
|
* This call is used to power up cores that either have not yet been booted
|
|
* into the calling supervisory software or have been previously powered down
|
|
* with a CPU_OFF call
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param cpuid This parameter contains a copy of the affinity fields of the
|
|
* MPIDR register
|
|
* @param entry_point Address at which the core must commence execution, when
|
|
* it enters the return Non-secure Exception level.
|
|
*
|
|
* @return 0 on success, a negative errno otherwise
|
|
*/
|
|
__syscall int psci_cpu_on(const struct device *dev, unsigned long cpuid,
|
|
unsigned long entry_point);
|
|
|
|
static inline int z_impl_psci_cpu_on(const struct device *dev,
|
|
unsigned long cpuid,
|
|
unsigned long entry_point) {
|
|
const struct psci_driver_api *api =
|
|
(const struct psci_driver_api *)dev->api;
|
|
|
|
return api->cpu_on(dev, cpuid, entry_point);
|
|
}
|
|
|
|
/**
|
|
* @brief Enable the caller to request status of an affinity instance
|
|
*
|
|
* @param dev Pointer to the device structure for the driver instance
|
|
* @param target_affinity This parameter contains a copy of the affinity fields
|
|
* of the MPIDR register
|
|
* @param lowest_affinity_level Denotes the lowest affinity level field that is
|
|
* valid in the target_affinity parameter
|
|
*
|
|
* @return 2 if the affinity instance is transitioning to an ON sate, 1 off, 0
|
|
* on, a negative errno otherwise
|
|
*/
|
|
__syscall int psci_affinity_info(const struct device *dev,
|
|
unsigned long target_affinity,
|
|
unsigned long lowest_affinity_level);
|
|
|
|
static inline int z_impl_psci_affinity_info(const struct device *dev,
|
|
unsigned long target_affinity,
|
|
unsigned long lowest_affinity_level)
|
|
{
|
|
const struct psci_driver_api *api =
|
|
(const struct psci_driver_api *)dev->api;
|
|
|
|
return api->affinity_info(dev, target_affinity, lowest_affinity_level);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#include <syscalls/psci.h>
|
|
|
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_PSCI_H_ */
|