arm64: psci: add node hierarchy
In order to properly support more arm64 SoCs PSCI needs to handle the hierarchy of cpus/clusters within the SoC. The nodes within PSCI are kept in a tree as well as a depth-first ordered array of same tree. Additionally, the PSCI states are now maintained in a hierachal manner. OFF propogates up the tree as long as all siblings are set to OFF. ON propogates up the tree until a node is not already set to OFF. The SoC provides the operations for determining how many children are at a given affinity level. Lastly, the secmon startup has been reworked in that all non-BSP CPUs wait for instructions from the BSP. BUG=chrome-os-partner:32136 BRANCH=None TEST=Can still boot into kernel with SMP. Change-Id: I036fabaf0f1cefa2841264c47e4092c75a2ff4dc Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 721d408cd110e1b56d38789177b740aa0e54ca33 Original-Change-Id: I520a9726e283bee7edcb514cda28ec1eb31b5ea0 Original-Signed-off-by: Aaron Durbin <adurbin@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/226480 Original-Reviewed-by: Furquan Shaikh <furquan@chromium.org> Reviewed-on: http://review.coreboot.org/9390 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh <furquan@google.com>
This commit is contained in:
		
				
					committed by
					
						
						Patrick Georgi
					
				
			
			
				
	
			
			
			
						parent
						
							7d62ad05fb
						
					
				
				
					commit
					b777f3e3d1
				
			@@ -28,23 +28,14 @@
 | 
				
			|||||||
#include <console/console.h>
 | 
					#include <console/console.h>
 | 
				
			||||||
