/******************************************************************************* * Handler routine to turn a cluster of clusters on. It takes care or any * generic, arch. or platform specific setup required. * TODO: Split this code across separate handlers for each type of setup? ******************************************************************************/ static int psci_afflvl2_on(unsigned long target_cpu, aff_map_node_t *system_node, unsigned long ns_entrypoint, unsigned long context_id) { unsigned long psci_entrypoint; /* Cannot go beyond affinity level 2 in this psci imp. */ assert(system_node->level == MPIDR_AFFLVL2); /* * There is no generic and arch. specific system management * required */ /* State management: Is not required while turning a system on */ if (!psci_plat_pm_ops->affinst_on) return PSCI_E_SUCCESS; /* * Plat. management: Give the platform the current state * of the target cpu to allow it to perform the necessary * steps to power on. */ psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; return psci_plat_pm_ops->affinst_on(target_cpu, psci_entrypoint, ns_entrypoint, system_node->level, psci_get_phys_state(system_node)); }
static unsigned int psci_afflvl2_on_finish(aff_map_node_t *system_node) { unsigned int plat_state; /* Cannot go beyond this affinity level */ assert(system_node->level == MPIDR_AFFLVL2); if (!psci_plat_pm_ops->affinst_on_finish) return PSCI_E_SUCCESS; /* * Currently, there are no architectural actions to perform * at the system level. */ /* * Plat. management: Perform the platform specific actions * as per the old state of the cluster e.g. enabling * coherency at the interconnect depends upon the state with * which this cluster was powered up. If anything goes wrong * then assert as there is no way to recover from this * situation. */ plat_state = psci_get_phys_state(system_node); return psci_plat_pm_ops->affinst_on_finish(read_mpidr_el1(), system_node->level, plat_state); }
static int psci_afflvl2_off(unsigned long mpidr, aff_map_node *system_node) { int rc = PSCI_E_SUCCESS; unsigned int plat_state; /* Cannot go beyond this level */ assert(system_node->level == MPIDR_AFFLVL2); /* State management: Decrement the system reference count */ psci_set_state(system_node, PSCI_STATE_OFF); /* * Keep the physical state of the system handy to decide what * action needs to be taken */ plat_state = psci_get_phys_state(system_node); /* No arch. and generic bookeeping to do here currently */ /* * Plat. Management : Allow the platform to do its bookeeping * at this affinity level */ if (psci_plat_pm_ops->affinst_off) rc = psci_plat_pm_ops->affinst_off(mpidr, system_node->level, plat_state); return rc; }
static unsigned int psci_afflvl1_suspend_finish(aff_map_node_t *cluster_node) { unsigned int plat_state, rc = PSCI_E_SUCCESS; assert(cluster_node->level == MPIDR_AFFLVL1); /* * Plat. management: Perform the platform specific actions * as per the old state of the cluster e.g. enabling * coherency at the interconnect depends upon the state with * which this cluster was powered up. If anything goes wrong * then assert as there is no way to recover from this * situation. */ if (psci_plat_pm_ops->affinst_suspend_finish) { /* Get the physical state of this cpu */ plat_state = psci_get_phys_state(cluster_node); rc = psci_plat_pm_ops->affinst_suspend_finish(read_mpidr_el1(), cluster_node->level, plat_state); assert(rc == PSCI_E_SUCCESS); } return rc; }
static int psci_afflvl1_off(aff_map_node_t *cluster_node) { int rc; /* Sanity check the cluster level */ assert(cluster_node->level == MPIDR_AFFLVL1); if (!psci_plat_pm_ops->affinst_off) return PSCI_E_SUCCESS; /* * Plat. Management. Allow the platform to do its cluster * specific bookeeping e.g. turn off interconnect coherency, * program the power controller etc. */ rc = psci_plat_pm_ops->affinst_off(read_mpidr_el1(), cluster_node->level, psci_get_phys_state(cluster_node)); /* * Arch. Management. Flush all levels of caches to PoC if * the cluster is to be shutdown. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); return rc; }
static int psci_afflvl2_off(aff_map_node_t *system_node) { int rc; /* Cannot go beyond this level */ assert(system_node->level == MPIDR_AFFLVL2); /* * Keep the physical state of the system handy to decide what * action needs to be taken */ if (!psci_plat_pm_ops->affinst_off) return PSCI_E_SUCCESS; /* * Plat. Management : Allow the platform to do its bookeeping * at this affinity level */ rc = psci_plat_pm_ops->affinst_off(read_mpidr_el1(), system_node->level, psci_get_phys_state(system_node)); /* * Arch. Management. Flush all levels of caches to PoC if * the system is to be shutdown. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); return rc; }
static int psci_afflvl2_suspend(aff_map_node_t *system_node, unsigned long ns_entrypoint, unsigned long context_id, unsigned int power_state) { unsigned int plat_state; unsigned long psci_entrypoint; int rc; /* Cannot go beyond this */ assert(system_node->level == MPIDR_AFFLVL2); /* * Keep the physical state of the system handy to decide what * action needs to be taken */ plat_state = psci_get_phys_state(system_node); /* * Plat. Management : Allow the platform to do its bookeeping * at this affinity level */ if (!psci_plat_pm_ops->affinst_suspend) return PSCI_E_SUCCESS; /* * Sending the psci entrypoint is currently redundant * beyond affinity level 0 but one never knows what a * platform might do. Also it allows us to keep the * platform handler prototype the same. */ plat_state = psci_get_phys_state(system_node); psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; rc = psci_plat_pm_ops->affinst_suspend(read_mpidr_el1(), psci_entrypoint, ns_entrypoint, system_node->level, plat_state); /* * Arch. management: Flush all levels of caches to PoC if the * system is to be shutdown. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2); return rc; }
/******************************************************************************* * Handler routine to turn a cpu on. It takes care of any generic, architectural * or platform specific setup required. * TODO: Split this code across separate handlers for each type of setup? ******************************************************************************/ static int psci_afflvl0_on(unsigned long target_cpu, aff_map_node_t *cpu_node, unsigned long ns_entrypoint, unsigned long context_id) { unsigned long psci_entrypoint; uint32_t ns_scr_el3 = read_scr_el3(); uint32_t ns_sctlr_el1 = read_sctlr_el1(); int rc; /* Sanity check to safeguard against data corruption */ assert(cpu_node->level == MPIDR_AFFLVL0); /* * Generic management: Ensure that the cpu is off to be * turned on */ rc = cpu_on_validate_state(cpu_node); if (rc != PSCI_E_SUCCESS) return rc; /* * Call the cpu on handler registered by the Secure Payload Dispatcher * to let it do any bookeeping. If the handler encounters an error, it's * expected to assert within */ if (psci_spd_pm && psci_spd_pm->svc_on) psci_spd_pm->svc_on(target_cpu); /* * Arch. management: Derive the re-entry information for * the non-secure world from the non-secure state from * where this call originated. */ rc = psci_save_ns_entry(target_cpu, ns_entrypoint, context_id, ns_scr_el3, ns_sctlr_el1); if (rc != PSCI_E_SUCCESS) return rc; /* Set the secure world (EL3) re-entry point after BL1 */ psci_entrypoint = (unsigned long) psci_aff_on_finish_entry; if (!psci_plat_pm_ops->affinst_on) return PSCI_E_SUCCESS; /* * Plat. management: Give the platform the current state * of the target cpu to allow it to perform the necessary * steps to power on. */ return psci_plat_pm_ops->affinst_on(target_cpu, psci_entrypoint, ns_entrypoint, cpu_node->level, psci_get_phys_state(cpu_node)); }
/******************************************************************************* * This function is passed an array of pointers to affinity level nodes in the * topology tree for an mpidr. It iterates through the nodes to find the highest * affinity level which is marked as physically powered off. ******************************************************************************/ uint32_t psci_find_max_phys_off_afflvl(uint32_t start_afflvl, uint32_t end_afflvl, aff_map_node_t *mpidr_nodes[]) { uint32_t max_afflvl = PSCI_INVALID_DATA; for (; start_afflvl <= end_afflvl; start_afflvl++) { if (mpidr_nodes[start_afflvl] == NULL) continue; if (psci_get_phys_state(mpidr_nodes[start_afflvl]) == PSCI_STATE_OFF) max_afflvl = start_afflvl; } return max_afflvl; }
/******************************************************************************* * The next three functions implement a handler for each supported affinity * level which is called when that affinity level is turned off. ******************************************************************************/ static int psci_afflvl0_off(aff_map_node_t *cpu_node) { int rc; assert(cpu_node->level == MPIDR_AFFLVL0); /* * Generic management: Get the index for clearing any lingering re-entry * information and allow the secure world to switch itself off */ /* * Call the cpu off handler registered by the Secure Payload Dispatcher * to let it do any bookeeping. Assume that the SPD always reports an * E_DENIED error if SP refuse to power down */ if (psci_spd_pm && psci_spd_pm->svc_off) { rc = psci_spd_pm->svc_off(0); if (rc) return rc; } if (!psci_plat_pm_ops->affinst_off) return PSCI_E_SUCCESS; /* * Plat. management: Perform platform specific actions to turn this * cpu off e.g. exit cpu coherency, program the power controller etc. */ rc = psci_plat_pm_ops->affinst_off(read_mpidr_el1(), cpu_node->level, psci_get_phys_state(cpu_node)); /* * Arch. management. Perform the necessary steps to flush all * cpu caches. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); return rc; }
static int psci_afflvl1_suspend(aff_map_node_t *cluster_node, unsigned long ns_entrypoint, unsigned long context_id, unsigned int power_state) { unsigned int plat_state; unsigned long psci_entrypoint; int rc; /* Sanity check the cluster level */ assert(cluster_node->level == MPIDR_AFFLVL1); if (!psci_plat_pm_ops->affinst_suspend) return PSCI_E_SUCCESS; /* * Plat. Management. Allow the platform to do its cluster specific * bookeeping e.g. turn off interconnect coherency, program the power * controller etc. Sending the psci entrypoint is currently redundant * beyond affinity level 0 but one never knows what a platform might * do. Also it allows us to keep the platform handler prototype the * same. */ plat_state = psci_get_phys_state(cluster_node); psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; rc = psci_plat_pm_ops->affinst_suspend(read_mpidr_el1(), psci_entrypoint, ns_entrypoint, cluster_node->level, plat_state); /* * Arch. management: Flush all levels of caches to PoC if the * cluster is to be shutdown. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1); return rc; }
static int psci_afflvl1_off(unsigned long mpidr, aff_map_node *cluster_node) { int rc = PSCI_E_SUCCESS; unsigned int plat_state; /* Sanity check the cluster level */ assert(cluster_node->level == MPIDR_AFFLVL1); /* State management: Decrement the cluster reference count */ psci_set_state(cluster_node, PSCI_STATE_OFF); /* * Keep the physical state of this cluster handy to decide * what action needs to be taken */ plat_state = psci_get_phys_state(cluster_node); /* * Arch. Management. Flush all levels of caches to PoC if * the cluster is to be shutdown */ if (plat_state == PSCI_STATE_OFF) dcsw_op_all(DCCISW); /* * Plat. Management. Allow the platform to do its cluster * specific bookeeping e.g. turn off interconnect coherency, * program the power controller etc. */ if (psci_plat_pm_ops->affinst_off) rc = psci_plat_pm_ops->affinst_off(mpidr, cluster_node->level, plat_state); return rc; }
/******************************************************************************* * The next three functions implement a handler for each supported affinity * level which is called when that affinity level is about to be suspended. ******************************************************************************/ static int psci_afflvl0_suspend(aff_map_node_t *cpu_node, unsigned long ns_entrypoint, unsigned long context_id, unsigned int power_state) { unsigned long psci_entrypoint; uint32_t ns_scr_el3 = read_scr_el3(); uint32_t ns_sctlr_el1 = read_sctlr_el1(); int rc; /* Sanity check to safeguard against data corruption */ assert(cpu_node->level == MPIDR_AFFLVL0); /* Save PSCI power state parameter for the core in suspend context */ psci_set_suspend_power_state(power_state); /* * Generic management: Store the re-entry information for the non-secure * world and allow the secure world to suspend itself */ /* * Call the cpu suspend handler registered by the Secure Payload * Dispatcher to let it do any bookeeping. If the handler encounters an * error, it's expected to assert within */ if (psci_spd_pm && psci_spd_pm->svc_suspend) psci_spd_pm->svc_suspend(power_state); /* * Generic management: Store the re-entry information for the * non-secure world */ rc = psci_save_ns_entry(read_mpidr_el1(), ns_entrypoint, context_id, ns_scr_el3, ns_sctlr_el1); if (rc != PSCI_E_SUCCESS) return rc; /* Set the secure world (EL3) re-entry point after BL1 */ psci_entrypoint = (unsigned long) psci_aff_suspend_finish_entry; if (!psci_plat_pm_ops->affinst_suspend) return PSCI_E_SUCCESS; /* * Plat. management: Allow the platform to perform the * necessary actions to turn off this cpu e.g. set the * platform defined mailbox with the psci entrypoint, * program the power controller etc. */ rc = psci_plat_pm_ops->affinst_suspend(read_mpidr_el1(), psci_entrypoint, ns_entrypoint, cpu_node->level, psci_get_phys_state(cpu_node)); /* * Arch. management. Perform the necessary steps to flush all * cpu caches. */ psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0); return rc; }
/******************************************************************************* * The next three functions implement a handler for each supported affinity * level which is called when that affinity level is turned off. ******************************************************************************/ static int psci_afflvl0_off(unsigned long mpidr, aff_map_node *cpu_node) { unsigned int index, plat_state; int rc = PSCI_E_SUCCESS; unsigned long sctlr; assert(cpu_node->level == MPIDR_AFFLVL0); /* State management: mark this cpu as turned off */ psci_set_state(cpu_node, PSCI_STATE_OFF); /* * Generic management: Get the index for clearing any lingering re-entry * information and allow the secure world to switch itself off */ /* * Call the cpu off handler registered by the Secure Payload Dispatcher * to let it do any bookeeping. Assume that the SPD always reports an * E_DENIED error if SP refuse to power down */ if (psci_spd_pm && psci_spd_pm->svc_off) { rc = psci_spd_pm->svc_off(0); if (rc) return rc; } index = cpu_node->data; memset(&psci_ns_entry_info[index], 0, sizeof(psci_ns_entry_info[index])); /* * Arch. management. Perform the necessary steps to flush all * cpu caches. * * TODO: This power down sequence varies across cpus so it needs to be * abstracted out on the basis of the MIDR like in cpu_reset_handler(). * Do the bare minimal for the time being. Fix this before porting to * Cortex models. */ sctlr = read_sctlr_el3(); sctlr &= ~SCTLR_C_BIT; write_sctlr_el3(sctlr); /* * CAUTION: This flush to the level of unification makes an assumption * about the cache hierarchy at affinity level 0 (cpu) in the platform. * Ideally the platform should tell psci which levels to flush to exit * coherency. */ dcsw_op_louis(DCCISW); /* * Plat. management: Perform platform specific actions to turn this * cpu off e.g. exit cpu coherency, program the power controller etc. */ if (psci_plat_pm_ops->affinst_off) { /* Get the current physical state of this cpu */ plat_state = psci_get_phys_state(cpu_node); rc = psci_plat_pm_ops->affinst_off(mpidr, cpu_node->level, plat_state); } return rc; }