/*******************************************************************************
 * This function initializes the set of hooks that PSCI invokes as part of power
 * management operation. The power management hooks are expected to be provided
 * by the SPD, after it finishes all its initialization
 ******************************************************************************/
void psci_register_spd_pm_hook(const spd_pm_ops_t *pm)
{
	assert(pm);
	psci_spd_pm = pm;

	if (pm->svc_migrate)
		psci_caps |= define_psci_cap(PSCI_MIG_AARCH64);

	if (pm->svc_migrate_info)
		psci_caps |= define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64)
				| define_psci_cap(PSCI_MIG_INFO_TYPE);
}
/*******************************************************************************
 * This function does the architectural setup and takes the warm boot
 * entry-point `mailbox_ep` as an argument. The function also initializes the
 * power domain topology tree by querying the platform. The power domain nodes
 * higher than the CPU are populated in the array psci_non_cpu_pd_nodes[] and
 * the CPU power domains are populated in psci_cpu_pd_nodes[]. The platform
 * exports its static topology map through the
 * populate_power_domain_topology_tree() API. The algorithm populates the
 * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this
 * topology map.  On a platform that implements two clusters of 2 cpus each,
 * and supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would
 * look like this:
 *
 * ---------------------------------------------------
 * | system node | cluster 0 node  | cluster 1 node  |
 * ---------------------------------------------------
 *
 * And populated psci_cpu_pd_nodes would look like this :
 * <-    cpus cluster0   -><-   cpus cluster1   ->
 * ------------------------------------------------
 * |   CPU 0   |   CPU 1   |   CPU 2   |   CPU 3  |
 * ------------------------------------------------
 ******************************************************************************/
int __init psci_setup(const psci_lib_args_t *lib_args)
{
	const unsigned char *topology_tree;

	assert(VERIFY_PSCI_LIB_ARGS_V1(lib_args));

	/* Do the Architectural initialization */
	psci_arch_setup();

	/* Query the topology map from the platform */
	topology_tree = plat_get_power_domain_tree_desc();

	/* Populate the power domain arrays using the platform topology map */
	populate_power_domain_tree(topology_tree);

	/* Update the CPU limits for each node in psci_non_cpu_pd_nodes */
	psci_update_pwrlvl_limits();

	/* Populate the mpidr field of cpu node for this CPU */
	psci_cpu_pd_nodes[plat_my_core_pos()].mpidr =
		read_mpidr() & MPIDR_AFFINITY_MASK;

	psci_init_req_local_pwr_states();

	/*
	 * Set the requested and target state of this CPU and all the higher
	 * power domain levels for this CPU to run.
	 */
	psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL);

	(void) plat_setup_psci_ops((uintptr_t)lib_args->mailbox_ep,
				   &psci_plat_pm_ops);
	assert(psci_plat_pm_ops != NULL);

	/*
	 * Flush `psci_plat_pm_ops` as it will be accessed by secondary CPUs
	 * during warm boot, possibly before data cache is enabled.
	 */
	psci_flush_dcache_range((uintptr_t)&psci_plat_pm_ops,
					sizeof(psci_plat_pm_ops));

	/* Initialize the psci capability */
	psci_caps = PSCI_GENERIC_CAP;

	if (psci_plat_pm_ops->pwr_domain_off != NULL)
		psci_caps |=  define_psci_cap(PSCI_CPU_OFF);
	if ((psci_plat_pm_ops->pwr_domain_on != NULL) &&
	    (psci_plat_pm_ops->pwr_domain_on_finish != NULL))
		psci_caps |=  define_psci_cap(PSCI_CPU_ON_AARCH64);
	if ((psci_plat_pm_ops->pwr_domain_suspend != NULL) &&
	    (psci_plat_pm_ops->pwr_domain_suspend_finish != NULL)) {
		psci_caps |=  define_psci_cap(PSCI_CPU_SUSPEND_AARCH64);
		if (psci_plat_pm_ops->get_sys_suspend_power_state != NULL)
			psci_caps |=  define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64);
	}
	if (psci_plat_pm_ops->system_off != NULL)
		psci_caps |=  define_psci_cap(PSCI_SYSTEM_OFF);
	if (psci_plat_pm_ops->system_reset != NULL)
		psci_caps |=  define_psci_cap(PSCI_SYSTEM_RESET);
	if (psci_plat_pm_ops->get_node_hw_state != NULL)
		psci_caps |= define_psci_cap(PSCI_NODE_HW_STATE_AARCH64);
	if ((psci_plat_pm_ops->read_mem_protect != NULL) &&
			(psci_plat_pm_ops->write_mem_protect != NULL))
		psci_caps |= define_psci_cap(PSCI_MEM_PROTECT);
	if (psci_plat_pm_ops->mem_protect_chk != NULL)
		psci_caps |= define_psci_cap(PSCI_MEM_CHK_RANGE_AARCH64);
	if (psci_plat_pm_ops->system_reset2 != NULL)
		psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET2_AARCH64);