#include "secmon.h"
 | 
					#include "secmon.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					 | 
				
			||||||
	PSCI_CPU_STATE_OFF = 0,
 | 
					 | 
				
			||||||
	PSCI_CPU_STATE_ON_PENDING,
 | 
					 | 
				
			||||||
	PSCI_CPU_STATE_ON,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct psci_cpu_state {
 | 
					 | 
				
			||||||
	uint64_t mpidr;
 | 
					 | 
				
			||||||
	void *entry;
 | 
					 | 
				
			||||||
	void *arg;
 | 
					 | 
				
			||||||
	int state;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DECLARE_SPIN_LOCK(psci_spinlock);
 | 
					DECLARE_SPIN_LOCK(psci_spinlock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct psci_cpu_state psci_state[CONFIG_MAX_CPUS];
 | 
					/* Root of PSCI node tree. */
 | 
				
			||||||
 | 
					static struct psci_node psci_root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Array of all the psci_nodes in system.  */
 | 
				
			||||||
 | 
					static size_t psci_num_nodes;
 | 
				
			||||||
 | 
					static struct psci_node **psci_nodes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void psci_lock(void)
 | 
					static inline void psci_lock(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -56,33 +47,173 @@ static inline void psci_unlock(void)
 | 
				
			|||||||
	spin_unlock(&psci_spinlock);
 | 
						spin_unlock(&psci_spinlock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int psci_cpu_state_locked(int i)
 | 
					static inline int psci_state_locked(const struct psci_node *e)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return psci_state[i].state;
 | 
						return e->state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void psci_cpu_set_state_locked(int i, int s)
 | 
					static inline void psci_set_state_locked(struct psci_node *e, int s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	psci_state[i].state = s;
 | 
						e->state = s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct cpu_info *mpidr_to_cpu_info(uint64_t mpidr)
 | 
					static struct psci_node *psci_node_lookup(uint64_t mpidr, int level)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						size_t i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(psci_state); i++) {
 | 
						/* The array of node pointers are in depth-first order of the tree. */
 | 
				
			||||||
		if (mpidr == psci_state[i].mpidr)
 | 
						for (i = 0; i < psci_num_nodes; i++) {
 | 
				
			||||||
			return cpu_info_for_cpu(i);
 | 
							struct psci_node *current = psci_nodes[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (current->mpidr > mpidr)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							if (current->mpidr < mpidr)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (current->level == level)
 | 
				
			||||||
 | 
								return current;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct psci_node *node_self(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return psci_node_lookup(cpu_info()->mpidr, PSCI_AFFINITY_LEVEL_0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Find the ancestor of node affected by a state transition limited by level. */
 | 
				
			||||||
 | 
					static struct psci_node *psci_find_ancestor(struct psci_node *e, int level,
 | 
				
			||||||
 | 
											int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If all siblings of the node are already off then parent can be
 | 
				
			||||||
 | 
						 * set to off as well. */
 | 
				
			||||||
 | 
						if (state == PSCI_STATE_OFF) {
 | 
				
			||||||
 | 
							while (1) {
 | 
				
			||||||
 | 
								size_t i;
 | 
				
			||||||
 | 
								struct psci_node *s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (psci_root_node(e))
 | 
				
			||||||
 | 
									return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								p = psci_node_parent(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (p->level > level)
 | 
				
			||||||
 | 
									return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (i = 0; i < p->children.num; i++) {
 | 
				
			||||||
 | 
									s = &p->children.nodes[i];
 | 
				
			||||||
 | 
									/* Don't check target. */
 | 
				
			||||||
 | 
									if (s == e)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									if (psci_state_locked(s) != PSCI_STATE_OFF)
 | 
				
			||||||
 | 
										return e;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								e = p;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* All ancestors in state OFF are affected. */
 | 
				
			||||||
 | 
						if (state == PSCI_STATE_ON_PENDING) {
 | 
				
			||||||
 | 
							while (1) {
 | 
				
			||||||
 | 
								/* At the root. Return last affected node. */
 | 
				
			||||||
 | 
								if (psci_root_node(e))
 | 
				
			||||||
 | 
									return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								p = psci_node_parent(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (p->level > level)
 | 
				
			||||||
 | 
									return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* This parent is already ON. */
 | 
				
			||||||
 | 
								if (psci_state_locked(p) != PSCI_STATE_OFF)
 | 
				
			||||||
 | 
									return e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								e = p;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Default to returning node passed in. */
 | 
				
			||||||
 | 
						return e;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void psci_set_hierarchy_state(struct psci_node *from,
 | 
				
			||||||
 | 
										struct psci_node *to,
 | 
				
			||||||
 | 
										int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						end = psci_node_parent(to);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (from != end) {
 | 
				
			||||||
 | 
							/* Raced with another CPU as state is already set. */
 | 
				
			||||||
 | 
							if (psci_state_locked(from) == state)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							psci_set_state_locked(from, state);
 | 
				
			||||||
 | 
							from = psci_node_parent(from);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void psci_cpu_on_callback(void *arg)
 | 
					static void psci_cpu_on_callback(void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct psci_cpu_state *s = arg;
 | 
						struct exc_state state;
 | 
				
			||||||
 | 
						int target_el;
 | 
				
			||||||
 | 
						struct psci_node *e = arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	psci_turn_on_self(s->entry, s->arg);
 | 
						psci_lock();
 | 
				
			||||||
 | 
						psci_set_hierarchy_state(e, e->cpu_state.ancestor, PSCI_STATE_ON);
 | 
				
			||||||
 | 
						psci_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Target EL is determined if HVC is enabled or not. */
 | 
				
			||||||
 | 
						target_el = (raw_read_scr_el3() & SCR_HVC_ENABLE) ? EL2 : EL1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&state, 0, sizeof(state));
 | 
				
			||||||
 | 
						state.elx.spsr = get_eret_el(target_el, SPSR_USE_H);
 | 
				
			||||||
 | 
						transition_with_entry(e->cpu_state.entry, e->cpu_state.arg, &state);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void psci_cpu_on_prepare(struct psci_node *e,
 | 
				
			||||||
 | 
									void *entry, void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *ancestor;
 | 
				
			||||||
 | 
						int state = PSCI_STATE_ON_PENDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e->cpu_state.entry = entry;
 | 
				
			||||||
 | 
						e->cpu_state.arg = arg;
 | 
				
			||||||
 | 
						ancestor = psci_find_ancestor(e, PSCI_AFFINITY_LEVEL_HIGHEST, state);
 | 
				
			||||||
 | 
						e->cpu_state.ancestor = ancestor;
 | 
				
			||||||
 | 
						psci_set_hierarchy_state(e, ancestor, state);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int psci_schedule_cpu_on(struct psci_node *e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cpu_action action = {
 | 
				
			||||||
 | 
							.run = &psci_cpu_on_callback,
 | 
				
			||||||
 | 
							.arg = e,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (arch_run_on_cpu_async(e->cpu_state.ci->id, &action))
 | 
				
			||||||
 | 
							return PSCI_RET_INTERNAL_FAILURE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return PSCI_RET_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void psci_turn_on_self(void *entry, void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *e = node_self();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "Couldn't turn on self: mpidr %llx\n",
 | 
				
			||||||
 | 
								cpu_info()->mpidr);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						psci_lock();
 | 
				
			||||||
 | 
						psci_cpu_on_prepare(e, entry, arg);
 | 
				
			||||||
 | 
						psci_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						psci_schedule_cpu_on(e);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void psci_cpu_on(struct psci_func *pf)
 | 
					static void psci_cpu_on(struct psci_func *pf)
 | 
				
			||||||
@@ -90,60 +221,68 @@ static void psci_cpu_on(struct psci_func *pf)
 | 
				
			|||||||
	uint64_t entry;
 | 
						uint64_t entry;
 | 
				
			||||||
	uint64_t target_mpidr;
 | 
						uint64_t target_mpidr;
 | 
				
			||||||
	uint64_t context_id;
 | 
						uint64_t context_id;
 | 
				
			||||||
	struct cpu_info *ci;
 | 
					 | 
				
			||||||
	int cpu_state;
 | 
						int cpu_state;
 | 
				
			||||||
	struct cpu_action action;
 | 
						struct psci_node *e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	target_mpidr = psci64_arg(pf, PSCI_PARAM_0);
 | 
						target_mpidr = psci64_arg(pf, PSCI_PARAM_0);
 | 
				
			||||||
	entry = psci64_arg(pf, PSCI_PARAM_1);
 | 
						entry = psci64_arg(pf, PSCI_PARAM_1);
 | 
				
			||||||
	context_id = psci64_arg(pf, PSCI_PARAM_2);
 | 
						context_id = psci64_arg(pf, PSCI_PARAM_2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ci = mpidr_to_cpu_info(target_mpidr);
 | 
						e = psci_node_lookup(target_mpidr, PSCI_AFFINITY_LEVEL_0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ci == NULL) {
 | 
						if (e == NULL) {
 | 
				
			||||||
		psci32_return(pf, PSCI_RET_INVALID_PARAMETERS);
 | 
							psci32_return(pf, PSCI_RET_INVALID_PARAMETERS);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	psci_lock();
 | 
						psci_lock();
 | 
				
			||||||
	cpu_state = psci_cpu_state_locked(ci->id);
 | 
						cpu_state = psci_state_locked(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cpu_state == PSCI_CPU_STATE_ON_PENDING) {
 | 
						if (cpu_state == PSCI_STATE_ON_PENDING) {
 | 
				
			||||||
		psci32_return(pf, PSCI_RET_ON_PENDING);
 | 
							psci32_return(pf, PSCI_RET_ON_PENDING);
 | 
				
			||||||
		psci_unlock();
 | 
							psci_unlock();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	} else if (cpu_state == PSCI_CPU_STATE_ON) {
 | 
						} else if (cpu_state == PSCI_STATE_ON) {
 | 
				
			||||||
		psci32_return(pf, PSCI_RET_ALREADY_ON);
 | 
							psci32_return(pf, PSCI_RET_ALREADY_ON);
 | 
				
			||||||
		psci_unlock();
 | 
							psci_unlock();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	psci_cpu_set_state_locked(ci->id, PSCI_CPU_STATE_ON_PENDING);
 | 
						psci_cpu_on_prepare(e, (void *)entry, (void *)context_id);
 | 
				
			||||||
	/* Set the parameters and initialize the action. */
 | 
					 | 
				
			||||||
	psci_state[ci->id].entry = (void *)(uintptr_t)entry;
 | 
					 | 
				
			||||||
	psci_state[ci->id].arg = (void *)(uintptr_t)context_id;
 | 
					 | 
				
			||||||
	action.run = &psci_cpu_on_callback;
 | 
					 | 
				
			||||||
	action.arg = &psci_state[ci->id];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (arch_run_on_cpu_async(ci->id, &action)) {
 | 
					 | 
				
			||||||
		psci32_return(pf, PSCI_RET_INTERNAL_FAILURE);
 | 
					 | 
				
			||||||
		psci_unlock();
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	psci_unlock();
 | 
						psci_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	psci32_return(pf, PSCI_RET_SUCCESS);
 | 
						psci32_return(pf, psci_schedule_cpu_on(e));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void psci_cpu_off(struct psci_func *pf)
 | 
					static int psci_turn_off_node(struct psci_node *e, int level,
 | 
				
			||||||
 | 
										int state_id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *ancestor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	psci_lock();
 | 
						psci_lock();
 | 
				
			||||||
	psci_cpu_set_state_locked(cpu_info()->id, PSCI_CPU_STATE_OFF);
 | 
						ancestor = psci_find_ancestor(e, level, PSCI_STATE_OFF);
 | 
				
			||||||
 | 
						psci_set_hierarchy_state(e, ancestor, PSCI_STATE_OFF);
 | 
				
			||||||
	psci_unlock();
 | 
						psci_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* TODO(adurbin): writeback cache and actually turn off CPU. */
 | 
						/* TODO(adurbin): writeback cache and actually turn off CPU. */
 | 
				
			||||||
	secmon_trampoline(&secmon_wait_for_action, NULL);
 | 
						secmon_trampoline(&secmon_wait_for_action, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return PSCI_RET_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int psci_turn_off_self(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *e = node_self();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "No PSCI node for MPIDR %llx.\n",
 | 
				
			||||||
 | 
								cpu_info()->mpidr);
 | 
				
			||||||
 | 
							return PSCI_RET_INTERNAL_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* -1 state id indicates to SoC to make its own decision for
 | 
				
			||||||
 | 
						 * internal state when powering off the node. */
 | 
				
			||||||
 | 
						return psci_turn_off_node(e, PSCI_AFFINITY_LEVEL_HIGHEST, -1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int psci_handler(struct smc_call *smc)
 | 
					static int psci_handler(struct smc_call *smc)
 | 
				
			||||||
@@ -158,7 +297,7 @@ static int psci_handler(struct smc_call *smc)
 | 
				
			|||||||
		psci_cpu_on(pf);
 | 
							psci_cpu_on(pf);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case PSCI_CPU_OFF64:
 | 
						case PSCI_CPU_OFF64:
 | 
				
			||||||
		psci_cpu_off(pf);
 | 
							psci32_return(pf, psci_turn_off_self());
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		psci32_return(pf, PSCI_RET_NOT_SUPPORTED);
 | 
							psci32_return(pf, PSCI_RET_NOT_SUPPORTED);
 | 
				
			||||||
@@ -168,41 +307,190 @@ static int psci_handler(struct smc_call *smc)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void psci_link_cpu_info(void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct psci_node *e = node_self();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "No PSCI node for MPIDR %llx.\n",
 | 
				
			||||||
 | 
								cpu_info()->mpidr);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e->cpu_state.ci = cpu_info();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int psci_init_node(struct psci_node *e,
 | 
				
			||||||
 | 
									struct psci_node *parent,
 | 
				
			||||||
 | 
									int level, uint64_t mpidr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						uint64_t mpidr_inc;
 | 
				
			||||||
 | 
						struct psci_node_group *ng;
 | 
				
			||||||
 | 
						size_t num_children;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(e, 0, sizeof(*e));
 | 
				
			||||||
 | 
						e->mpidr = mpidr;
 | 
				
			||||||
 | 
						psci_set_state_locked(e, PSCI_STATE_OFF);
 | 
				
			||||||
 | 
						e->parent = parent;
 | 
				
			||||||
 | 
						e->level = level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (level == PSCI_AFFINITY_LEVEL_0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_children = soc_psci_ops.children_at_level(level, mpidr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (num_children == 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ng = &e->children;
 | 
				
			||||||
 | 
						ng->num = num_children;
 | 
				
			||||||
 | 
						ng->nodes = malloc(ng->num * sizeof(struct psci_node));
 | 
				
			||||||
 | 
						if (ng->nodes == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_DEBUG, "PSCI: Allocation failure at level %d\n",
 | 
				
			||||||
 | 
								level);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Switch to next level below. */
 | 
				
			||||||
 | 
						level = psci_level_below(level);
 | 
				
			||||||
 | 
						mpidr_inc = mpidr_mask(!!(level == PSCI_AFFINITY_LEVEL_3),
 | 
				
			||||||
 | 
									!!(level == PSCI_AFFINITY_LEVEL_2),
 | 
				
			||||||
 | 
									!!(level == PSCI_AFFINITY_LEVEL_1),
 | 
				
			||||||
 | 
									!!(level == PSCI_AFFINITY_LEVEL_0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ng->num; i++) {
 | 
				
			||||||
 | 
							struct psci_node *c = &ng->nodes[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Recursively initialize the nodes. */
 | 
				
			||||||
 | 
							if (psci_init_node(c, e, level, mpidr))
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							mpidr += mpidr_inc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t psci_count_children(struct psci_node *e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						size_t count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e->level == PSCI_AFFINITY_LEVEL_0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						count = e->children.num;
 | 
				
			||||||
 | 
						for (i = 0; i < e->children.num; i++)
 | 
				
			||||||
 | 
							count +=  psci_count_children(&e->children.nodes[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t psci_write_nodes(struct psci_node *e, size_t index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Recursively save node pointers in array. Node pointers are
 | 
				
			||||||
 | 
						 * ordered in ascending mpidr and descending level within same mpidr.
 | 
				
			||||||
 | 
						 * i.e. each node is saved in depth-first order of the tree.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (e->level != PSCI_AFFINITY_ROOT) {
 | 
				
			||||||
 | 
							psci_nodes[index] = e;
 | 
				
			||||||
 | 
							index++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (e->level == PSCI_AFFINITY_LEVEL_0)
 | 
				
			||||||
 | 
							return index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < e->children.num; i++)
 | 
				
			||||||
 | 
							index = psci_write_nodes(&e->children.nodes[i], index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int psci_allocate_nodes(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int level;
 | 
				
			||||||
 | 
						size_t num_children;
 | 
				
			||||||
 | 
						uint64_t mpidr;
 | 
				
			||||||
 | 
						struct psci_node *e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mpidr = 0;
 | 
				
			||||||
 | 
						level = PSCI_AFFINITY_ROOT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find where the root should start. */
 | 
				
			||||||
 | 
						while (psci_level_below(level) >= PSCI_AFFINITY_LEVEL_0) {
 | 
				
			||||||
 | 
							num_children = soc_psci_ops.children_at_level(level, mpidr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (num_children == 0) {
 | 
				
			||||||
 | 
								printk(BIOS_ERR, "PSCI: No children at level %d!\n",
 | 
				
			||||||
 | 
									level);
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* The root starts where the affinity levels branch. */
 | 
				
			||||||
 | 
							if (num_children > 1)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							level = psci_level_below(level);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (psci_init_node(&psci_root, NULL, level, mpidr)) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "PSCI init node failure.\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_children = psci_count_children(&psci_root);
 | 
				
			||||||
 | 
						/* Count the root node if isn't a fake node. */
 | 
				
			||||||
 | 
						if (psci_root.level != PSCI_AFFINITY_ROOT)
 | 
				
			||||||
 | 
							num_children++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						psci_nodes = malloc(num_children * sizeof(void *));
 | 
				
			||||||
 | 
						psci_num_nodes = num_children;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (psci_nodes == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "PSCI node pointer array failure.\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_children = psci_write_nodes(&psci_root, 0);
 | 
				
			||||||
 | 
						if (num_children != psci_num_nodes) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "Wrong nodes written: %zd vs %zd.\n",
 | 
				
			||||||
 | 
								num_children, psci_num_nodes);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * By default all nodes are set to PSCI_STATE_OFF. In order not
 | 
				
			||||||
 | 
						 * to race with other CPUs turning themselves off set the BSPs
 | 
				
			||||||
 | 
						 * affinity node to ON.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						e = node_self();
 | 
				
			||||||
 | 
						if (e == NULL) {
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "No PSCI node for BSP.\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						psci_set_state_locked(e, PSCI_STATE_ON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void psci_init(void)
 | 
					void psci_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cpu_info *ci;
 | 
						struct cpu_action action = {
 | 
				
			||||||
	uint64_t mpidr;
 | 
							.run = &psci_link_cpu_info,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set this CPUs MPIDR clearing the bits that are not per-cpu. */
 | 
						if (psci_allocate_nodes()) {
 | 
				
			||||||
	ci = cpu_info();
 | 
							printk(BIOS_ERR, "PSCI support not enabled.\n");
 | 
				
			||||||
	mpidr = raw_read_mpidr_el1();
 | 
					 | 
				
			||||||
	mpidr &= ~(1ULL << 31); /* RES1 */
 | 
					 | 
				
			||||||
	mpidr &= ~(1ULL << 30); /* U */
 | 
					 | 
				
			||||||
	mpidr &= ~(1ULL << 24); /* MT */
 | 
					 | 
				
			||||||
	psci_state[ci->id].mpidr = mpidr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!cpu_is_bsp())
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (arch_run_on_all_cpus_async(&action))
 | 
				
			||||||
 | 
							printk(BIOS_ERR, "Error linking cpu_info to PSCI nodes.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Register PSCI handlers. */
 | 
						/* Register PSCI handlers. */
 | 
				
			||||||
	if (smc_register_range(PSCI_CPU_OFF64, PSCI_CPU_ON64, &psci_handler))
 | 
						if (smc_register_range(PSCI_CPU_OFF64, PSCI_CPU_ON64, &psci_handler))
 | 
				
			||||||
		printk(BIOS_ERR, "Couldn't register PSCI handler.\n");
 | 
							printk(BIOS_ERR, "Couldn't register PSCI handler.\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
void psci_turn_on_self(void *entry, void *arg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct exc_state state;
 | 
					 | 
				
			||||||
	int target_el;
 | 
					 | 
				
			||||||
	struct cpu_info *ci = cpu_info();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	psci_lock();
 | 
					 | 
				
			||||||
	psci_cpu_set_state_locked(ci->id, PSCI_CPU_STATE_ON);
 | 
					 | 
				
			||||||
	psci_unlock();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Target EL is determined if HVC is enabled or not. */
 | 
					 | 
				
			||||||
	target_el = (raw_read_scr_el3() & SCR_HVC_ENABLE) ? EL2 : EL1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memset(&state, 0, sizeof(state));
 | 
					 | 
				
			||||||
	state.elx.spsr = get_eret_el(target_el, SPSR_USE_H);
 | 
					 | 
				
			||||||
	transition_with_entry(entry, arg, &state);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,18 @@
 | 
				
			|||||||
#include <stddef.h>
 | 
					#include <stddef.h>
 | 
				
			||||||
#include "secmon.h"
 | 
					#include "secmon.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Save initial secmon params per CPU to handle turn up. */
 | 
				
			||||||
 | 
					static struct secmon_params *init_params[CONFIG_MAX_CPUS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void start_up_cpu(void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct secmon_params *params = init_params[cpu_info()->id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (params == NULL)
 | 
				
			||||||
 | 
							psci_turn_off_self();
 | 
				
			||||||
 | 
						psci_turn_on_self(params->entry, params->arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void cpu_init(int bsp)
 | 
					static void cpu_init(int bsp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cpu_info *ci = cpu_info();
 | 
						struct cpu_info *ci = cpu_info();
 | 
				
			||||||
@@ -43,17 +55,26 @@ static void cpu_init(int bsp)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void secmon_init(struct secmon_params *params, int bsp)
 | 
					static void secmon_init(struct secmon_params *params, int bsp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct cpu_action action = {
 | 
				
			||||||
 | 
							.run = start_up_cpu,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	exception_hwinit();
 | 
						exception_hwinit();
 | 
				
			||||||
	cpu_init(bsp);
 | 
						cpu_init(bsp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init_params[cpu_info()->id] = params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!cpu_is_bsp())
 | 
				
			||||||
 | 
							secmon_wait_for_action();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	smc_init();
 | 
						smc_init();
 | 
				
			||||||
	psci_init();
 | 
						psci_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Turn on CPU if params are not NULL. */
 | 
						arch_run_on_all_cpus_async(&action);
 | 
				
			||||||
	if (params != NULL)
 | 
					 | 
				
			||||||
		psci_turn_on_self(params->entry, params->arg);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	secmon_wait_for_action();
 | 
						printk(BIOS_ERR, "CPU turn on failed for BSP.\n");
 | 
				
			||||||
 | 
						while (1)
 | 
				
			||||||
 | 
							;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void secmon_wait_for_action(void)
 | 
					void secmon_wait_for_action(void)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,7 +140,7 @@ static struct exception_handler smc_handler32 = {
 | 
				
			|||||||
	.handler = &smc_handler,
 | 
						.handler = &smc_handler,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void smc_init(void)
 | 
					static void enable_smc(void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32_t scr;
 | 
						uint32_t scr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -149,9 +149,15 @@ void smc_init(void)
 | 
				
			|||||||
	scr &= ~(SCR_SMC_MASK);
 | 
						scr &= ~(SCR_SMC_MASK);
 | 
				
			||||||
	scr |= SCR_SMC_ENABLE;
 | 
						scr |= SCR_SMC_ENABLE;
 | 
				
			||||||
	raw_write_scr_el3(scr);
 | 
						raw_write_scr_el3(scr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cpu_is_bsp())
 | 
					void smc_init(void)
 | 
				
			||||||
		return;
 | 
					{
 | 
				
			||||||
 | 
						struct cpu_action action = {
 | 
				
			||||||
 | 
							.run = enable_smc,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						arch_run_on_all_cpus_async(&action);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Register SMC handlers. */
 | 
						/* Register SMC handlers. */
 | 
				
			||||||
	exception_handler_register(EXC_VID_LOW64_SYNC, &smc_handler64);
 | 
						exception_handler_register(EXC_VID_LOW64_SYNC, &smc_handler64);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,8 @@
 | 
				
			|||||||
#ifndef __ARCH_PSCI_H__
 | 
					#ifndef __ARCH_PSCI_H__
 | 
				
			||||||
#define __ARCH_PSCI_H__
 | 
					#define __ARCH_PSCI_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <arch/cpu.h>
 | 
				
			||||||
#include <arch/smc.h>
 | 
					#include <arch/smc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Return Values */
 | 
					/* Return Values */
 | 
				
			||||||
@@ -35,6 +37,85 @@ enum {
 | 
				
			|||||||
	PSCI_RET_DISABLED = -8,
 | 
						PSCI_RET_DISABLED = -8,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Generic PSCI state. */
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						PSCI_STATE_OFF = 0,
 | 
				
			||||||
 | 
						PSCI_STATE_ON_PENDING,
 | 
				
			||||||
 | 
						PSCI_STATE_ON,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Affinity level support. */
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						PSCI_AFFINITY_LEVEL_0,
 | 
				
			||||||
 | 
						PSCI_AFFINITY_LEVEL_1,
 | 
				
			||||||
 | 
						PSCI_AFFINITY_LEVEL_2,
 | 
				
			||||||
 | 
						PSCI_AFFINITY_LEVEL_3,
 | 
				
			||||||
 | 
						PSCI_AFFINITY_ROOT,
 | 
				
			||||||
 | 
						PSCI_AFFINITY_LEVEL_HIGHEST = PSCI_AFFINITY_ROOT,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int psci_level_below(int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return level - 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_cpu_state {
 | 
				
			||||||
 | 
						struct cpu_info *ci;
 | 
				
			||||||
 | 
						void *entry;
 | 
				
			||||||
 | 
						void *arg;
 | 
				
			||||||
 | 
						/* Ancestor of target to update state in CPU_ON case. */
 | 
				
			||||||
 | 
						struct psci_node *ancestor;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_node_group {
 | 
				
			||||||
 | 
						size_t num;
 | 
				
			||||||
 | 
						struct psci_node *nodes;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_node {
 | 
				
			||||||
 | 
						uint64_t mpidr;
 | 
				
			||||||
 | 
						/* Affinity level of node. */
 | 
				
			||||||
 | 
						int level;
 | 
				
			||||||
 | 
						/* Generic power state of this entity. */
 | 
				
			||||||
 | 
						int state;
 | 
				
			||||||
 | 
						/* The SoC can stash its own state accounting in here. */
 | 
				
			||||||
 | 
						int soc_state;
 | 
				
			||||||
 | 
						/* Parent of curernt entity. */
 | 
				
			||||||
 | 
						struct psci_node *parent;
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * CPUs are leaves in the tree. They don't have children. The
 | 
				
			||||||
 | 
						 * CPU-specific bits of storage can be shared with the children
 | 
				
			||||||
 | 
						 * storage.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct psci_node_group children;
 | 
				
			||||||
 | 
							struct psci_cpu_state cpu_state;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct psci_node *psci_node_parent(const struct psci_node *n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return n->parent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int psci_root_node(const struct psci_node *n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return psci_node_parent(n) == NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_soc_ops {
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Return number of entities one level below given parent affinitly
 | 
				
			||||||
 | 
						 * level and mpidr.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						size_t (*children_at_level)(int parent_level, uint64_t mpidr);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Each SoC needs to provide the functions in the psci_soc_ops structure. */
 | 
				
			||||||
 | 
					extern struct psci_soc_ops soc_psci_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* PSCI Functions. */
 | 
					/* PSCI Functions. */
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	/* 32-bit System level functions. */
 | 
						/* 32-bit System level functions. */
 | 
				
			||||||
@@ -111,5 +192,6 @@ void psci_init(void);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Turn on the current CPU within the PSCI subsystem. */
 | 
					/* Turn on the current CPU within the PSCI subsystem. */
 | 
				
			||||||
void psci_turn_on_self(void *entry, void *arg);
 | 
					void psci_turn_on_self(void *entry, void *arg);
 | 
				
			||||||
 | 
					int psci_turn_off_self(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* __ARCH_PSCI_H__ */
 | 
					#endif /* __ARCH_PSCI_H__ */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,6 +88,7 @@ ramstage-y += ../tegra/usb.c
 | 
				
			|||||||
ramstage-$(CONFIG_ARCH_USE_SECURE_MONITOR) += secmon.c
 | 
					ramstage-$(CONFIG_ARCH_USE_SECURE_MONITOR) += secmon.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu_lib.S
 | 
					secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu_lib.S
 | 
				
			||||||
 | 
					secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += psci.c
 | 
				
			||||||
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += uart.c
 | 
					secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += uart.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
modules_arm-y += monotonic_timer.c
 | 
					modules_arm-y += monotonic_timer.c
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								src/soc/nvidia/tegra132/psci.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/soc/nvidia/tegra132/psci.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the coreboot project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright 2014 Google Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; version 2 of the License.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program; if not, write to the Free Software
 | 
				
			||||||
 | 
					 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <arch/psci.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t children_at_level(int parent_level, uint64_t mpidr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mpidr != 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* T132 just has 2 cores. 0. Level 1 has 2 children at level 0. */
 | 
				
			||||||
 | 
						switch (parent_level) {
 | 
				
			||||||
 | 
						case PSCI_AFFINITY_ROOT:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						case PSCI_AFFINITY_LEVEL_3:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						case PSCI_AFFINITY_LEVEL_2:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						case PSCI_AFFINITY_LEVEL_1:
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						case PSCI_AFFINITY_LEVEL_0:
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct psci_soc_ops soc_psci_ops = {
 | 
				
			||||||
 | 
						.children_at_level = &children_at_level,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Reference in New Issue
	
	Block a user