/******************************************************************************* * This function is passed the target local power states for each power * domain (state_info) between the current CPU domain and its ancestors until * the target power level (end_pwrlvl). * * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it * updates the `last_cpu_in_non_cpu_pd[]` with last power down cpu id. * * This function will only be invoked with data cache enabled and while * powering down a core. ******************************************************************************/ void psci_stats_update_pwr_down(unsigned int end_pwrlvl, const psci_power_state_t *state_info) { int lvl, parent_idx, cpu_idx = plat_my_core_pos(); assert(end_pwrlvl <= PLAT_MAX_PWR_LVL); assert(state_info); parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { /* Break early if the target power state is RUN */ if (is_local_state_run(state_info->pwr_domain_state[lvl])) break; /* * The power domain is entering a low power state, so this is * the last CPU for this power domain */ last_cpu_in_non_cpu_pd[parent_idx] = cpu_idx; parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } }
/******************************************************************************* * PSCI Compatibility helper function to return the 'power_state' parameter of * the PSCI CPU SUSPEND request for the current CPU. Returns PSCI_INVALID_DATA * if not invoked within CPU_SUSPEND for the current CPU. ******************************************************************************/ int psci_get_suspend_powerstate(void) { /* Sanity check to verify that CPU is within CPU_SUSPEND */ if (psci_get_aff_info_state() == AFF_STATE_ON && !is_local_state_run(psci_get_cpu_local_state())) return psci_power_state_compat[plat_my_core_pos()]; return PSCI_INVALID_DATA; }
/****************************************************************************** * This function is passed the local power states requested for each power * domain (state_info) between the current CPU domain and its ancestors until * the target power level (end_pwrlvl). It updates the array of requested power * states with this information. * * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it * retrieves the states requested by all the cpus of which the power domain at * that level is an ancestor. It passes this information to the platform to * coordinate and return the target power state. If the target state for a level * is RUN then subsequent levels are not considered. At the CPU level, state * coordination is not required. Hence, the requested and the target states are * the same. * * The 'state_info' is updated with the target state for each level between the * CPU and the 'end_pwrlvl' and returned to the caller. * * This function will only be invoked with data cache enabled and while * powering down a core. *****************************************************************************/ void psci_do_state_coordination(unsigned int end_pwrlvl, psci_power_state_t *state_info) { unsigned int lvl, parent_idx, cpu_idx = plat_my_core_pos(); unsigned int start_idx, ncpus; plat_local_state_t target_state, *req_states; parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; /* For level 0, the requested state will be equivalent to target state */ for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { /* First update the requested power state */ psci_set_req_local_pwr_state(lvl, cpu_idx, state_info->pwr_domain_state[lvl]); /* Get the requested power states for this power level */ start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx; req_states = psci_get_req_local_pwr_states(lvl, start_idx); /* * Let the platform coordinate amongst the requested states at * this power level and return the target local power state. */ ncpus = psci_non_cpu_pd_nodes[parent_idx].ncpus; target_state = plat_get_target_pwr_state(lvl, req_states, ncpus); state_info->pwr_domain_state[lvl] = target_state; /* Break early if the negotiated target power state is RUN */ if (is_local_state_run(state_info->pwr_domain_state[lvl])) break; parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } /* * This is for cases when we break out of the above loop early because * the target power state is RUN at a power level < end_pwlvl. * We update the requested power state from state_info and then * set the target state as RUN. */ for (lvl = lvl + 1; lvl <= end_pwrlvl; lvl++) { psci_set_req_local_pwr_state(lvl, cpu_idx, state_info->pwr_domain_state[lvl]); state_info->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN; } /* Update the target state in the power domain nodes */ psci_set_target_local_pwr_states(end_pwrlvl, state_info); }
/****************************************************************************** * This functions finds the level of the highest power domain which will be * placed in a low power state during a suspend operation. *****************************************************************************/ unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info) { int i; for (i = PLAT_MAX_PWR_LVL; i >= PSCI_CPU_PWR_LVL; i--) { if (!is_local_state_run(state_info->pwr_domain_state[i])) return i; } return PSCI_INVALID_PWR_LVL; }
/******************************************************************************* * PSCI Compatibility helper function to return the state id encoded in the * 'power_state' parameter of the CPU specified by 'mpidr'. Returns * PSCI_INVALID_DATA if the CPU is not in CPU_SUSPEND. ******************************************************************************/ int psci_get_suspend_stateid_by_mpidr(unsigned long mpidr) { int cpu_idx = plat_core_pos_by_mpidr(mpidr); if (cpu_idx == -1) return PSCI_INVALID_DATA; /* Sanity check to verify that the CPU is in CPU_SUSPEND */ if (psci_get_aff_info_state_by_idx(cpu_idx) == AFF_STATE_ON && !is_local_state_run(psci_get_cpu_local_state_by_idx(cpu_idx))) return psci_get_pstate_id(psci_power_state_compat[cpu_idx]); return PSCI_INVALID_DATA; }
/******************************************************************************* * This function updates the PSCI STATS(residency time and count) for CPU * and NON-CPU power domains. * It is called with caches enabled and locks acquired(for NON-CPU domain) ******************************************************************************/ void psci_stats_update_pwr_up(unsigned int end_pwrlvl, const psci_power_state_t *state_info, unsigned int flags) { int parent_idx, cpu_idx = plat_my_core_pos(); int lvl, stat_idx; plat_local_state_t local_state; unsigned long long pwrup_ts = 0, pwrdn_ts = 0; u_register_t residency; assert(end_pwrlvl <= PLAT_MAX_PWR_LVL); assert(state_info); /* Initialize the residency divisor if not already initialized */ if (!residency_div) { /* Pre-calculate divisor so that it can be directly used to convert time-stamp into microseconds */ residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; assert(residency_div); } /* Get power down time-stamp for current CPU */ PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, cpu_idx, flags, pwrdn_ts); /* In the case of 1st power on just return */ if (!pwrdn_ts) return; /* Get power up time-stamp for current CPU */ PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, cpu_idx, flags, pwrup_ts); /* Get the index into the stats array */ local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL); /* Calculate stats residency */ calc_stat_residency(pwrup_ts, pwrdn_ts, residency); /* Update CPU stats. */ psci_cpu_stat[cpu_idx][stat_idx].residency += residency; psci_cpu_stat[cpu_idx][stat_idx].count++; /* * Check what power domains above CPU were off * prior to this CPU powering on. */ parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node; for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) { local_state = state_info->pwr_domain_state[lvl]; if (is_local_state_run(local_state)) { /* Break early */ break; } assert(last_cpu_in_non_cpu_pd[parent_idx] != -1); /* Get power down time-stamp for last CPU */ PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, last_cpu_in_non_cpu_pd[parent_idx], flags, pwrdn_ts); /* Initialize back to reset value */ last_cpu_in_non_cpu_pd[parent_idx] = -1; /* Get the index into the stats array */ stat_idx = get_stat_idx(local_state, lvl); /* Calculate stats residency */ calc_stat_residency(pwrup_ts, pwrdn_ts, residency); /* Update non cpu stats */ psci_non_cpu_stat[parent_idx][stat_idx].residency += residency; psci_non_cpu_stat[parent_idx][stat_idx].count++; parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node; } }