#if ENABLE_PSCI_STAT
	psci_caps |=  define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64);
	psci_caps |=  define_psci_cap(PSCI_STAT_COUNT_AARCH64);
#endif

	return 0;
}
/*******************************************************************************
 * This function initializes the power domain topology tree by querying the
 * platform. The power domain nodes higher than the CPU are populated in the
 * array psci_non_cpu_pd_nodes[] and the CPU power domains are populated in
 * psci_cpu_pd_nodes[]. The platform exports its static topology map through the
 * populate_power_domain_topology_tree() API. The algorithm populates the
 * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this
 * topology map.  On a platform that implements two clusters of 2 cpus each, and
 * supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would look
 * like this:
 *
 * ---------------------------------------------------
 * | system node | cluster 0 node  | cluster 1 node  |
 * ---------------------------------------------------
 *
 * And populated psci_cpu_pd_nodes would look like this :
 * <-    cpus cluster0   -><-   cpus cluster1   ->
 * ------------------------------------------------
 * |   CPU 0   |   CPU 1   |   CPU 2   |   CPU 3  |
 * ------------------------------------------------
 ******************************************************************************/
int psci_setup(void)
{
	const unsigned char *topology_tree;

	/* Query the topology map from the platform */
	topology_tree = plat_get_power_domain_tree_desc();

	/* Populate the power domain arrays using the platform topology map */
	populate_power_domain_tree(topology_tree);

	/* Update the CPU limits for each node in psci_non_cpu_pd_nodes */
	psci_update_pwrlvl_limits();

	/* Populate the mpidr field of cpu node for this CPU */
	psci_cpu_pd_nodes[plat_my_core_pos()].mpidr =
		read_mpidr() & MPIDR_AFFINITY_MASK;

#if !USE_COHERENT_MEM
	/*
	 * The psci_non_cpu_pd_nodes only needs flushing when it's not allocated in
	 * coherent memory.
	 */
	flush_dcache_range((uintptr_t) &psci_non_cpu_pd_nodes,
			   sizeof(psci_non_cpu_pd_nodes));
#endif

	flush_dcache_range((uintptr_t) &psci_cpu_pd_nodes,
			   sizeof(psci_cpu_pd_nodes));

	psci_init_req_local_pwr_states();

	/*
	 * Set the requested and target state of this CPU and all the higher
	 * power domain levels for this CPU to run.
	 */
	psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL);

	plat_setup_psci_ops((uintptr_t)psci_entrypoint,
					&psci_plat_pm_ops);
	assert(psci_plat_pm_ops);

	/* Initialize the psci capability */
	psci_caps = PSCI_GENERIC_CAP;

	if (psci_plat_pm_ops->pwr_domain_off)
		psci_caps |=  define_psci_cap(PSCI_CPU_OFF);
	if (psci_plat_pm_ops->pwr_domain_on &&
			psci_plat_pm_ops->pwr_domain_on_finish)
		psci_caps |=  define_psci_cap(PSCI_CPU_ON_AARCH64);
	if (psci_plat_pm_ops->pwr_domain_suspend &&
			psci_plat_pm_ops->pwr_domain_suspend_finish) {
		psci_caps |=  define_psci_cap(PSCI_CPU_SUSPEND_AARCH64);
		if (psci_plat_pm_ops->get_sys_suspend_power_state)
			psci_caps |=  define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64);
	}
	if (psci_plat_pm_ops->system_off)
		psci_caps |=  define_psci_cap(PSCI_SYSTEM_OFF);
	if (psci_plat_pm_ops->system_reset)
		psci_caps |=  define_psci_cap(PSCI_SYSTEM_RESET);

	return 0;
}