162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP powerdomain control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2007-2011 Nokia Corporation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Written by Paul Walmsley 962306a36Sopenharmony_ci * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> 1062306a36Sopenharmony_ci * State counting code by Tero Kristo <tero.kristo@nokia.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#undef DEBUG 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/cpu_pm.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <trace/events/power.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "cm2xxx_3xxx.h" 2462306a36Sopenharmony_ci#include "prcm44xx.h" 2562306a36Sopenharmony_ci#include "cm44xx.h" 2662306a36Sopenharmony_ci#include "prm2xxx_3xxx.h" 2762306a36Sopenharmony_ci#include "prm44xx.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/cpu.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "powerdomain.h" 3262306a36Sopenharmony_ci#include "clockdomain.h" 3362306a36Sopenharmony_ci#include "voltage.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "soc.h" 3662306a36Sopenharmony_ci#include "pm.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define PWRDM_TRACE_STATES_FLAG (1<<31) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void pwrdms_save_context(void); 4162306a36Sopenharmony_cistatic void pwrdms_restore_context(void); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cienum { 4462306a36Sopenharmony_ci PWRDM_STATE_NOW = 0, 4562306a36Sopenharmony_ci PWRDM_STATE_PREV, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Types of sleep_switch used internally in omap_set_pwrdm_state() 5062306a36Sopenharmony_ci * and its associated static functions 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * XXX Better documentation is needed here 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define ALREADYACTIVE_SWITCH 0 5562306a36Sopenharmony_ci#define FORCEWAKEUP_SWITCH 1 5662306a36Sopenharmony_ci#define LOWPOWERSTATE_SWITCH 2 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* pwrdm_list contains all registered struct powerdomains */ 5962306a36Sopenharmony_cistatic LIST_HEAD(pwrdm_list); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct pwrdm_ops *arch_pwrdm; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Private functions */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct powerdomain *_pwrdm_lookup(const char *name) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct powerdomain *pwrdm, *temp_pwrdm; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci pwrdm = NULL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { 7262306a36Sopenharmony_ci if (!strcmp(name, temp_pwrdm->name)) { 7362306a36Sopenharmony_ci pwrdm = temp_pwrdm; 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return pwrdm; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * _pwrdm_register - register a powerdomain 8362306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to register 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Adds a powerdomain to the internal powerdomain list. Returns 8662306a36Sopenharmony_ci * -EINVAL if given a null pointer, -EEXIST if a powerdomain is 8762306a36Sopenharmony_ci * already registered by the provided name, or 0 upon success. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic int _pwrdm_register(struct powerdomain *pwrdm) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int i; 9262306a36Sopenharmony_ci struct voltagedomain *voltdm; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!pwrdm || !pwrdm->name) 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (cpu_is_omap44xx() && 9862306a36Sopenharmony_ci pwrdm->prcm_partition == OMAP4430_INVALID_PRCM_PARTITION) { 9962306a36Sopenharmony_ci pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n", 10062306a36Sopenharmony_ci pwrdm->name); 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (_pwrdm_lookup(pwrdm->name)) 10562306a36Sopenharmony_ci return -EEXIST; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_has_voltdm) 10862306a36Sopenharmony_ci if (!arch_pwrdm->pwrdm_has_voltdm()) 10962306a36Sopenharmony_ci goto skip_voltdm; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci voltdm = voltdm_lookup(pwrdm->voltdm.name); 11262306a36Sopenharmony_ci if (!voltdm) { 11362306a36Sopenharmony_ci pr_err("powerdomain: %s: voltagedomain %s does not exist\n", 11462306a36Sopenharmony_ci pwrdm->name, pwrdm->voltdm.name); 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci pwrdm->voltdm.ptr = voltdm; 11862306a36Sopenharmony_ci INIT_LIST_HEAD(&pwrdm->voltdm_node); 11962306a36Sopenharmony_ciskip_voltdm: 12062306a36Sopenharmony_ci spin_lock_init(&pwrdm->_lock); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci list_add(&pwrdm->node, &pwrdm_list); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Initialize the powerdomain's state counter */ 12562306a36Sopenharmony_ci for (i = 0; i < PWRDM_MAX_PWRSTS; i++) 12662306a36Sopenharmony_ci pwrdm->state_counter[i] = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci pwrdm->ret_logic_off_counter = 0; 12962306a36Sopenharmony_ci for (i = 0; i < pwrdm->banks; i++) 13062306a36Sopenharmony_ci pwrdm->ret_mem_off_counter[i] = 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition) 13362306a36Sopenharmony_ci arch_pwrdm->pwrdm_wait_transition(pwrdm); 13462306a36Sopenharmony_ci pwrdm->state = pwrdm_read_pwrst(pwrdm); 13562306a36Sopenharmony_ci pwrdm->state_counter[pwrdm->state] = 1; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci pr_debug("powerdomain: registered %s\n", pwrdm->name); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void _update_logic_membank_counters(struct powerdomain *pwrdm) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci u8 prev_logic_pwrst, prev_mem_pwrst; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm); 14862306a36Sopenharmony_ci if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) && 14962306a36Sopenharmony_ci (prev_logic_pwrst == PWRDM_POWER_OFF)) 15062306a36Sopenharmony_ci pwrdm->ret_logic_off_counter++; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (i = 0; i < pwrdm->banks; i++) { 15362306a36Sopenharmony_ci prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) && 15662306a36Sopenharmony_ci (prev_mem_pwrst == PWRDM_POWER_OFF)) 15762306a36Sopenharmony_ci pwrdm->ret_mem_off_counter[i]++; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci int prev, next, state, trace_state = 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (pwrdm == NULL) 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci state = pwrdm_read_pwrst(pwrdm); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci switch (flag) { 17262306a36Sopenharmony_ci case PWRDM_STATE_NOW: 17362306a36Sopenharmony_ci prev = pwrdm->state; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci case PWRDM_STATE_PREV: 17662306a36Sopenharmony_ci prev = pwrdm_read_prev_pwrst(pwrdm); 17762306a36Sopenharmony_ci if (prev >= 0 && pwrdm->state != prev) 17862306a36Sopenharmony_ci pwrdm->state_counter[prev]++; 17962306a36Sopenharmony_ci if (prev == PWRDM_POWER_RET) 18062306a36Sopenharmony_ci _update_logic_membank_counters(pwrdm); 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * If the power domain did not hit the desired state, 18362306a36Sopenharmony_ci * generate a trace event with both the desired and hit states 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci next = pwrdm_read_next_pwrst(pwrdm); 18662306a36Sopenharmony_ci if (next != prev) { 18762306a36Sopenharmony_ci trace_state = (PWRDM_TRACE_STATES_FLAG | 18862306a36Sopenharmony_ci ((next & OMAP_POWERSTATE_MASK) << 8) | 18962306a36Sopenharmony_ci ((prev & OMAP_POWERSTATE_MASK) << 0)); 19062306a36Sopenharmony_ci trace_power_domain_target(pwrdm->name, 19162306a36Sopenharmony_ci trace_state, 19262306a36Sopenharmony_ci raw_smp_processor_id()); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci return -EINVAL; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (state != prev) 20062306a36Sopenharmony_ci pwrdm->state_counter[state]++; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci pm_dbg_update_time(pwrdm, prev); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci pwrdm->state = state; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci pwrdm_clear_all_prev_pwrst(pwrdm); 21262306a36Sopenharmony_ci _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV); 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/** 22362306a36Sopenharmony_ci * _pwrdm_save_clkdm_state_and_activate - prepare for power state change 22462306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to operate on 22562306a36Sopenharmony_ci * @curr_pwrst: current power state of @pwrdm 22662306a36Sopenharmony_ci * @pwrst: power state to switch to 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * Determine whether the powerdomain needs to be turned on before 22962306a36Sopenharmony_ci * attempting to switch power states. Called by 23062306a36Sopenharmony_ci * omap_set_pwrdm_state(). NOTE that if the powerdomain contains 23162306a36Sopenharmony_ci * multiple clockdomains, this code assumes that the first clockdomain 23262306a36Sopenharmony_ci * supports software-supervised wakeup mode - potentially a problem. 23362306a36Sopenharmony_ci * Returns the power state switch mode currently in use (see the 23462306a36Sopenharmony_ci * "Types of sleep_switch" comment above). 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm, 23762306a36Sopenharmony_ci u8 curr_pwrst, u8 pwrst) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u8 sleep_switch; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (curr_pwrst < PWRDM_POWER_ON) { 24262306a36Sopenharmony_ci if (curr_pwrst > pwrst && 24362306a36Sopenharmony_ci pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE && 24462306a36Sopenharmony_ci arch_pwrdm->pwrdm_set_lowpwrstchange) { 24562306a36Sopenharmony_ci sleep_switch = LOWPOWERSTATE_SWITCH; 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci clkdm_deny_idle_nolock(pwrdm->pwrdm_clkdms[0]); 24862306a36Sopenharmony_ci sleep_switch = FORCEWAKEUP_SWITCH; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci sleep_switch = ALREADYACTIVE_SWITCH; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return sleep_switch; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change 25962306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to operate on 26062306a36Sopenharmony_ci * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate() 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Restore the clockdomain state perturbed by 26362306a36Sopenharmony_ci * _pwrdm_save_clkdm_state_and_activate(), and call the power state 26462306a36Sopenharmony_ci * bookkeeping code. Called by omap_set_pwrdm_state(). NOTE that if 26562306a36Sopenharmony_ci * the powerdomain contains multiple clockdomains, this assumes that 26662306a36Sopenharmony_ci * the first associated clockdomain supports either 26762306a36Sopenharmony_ci * hardware-supervised idle control in the register, or 26862306a36Sopenharmony_ci * software-supervised sleep. No return value. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm, 27162306a36Sopenharmony_ci u8 sleep_switch) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci switch (sleep_switch) { 27462306a36Sopenharmony_ci case FORCEWAKEUP_SWITCH: 27562306a36Sopenharmony_ci clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case LOWPOWERSTATE_SWITCH: 27862306a36Sopenharmony_ci if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE && 27962306a36Sopenharmony_ci arch_pwrdm->pwrdm_set_lowpwrstchange) 28062306a36Sopenharmony_ci arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm); 28162306a36Sopenharmony_ci pwrdm_state_switch_nolock(pwrdm); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* Public functions */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/** 28962306a36Sopenharmony_ci * pwrdm_register_platform_funcs - register powerdomain implementation fns 29062306a36Sopenharmony_ci * @po: func pointers for arch specific implementations 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Register the list of function pointers used to implement the 29362306a36Sopenharmony_ci * powerdomain functions on different OMAP SoCs. Should be called 29462306a36Sopenharmony_ci * before any other pwrdm_register*() function. Returns -EINVAL if 29562306a36Sopenharmony_ci * @po is null, -EEXIST if platform functions have already been 29662306a36Sopenharmony_ci * registered, or 0 upon success. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ciint pwrdm_register_platform_funcs(struct pwrdm_ops *po) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (!po) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (arch_pwrdm) 30462306a36Sopenharmony_ci return -EEXIST; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci arch_pwrdm = po; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/** 31262306a36Sopenharmony_ci * pwrdm_register_pwrdms - register SoC powerdomains 31362306a36Sopenharmony_ci * @ps: pointer to an array of struct powerdomain to register 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Register the powerdomains available on a particular OMAP SoC. Must 31662306a36Sopenharmony_ci * be called after pwrdm_register_platform_funcs(). May be called 31762306a36Sopenharmony_ci * multiple times. Returns -EACCES if called before 31862306a36Sopenharmony_ci * pwrdm_register_platform_funcs(); -EINVAL if the argument @ps is 31962306a36Sopenharmony_ci * null; or 0 upon success. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ciint pwrdm_register_pwrdms(struct powerdomain **ps) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct powerdomain **p = NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!arch_pwrdm) 32662306a36Sopenharmony_ci return -EEXIST; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!ps) 32962306a36Sopenharmony_ci return -EINVAL; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci for (p = ps; *p; p++) 33262306a36Sopenharmony_ci _pwrdm_register(*p); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci switch (cmd) { 34062306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 34162306a36Sopenharmony_ci if (enable_off_mode) 34262306a36Sopenharmony_ci pwrdms_save_context(); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 34562306a36Sopenharmony_ci if (enable_off_mode) 34662306a36Sopenharmony_ci pwrdms_restore_context(); 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return NOTIFY_OK; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/** 35462306a36Sopenharmony_ci * pwrdm_complete_init - set up the powerdomain layer 35562306a36Sopenharmony_ci * 35662306a36Sopenharmony_ci * Do whatever is necessary to initialize registered powerdomains and 35762306a36Sopenharmony_ci * powerdomain code. Currently, this programs the next power state 35862306a36Sopenharmony_ci * for each powerdomain to ON. This prevents powerdomains from 35962306a36Sopenharmony_ci * unexpectedly losing context or entering high wakeup latency modes 36062306a36Sopenharmony_ci * with non-power-management-enabled kernels. Must be called after 36162306a36Sopenharmony_ci * pwrdm_register_pwrdms(). Returns -EACCES if called before 36262306a36Sopenharmony_ci * pwrdm_register_pwrdms(), or 0 upon success. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ciint pwrdm_complete_init(void) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct powerdomain *temp_p; 36762306a36Sopenharmony_ci static struct notifier_block nb; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (list_empty(&pwrdm_list)) 37062306a36Sopenharmony_ci return -EACCES; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci list_for_each_entry(temp_p, &pwrdm_list, node) 37362306a36Sopenharmony_ci pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Only AM43XX can lose pwrdm context during rtc-ddr suspend */ 37662306a36Sopenharmony_ci if (soc_is_am43xx()) { 37762306a36Sopenharmony_ci nb.notifier_call = cpu_notifier; 37862306a36Sopenharmony_ci cpu_pm_register_notifier(&nb); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * pwrdm_lock - acquire a Linux spinlock on a powerdomain 38662306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to lock 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * Acquire the powerdomain spinlock on @pwrdm. No return value. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_civoid pwrdm_lock(struct powerdomain *pwrdm) 39162306a36Sopenharmony_ci __acquires(&pwrdm->_lock) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * pwrdm_unlock - release a Linux spinlock on a powerdomain 39862306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to unlock 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Release the powerdomain spinlock on @pwrdm. No return value. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_civoid pwrdm_unlock(struct powerdomain *pwrdm) 40362306a36Sopenharmony_ci __releases(&pwrdm->_lock) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/** 40962306a36Sopenharmony_ci * pwrdm_lookup - look up a powerdomain by name, return a pointer 41062306a36Sopenharmony_ci * @name: name of powerdomain 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * Find a registered powerdomain by its name @name. Returns a pointer 41362306a36Sopenharmony_ci * to the struct powerdomain if found, or NULL otherwise. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistruct powerdomain *pwrdm_lookup(const char *name) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct powerdomain *pwrdm; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!name) 42062306a36Sopenharmony_ci return NULL; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci pwrdm = _pwrdm_lookup(name); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return pwrdm; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/** 42862306a36Sopenharmony_ci * pwrdm_for_each - call function on each registered clockdomain 42962306a36Sopenharmony_ci * @fn: callback function * 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Call the supplied function @fn for each registered powerdomain. 43262306a36Sopenharmony_ci * The callback function @fn can return anything but 0 to bail out 43362306a36Sopenharmony_ci * early from the iterator. Returns the last return value of the 43462306a36Sopenharmony_ci * callback function, which should be 0 for success or anything else 43562306a36Sopenharmony_ci * to indicate failure; or -EINVAL if the function pointer is null. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ciint pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), 43862306a36Sopenharmony_ci void *user) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct powerdomain *temp_pwrdm; 44162306a36Sopenharmony_ci int ret = 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!fn) 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { 44762306a36Sopenharmony_ci ret = (*fn)(temp_pwrdm, user); 44862306a36Sopenharmony_ci if (ret) 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * pwrdm_add_clkdm - add a clockdomain to a powerdomain 45762306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to add the clockdomain to 45862306a36Sopenharmony_ci * @clkdm: struct clockdomain * to associate with a powerdomain 45962306a36Sopenharmony_ci * 46062306a36Sopenharmony_ci * Associate the clockdomain @clkdm with a powerdomain @pwrdm. This 46162306a36Sopenharmony_ci * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if 46262306a36Sopenharmony_ci * presented with invalid pointers; -ENOMEM if memory could not be allocated; 46362306a36Sopenharmony_ci * or 0 upon success. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ciint pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int i; 46862306a36Sopenharmony_ci int ret = -EINVAL; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!pwrdm || !clkdm) 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci pr_debug("powerdomain: %s: associating clockdomain %s\n", 47462306a36Sopenharmony_ci pwrdm->name, clkdm->name); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (i = 0; i < PWRDM_MAX_CLKDMS; i++) { 47762306a36Sopenharmony_ci if (!pwrdm->pwrdm_clkdms[i]) 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci#ifdef DEBUG 48062306a36Sopenharmony_ci if (pwrdm->pwrdm_clkdms[i] == clkdm) { 48162306a36Sopenharmony_ci ret = -EINVAL; 48262306a36Sopenharmony_ci goto pac_exit; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci#endif 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (i == PWRDM_MAX_CLKDMS) { 48862306a36Sopenharmony_ci pr_debug("powerdomain: %s: increase PWRDM_MAX_CLKDMS for clkdm %s\n", 48962306a36Sopenharmony_ci pwrdm->name, clkdm->name); 49062306a36Sopenharmony_ci WARN_ON(1); 49162306a36Sopenharmony_ci ret = -ENOMEM; 49262306a36Sopenharmony_ci goto pac_exit; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci pwrdm->pwrdm_clkdms[i] = clkdm; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = 0; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cipac_exit: 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain 50562306a36Sopenharmony_ci * @pwrdm: struct powerdomain * 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Return the number of controllable memory banks in powerdomain @pwrdm, 50862306a36Sopenharmony_ci * starting with 1. Returns -EINVAL if the powerdomain pointer is null. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ciint pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci if (!pwrdm) 51362306a36Sopenharmony_ci return -EINVAL; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return pwrdm->banks; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/** 51962306a36Sopenharmony_ci * pwrdm_set_next_pwrst - set next powerdomain power state 52062306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to set 52162306a36Sopenharmony_ci * @pwrst: one of the PWRDM_POWER_* macros 52262306a36Sopenharmony_ci * 52362306a36Sopenharmony_ci * Set the powerdomain @pwrdm's next power state to @pwrst. The powerdomain 52462306a36Sopenharmony_ci * may not enter this state immediately if the preconditions for this state 52562306a36Sopenharmony_ci * have not been satisfied. Returns -EINVAL if the powerdomain pointer is 52662306a36Sopenharmony_ci * null or if the power state is invalid for the powerdomin, or returns 0 52762306a36Sopenharmony_ci * upon success. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ciint pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci int ret = -EINVAL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!pwrdm) 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (!(pwrdm->pwrsts & (1 << pwrst))) 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_debug("powerdomain: %s: setting next powerstate to %0x\n", 54062306a36Sopenharmony_ci pwrdm->name, pwrst); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) { 54362306a36Sopenharmony_ci /* Trace the pwrdm desired target state */ 54462306a36Sopenharmony_ci trace_power_domain_target(pwrdm->name, pwrst, 54562306a36Sopenharmony_ci raw_smp_processor_id()); 54662306a36Sopenharmony_ci /* Program the pwrdm desired target state */ 54762306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return ret; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/** 55462306a36Sopenharmony_ci * pwrdm_read_next_pwrst - get next powerdomain power state 55562306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get power state 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * Return the powerdomain @pwrdm's next power state. Returns -EINVAL 55862306a36Sopenharmony_ci * if the powerdomain pointer is null or returns the next power state 55962306a36Sopenharmony_ci * upon success. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ciint pwrdm_read_next_pwrst(struct powerdomain *pwrdm) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int ret = -EINVAL; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (!pwrdm) 56662306a36Sopenharmony_ci return -EINVAL; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst) 56962306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/** 57562306a36Sopenharmony_ci * pwrdm_read_pwrst - get current powerdomain power state 57662306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get power state 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * Return the powerdomain @pwrdm's current power state. Returns -EINVAL 57962306a36Sopenharmony_ci * if the powerdomain pointer is null or returns the current power state 58062306a36Sopenharmony_ci * upon success. Note that if the power domain only supports the ON state 58162306a36Sopenharmony_ci * then just return ON as the current state. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ciint pwrdm_read_pwrst(struct powerdomain *pwrdm) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int ret = -EINVAL; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (!pwrdm) 58862306a36Sopenharmony_ci return -EINVAL; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (pwrdm->pwrsts == PWRSTS_ON) 59162306a36Sopenharmony_ci return PWRDM_POWER_ON; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_pwrst) 59462306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_pwrst(pwrdm); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/** 60062306a36Sopenharmony_ci * pwrdm_read_prev_pwrst - get previous powerdomain power state 60162306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get previous power state 60262306a36Sopenharmony_ci * 60362306a36Sopenharmony_ci * Return the powerdomain @pwrdm's previous power state. Returns -EINVAL 60462306a36Sopenharmony_ci * if the powerdomain pointer is null or returns the previous power state 60562306a36Sopenharmony_ci * upon success. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ciint pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci int ret = -EINVAL; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (!pwrdm) 61262306a36Sopenharmony_ci return -EINVAL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_pwrst) 61562306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/** 62162306a36Sopenharmony_ci * pwrdm_set_logic_retst - set powerdomain logic power state upon retention 62262306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to set 62362306a36Sopenharmony_ci * @pwrst: one of the PWRDM_POWER_* macros 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * Set the next power state @pwrst that the logic portion of the 62662306a36Sopenharmony_ci * powerdomain @pwrdm will enter when the powerdomain enters retention. 62762306a36Sopenharmony_ci * This will be either RETENTION or OFF, if supported. Returns 62862306a36Sopenharmony_ci * -EINVAL if the powerdomain pointer is null or the target power 62962306a36Sopenharmony_ci * state is not supported, or returns 0 upon success. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ciint pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci int ret = -EINVAL; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!pwrdm) 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst))) 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci pr_debug("powerdomain: %s: setting next logic powerstate to %0x\n", 64262306a36Sopenharmony_ci pwrdm->name, pwrst); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst) 64562306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/** 65162306a36Sopenharmony_ci * pwrdm_set_mem_onst - set memory power state while powerdomain ON 65262306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to set 65362306a36Sopenharmony_ci * @bank: memory bank number to set (0-3) 65462306a36Sopenharmony_ci * @pwrst: one of the PWRDM_POWER_* macros 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * Set the next power state @pwrst that memory bank @bank of the 65762306a36Sopenharmony_ci * powerdomain @pwrdm will enter when the powerdomain enters the ON 65862306a36Sopenharmony_ci * state. @bank will be a number from 0 to 3, and represents different 65962306a36Sopenharmony_ci * types of memory, depending on the powerdomain. Returns -EINVAL if 66062306a36Sopenharmony_ci * the powerdomain pointer is null or the target power state is not 66162306a36Sopenharmony_ci * supported for this memory bank, -EEXIST if the target memory 66262306a36Sopenharmony_ci * bank does not exist or is not controllable, or returns 0 upon 66362306a36Sopenharmony_ci * success. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ciint pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci int ret = -EINVAL; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!pwrdm) 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (pwrdm->banks < (bank + 1)) 67362306a36Sopenharmony_ci return -EEXIST; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst))) 67662306a36Sopenharmony_ci return -EINVAL; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n", 67962306a36Sopenharmony_ci pwrdm->name, bank, pwrst); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst) 68262306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return ret; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/** 68862306a36Sopenharmony_ci * pwrdm_set_mem_retst - set memory power state while powerdomain in RET 68962306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to set 69062306a36Sopenharmony_ci * @bank: memory bank number to set (0-3) 69162306a36Sopenharmony_ci * @pwrst: one of the PWRDM_POWER_* macros 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Set the next power state @pwrst that memory bank @bank of the 69462306a36Sopenharmony_ci * powerdomain @pwrdm will enter when the powerdomain enters the 69562306a36Sopenharmony_ci * RETENTION state. Bank will be a number from 0 to 3, and represents 69662306a36Sopenharmony_ci * different types of memory, depending on the powerdomain. @pwrst 69762306a36Sopenharmony_ci * will be either RETENTION or OFF, if supported. Returns -EINVAL if 69862306a36Sopenharmony_ci * the powerdomain pointer is null or the target power state is not 69962306a36Sopenharmony_ci * supported for this memory bank, -EEXIST if the target memory 70062306a36Sopenharmony_ci * bank does not exist or is not controllable, or returns 0 upon 70162306a36Sopenharmony_ci * success. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ciint pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci int ret = -EINVAL; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!pwrdm) 70862306a36Sopenharmony_ci return -EINVAL; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (pwrdm->banks < (bank + 1)) 71162306a36Sopenharmony_ci return -EEXIST; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst))) 71462306a36Sopenharmony_ci return -EINVAL; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n", 71762306a36Sopenharmony_ci pwrdm->name, bank, pwrst); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst) 72062306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/** 72662306a36Sopenharmony_ci * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state 72762306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get current logic retention power state 72862306a36Sopenharmony_ci * 72962306a36Sopenharmony_ci * Return the power state that the logic portion of powerdomain @pwrdm 73062306a36Sopenharmony_ci * will enter when the powerdomain enters retention. Returns -EINVAL 73162306a36Sopenharmony_ci * if the powerdomain pointer is null or returns the logic retention 73262306a36Sopenharmony_ci * power state upon success. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ciint pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci int ret = -EINVAL; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!pwrdm) 73962306a36Sopenharmony_ci return -EINVAL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst) 74262306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return ret; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci/** 74862306a36Sopenharmony_ci * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state 74962306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get previous logic power state 75062306a36Sopenharmony_ci * 75162306a36Sopenharmony_ci * Return the powerdomain @pwrdm's previous logic power state. Returns 75262306a36Sopenharmony_ci * -EINVAL if the powerdomain pointer is null or returns the previous 75362306a36Sopenharmony_ci * logic power state upon success. 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_ciint pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int ret = -EINVAL; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!pwrdm) 76062306a36Sopenharmony_ci return -EINVAL; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst) 76362306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return ret; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/** 76962306a36Sopenharmony_ci * pwrdm_read_logic_retst - get next powerdomain logic power state 77062306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get next logic power state 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * Return the powerdomain pwrdm's logic power state. Returns -EINVAL 77362306a36Sopenharmony_ci * if the powerdomain pointer is null or returns the next logic 77462306a36Sopenharmony_ci * power state upon success. 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_ciint pwrdm_read_logic_retst(struct powerdomain *pwrdm) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci int ret = -EINVAL; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (!pwrdm) 78162306a36Sopenharmony_ci return -EINVAL; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst) 78462306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return ret; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/** 79062306a36Sopenharmony_ci * pwrdm_read_mem_pwrst - get current memory bank power state 79162306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get current memory bank power state 79262306a36Sopenharmony_ci * @bank: memory bank number (0-3) 79362306a36Sopenharmony_ci * 79462306a36Sopenharmony_ci * Return the powerdomain @pwrdm's current memory power state for bank 79562306a36Sopenharmony_ci * @bank. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if 79662306a36Sopenharmony_ci * the target memory bank does not exist or is not controllable, or 79762306a36Sopenharmony_ci * returns the current memory power state upon success. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ciint pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci int ret = -EINVAL; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!pwrdm) 80462306a36Sopenharmony_ci return ret; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (pwrdm->banks < (bank + 1)) 80762306a36Sopenharmony_ci return ret; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) 81062306a36Sopenharmony_ci bank = 1; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_pwrst) 81362306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_mem_pwrst(pwrdm, bank); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return ret; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci/** 81962306a36Sopenharmony_ci * pwrdm_read_prev_mem_pwrst - get previous memory bank power state 82062306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get previous memory bank power state 82162306a36Sopenharmony_ci * @bank: memory bank number (0-3) 82262306a36Sopenharmony_ci * 82362306a36Sopenharmony_ci * Return the powerdomain @pwrdm's previous memory power state for 82462306a36Sopenharmony_ci * bank @bank. Returns -EINVAL if the powerdomain pointer is null, 82562306a36Sopenharmony_ci * -EEXIST if the target memory bank does not exist or is not 82662306a36Sopenharmony_ci * controllable, or returns the previous memory power state upon 82762306a36Sopenharmony_ci * success. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ciint pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci int ret = -EINVAL; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (!pwrdm) 83462306a36Sopenharmony_ci return ret; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (pwrdm->banks < (bank + 1)) 83762306a36Sopenharmony_ci return ret; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) 84062306a36Sopenharmony_ci bank = 1; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_mem_pwrst) 84362306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_prev_mem_pwrst(pwrdm, bank); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return ret; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/** 84962306a36Sopenharmony_ci * pwrdm_read_mem_retst - get next memory bank power state 85062306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to get mext memory bank power state 85162306a36Sopenharmony_ci * @bank: memory bank number (0-3) 85262306a36Sopenharmony_ci * 85362306a36Sopenharmony_ci * Return the powerdomain pwrdm's next memory power state for bank 85462306a36Sopenharmony_ci * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if 85562306a36Sopenharmony_ci * the target memory bank does not exist or is not controllable, or 85662306a36Sopenharmony_ci * returns the next memory power state upon success. 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_ciint pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci int ret = -EINVAL; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (!pwrdm) 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (pwrdm->banks < (bank + 1)) 86662306a36Sopenharmony_ci return ret; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst) 86962306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return ret; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/** 87562306a36Sopenharmony_ci * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm 87662306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to clear 87762306a36Sopenharmony_ci * 87862306a36Sopenharmony_ci * Clear the powerdomain's previous power state register @pwrdm. 87962306a36Sopenharmony_ci * Clears the entire register, including logic and memory bank 88062306a36Sopenharmony_ci * previous power states. Returns -EINVAL if the powerdomain pointer 88162306a36Sopenharmony_ci * is null, or returns 0 upon success. 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ciint pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci int ret = -EINVAL; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (!pwrdm) 88862306a36Sopenharmony_ci return ret; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* 89162306a36Sopenharmony_ci * XXX should get the powerdomain's current state here; 89262306a36Sopenharmony_ci * warn & fail if it is not ON. 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci pr_debug("powerdomain: %s: clearing previous power state reg\n", 89662306a36Sopenharmony_ci pwrdm->name); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst) 89962306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return ret; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/** 90562306a36Sopenharmony_ci * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm 90662306a36Sopenharmony_ci * @pwrdm: struct powerdomain * 90762306a36Sopenharmony_ci * 90862306a36Sopenharmony_ci * Enable automatic context save-and-restore upon power state change 90962306a36Sopenharmony_ci * for some devices in the powerdomain @pwrdm. Warning: this only 91062306a36Sopenharmony_ci * affects a subset of devices in a powerdomain; check the TRM 91162306a36Sopenharmony_ci * closely. Returns -EINVAL if the powerdomain pointer is null or if 91262306a36Sopenharmony_ci * the powerdomain does not support automatic save-and-restore, or 91362306a36Sopenharmony_ci * returns 0 upon success. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ciint pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci int ret = -EINVAL; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (!pwrdm) 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) 92362306a36Sopenharmony_ci return ret; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar) 92862306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return ret; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci/** 93462306a36Sopenharmony_ci * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm 93562306a36Sopenharmony_ci * @pwrdm: struct powerdomain * 93662306a36Sopenharmony_ci * 93762306a36Sopenharmony_ci * Disable automatic context save-and-restore upon power state change 93862306a36Sopenharmony_ci * for some devices in the powerdomain @pwrdm. Warning: this only 93962306a36Sopenharmony_ci * affects a subset of devices in a powerdomain; check the TRM 94062306a36Sopenharmony_ci * closely. Returns -EINVAL if the powerdomain pointer is null or if 94162306a36Sopenharmony_ci * the powerdomain does not support automatic save-and-restore, or 94262306a36Sopenharmony_ci * returns 0 upon success. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ciint pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci int ret = -EINVAL; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!pwrdm) 94962306a36Sopenharmony_ci return ret; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar) 95762306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci/** 96362306a36Sopenharmony_ci * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR 96462306a36Sopenharmony_ci * @pwrdm: struct powerdomain * 96562306a36Sopenharmony_ci * 96662306a36Sopenharmony_ci * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore 96762306a36Sopenharmony_ci * for some devices, or 0 if it does not. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cibool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ciint pwrdm_state_switch_nolock(struct powerdomain *pwrdm) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci int ret; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (!pwrdm || !arch_pwrdm) 97962306a36Sopenharmony_ci return -EINVAL; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci ret = arch_pwrdm->pwrdm_wait_transition(pwrdm); 98262306a36Sopenharmony_ci if (!ret) 98362306a36Sopenharmony_ci ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return ret; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ciint __deprecated pwrdm_state_switch(struct powerdomain *pwrdm) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci int ret; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci pwrdm_lock(pwrdm); 99362306a36Sopenharmony_ci ret = pwrdm_state_switch_nolock(pwrdm); 99462306a36Sopenharmony_ci pwrdm_unlock(pwrdm); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return ret; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ciint pwrdm_pre_transition(struct powerdomain *pwrdm) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci if (pwrdm) 100262306a36Sopenharmony_ci _pwrdm_pre_transition_cb(pwrdm, NULL); 100362306a36Sopenharmony_ci else 100462306a36Sopenharmony_ci pwrdm_for_each(_pwrdm_pre_transition_cb, NULL); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciint pwrdm_post_transition(struct powerdomain *pwrdm) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci if (pwrdm) 101262306a36Sopenharmony_ci _pwrdm_post_transition_cb(pwrdm, NULL); 101362306a36Sopenharmony_ci else 101462306a36Sopenharmony_ci pwrdm_for_each(_pwrdm_post_transition_cb, NULL); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/** 102062306a36Sopenharmony_ci * pwrdm_get_valid_lp_state() - Find best match deep power state 102162306a36Sopenharmony_ci * @pwrdm: power domain for which we want to find best match 102262306a36Sopenharmony_ci * @is_logic_state: Are we looking for logic state match here? Should 102362306a36Sopenharmony_ci * be one of PWRDM_xxx macro values 102462306a36Sopenharmony_ci * @req_state: requested power state 102562306a36Sopenharmony_ci * 102662306a36Sopenharmony_ci * Returns: closest match for requested power state. default fallback 102762306a36Sopenharmony_ci * is RET for logic state and ON for power state. 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * This does a search from the power domain data looking for the 103062306a36Sopenharmony_ci * closest valid power domain state that the hardware can achieve. 103162306a36Sopenharmony_ci * PRCM definitions for PWRSTCTRL allows us to program whatever 103262306a36Sopenharmony_ci * configuration we'd like, and PRCM will actually attempt such 103362306a36Sopenharmony_ci * a transition, however if the powerdomain does not actually support it, 103462306a36Sopenharmony_ci * we endup with a hung system. The valid power domain states are already 103562306a36Sopenharmony_ci * available in our powerdomain data files. So this function tries to do 103662306a36Sopenharmony_ci * the following: 103762306a36Sopenharmony_ci * a) find if we have an exact match to the request - no issues. 103862306a36Sopenharmony_ci * b) else find if a deeper power state is possible. 103962306a36Sopenharmony_ci * c) failing which, it tries to find closest higher power state for the 104062306a36Sopenharmony_ci * request. 104162306a36Sopenharmony_ci */ 104262306a36Sopenharmony_ciu8 pwrdm_get_valid_lp_state(struct powerdomain *pwrdm, 104362306a36Sopenharmony_ci bool is_logic_state, u8 req_state) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci u8 pwrdm_states = is_logic_state ? pwrdm->pwrsts_logic_ret : 104662306a36Sopenharmony_ci pwrdm->pwrsts; 104762306a36Sopenharmony_ci /* For logic, ret is highest and others, ON is highest */ 104862306a36Sopenharmony_ci u8 default_pwrst = is_logic_state ? PWRDM_POWER_RET : PWRDM_POWER_ON; 104962306a36Sopenharmony_ci u8 new_pwrst; 105062306a36Sopenharmony_ci bool found; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* If it is already supported, nothing to search */ 105362306a36Sopenharmony_ci if (pwrdm_states & BIT(req_state)) 105462306a36Sopenharmony_ci return req_state; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (!req_state) 105762306a36Sopenharmony_ci goto up_search; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * So, we dont have a exact match 106162306a36Sopenharmony_ci * Can we get a deeper power state match? 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ci new_pwrst = req_state - 1; 106462306a36Sopenharmony_ci found = true; 106562306a36Sopenharmony_ci while (!(pwrdm_states & BIT(new_pwrst))) { 106662306a36Sopenharmony_ci /* No match even at OFF? Not available */ 106762306a36Sopenharmony_ci if (new_pwrst == PWRDM_POWER_OFF) { 106862306a36Sopenharmony_ci found = false; 106962306a36Sopenharmony_ci break; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci new_pwrst--; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (found) 107562306a36Sopenharmony_ci goto done; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ciup_search: 107862306a36Sopenharmony_ci /* OK, no deeper ones, can we get a higher match? */ 107962306a36Sopenharmony_ci new_pwrst = req_state + 1; 108062306a36Sopenharmony_ci while (!(pwrdm_states & BIT(new_pwrst))) { 108162306a36Sopenharmony_ci if (new_pwrst > PWRDM_POWER_ON) { 108262306a36Sopenharmony_ci WARN(1, "powerdomain: %s: Fix max powerstate to ON\n", 108362306a36Sopenharmony_ci pwrdm->name); 108462306a36Sopenharmony_ci return PWRDM_POWER_ON; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (new_pwrst == default_pwrst) 108862306a36Sopenharmony_ci break; 108962306a36Sopenharmony_ci new_pwrst++; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_cidone: 109262306a36Sopenharmony_ci return new_pwrst; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/** 109662306a36Sopenharmony_ci * omap_set_pwrdm_state - change a powerdomain's current power state 109762306a36Sopenharmony_ci * @pwrdm: struct powerdomain * to change the power state of 109862306a36Sopenharmony_ci * @pwrst: power state to change to 109962306a36Sopenharmony_ci * 110062306a36Sopenharmony_ci * Change the current hardware power state of the powerdomain 110162306a36Sopenharmony_ci * represented by @pwrdm to the power state represented by @pwrst. 110262306a36Sopenharmony_ci * Returns -EINVAL if @pwrdm is null or invalid or if the 110362306a36Sopenharmony_ci * powerdomain's current power state could not be read, or returns 0 110462306a36Sopenharmony_ci * upon success or if @pwrdm does not support @pwrst or any 110562306a36Sopenharmony_ci * lower-power state. XXX Should not return 0 if the @pwrdm does not 110662306a36Sopenharmony_ci * support @pwrst or any lower-power state: this should be an error. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ciint omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci u8 next_pwrst, sleep_switch; 111162306a36Sopenharmony_ci int curr_pwrst; 111262306a36Sopenharmony_ci int ret = 0; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!pwrdm || IS_ERR(pwrdm)) 111562306a36Sopenharmony_ci return -EINVAL; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci while (!(pwrdm->pwrsts & (1 << pwrst))) { 111862306a36Sopenharmony_ci if (pwrst == PWRDM_POWER_OFF) 111962306a36Sopenharmony_ci return ret; 112062306a36Sopenharmony_ci pwrst--; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci pwrdm_lock(pwrdm); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci curr_pwrst = pwrdm_read_pwrst(pwrdm); 112662306a36Sopenharmony_ci if (curr_pwrst < 0) { 112762306a36Sopenharmony_ci ret = -EINVAL; 112862306a36Sopenharmony_ci goto osps_out; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci next_pwrst = pwrdm_read_next_pwrst(pwrdm); 113262306a36Sopenharmony_ci if (curr_pwrst == pwrst && next_pwrst == pwrst) 113362306a36Sopenharmony_ci goto osps_out; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst, 113662306a36Sopenharmony_ci pwrst); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ret = pwrdm_set_next_pwrst(pwrdm, pwrst); 113962306a36Sopenharmony_ci if (ret) 114062306a36Sopenharmony_ci pr_err("%s: unable to set power state of powerdomain: %s\n", 114162306a36Sopenharmony_ci __func__, pwrdm->name); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci _pwrdm_restore_clkdm_state(pwrdm, sleep_switch); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ciosps_out: 114662306a36Sopenharmony_ci pwrdm_unlock(pwrdm); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci return ret; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/** 115262306a36Sopenharmony_ci * pwrdm_save_context - save powerdomain registers 115362306a36Sopenharmony_ci * 115462306a36Sopenharmony_ci * Register state is going to be lost due to a suspend or hibernate 115562306a36Sopenharmony_ci * event. Save the powerdomain registers. 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_cistatic int pwrdm_save_context(struct powerdomain *pwrdm, void *unused) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_save_context) 116062306a36Sopenharmony_ci arch_pwrdm->pwrdm_save_context(pwrdm); 116162306a36Sopenharmony_ci return 0; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci/** 116562306a36Sopenharmony_ci * pwrdm_save_context - restore powerdomain registers 116662306a36Sopenharmony_ci * 116762306a36Sopenharmony_ci * Restore powerdomain control registers after a suspend or resume 116862306a36Sopenharmony_ci * event. 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_cistatic int pwrdm_restore_context(struct powerdomain *pwrdm, void *unused) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci if (arch_pwrdm && arch_pwrdm->pwrdm_restore_context) 117362306a36Sopenharmony_ci arch_pwrdm->pwrdm_restore_context(pwrdm); 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic void pwrdms_save_context(void) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci pwrdm_for_each(pwrdm_save_context, NULL); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic void pwrdms_restore_context(void) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci pwrdm_for_each(pwrdm_restore_context, NULL); 118562306a36Sopenharmony_ci} 1186