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