162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OMAP2/3/4 clockdomain framework functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008-2011 Texas Instruments, Inc.
662306a36Sopenharmony_ci * Copyright (C) 2008-2011 Nokia Corporation
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Written by Paul Walmsley and Jouni Högander
962306a36Sopenharmony_ci * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#undef DEBUG
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/clk.h>
2062306a36Sopenharmony_ci#include <linux/limits.h>
2162306a36Sopenharmony_ci#include <linux/err.h>
2262306a36Sopenharmony_ci#include <linux/clk-provider.h>
2362306a36Sopenharmony_ci#include <linux/cpu_pm.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/io.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/bitops.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "soc.h"
3062306a36Sopenharmony_ci#include "clock.h"
3162306a36Sopenharmony_ci#include "clockdomain.h"
3262306a36Sopenharmony_ci#include "pm.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* clkdm_list contains all registered struct clockdomains */
3562306a36Sopenharmony_cistatic LIST_HEAD(clkdm_list);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* array of clockdomain deps to be added/removed when clkdm in hwsup mode */
3862306a36Sopenharmony_cistatic struct clkdm_autodep *autodeps;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic struct clkdm_ops *arch_clkdm;
4162306a36Sopenharmony_civoid clkdm_save_context(void);
4262306a36Sopenharmony_civoid clkdm_restore_context(void);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Private functions */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct clockdomain *_clkdm_lookup(const char *name)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct clockdomain *clkdm, *temp_clkdm;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!name)
5162306a36Sopenharmony_ci		return NULL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	clkdm = NULL;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
5662306a36Sopenharmony_ci		if (!strcmp(name, temp_clkdm->name)) {
5762306a36Sopenharmony_ci			clkdm = temp_clkdm;
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return clkdm;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * _clkdm_register - register a clockdomain
6762306a36Sopenharmony_ci * @clkdm: struct clockdomain * to register
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Adds a clockdomain to the internal clockdomain list.
7062306a36Sopenharmony_ci * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
7162306a36Sopenharmony_ci * already registered by the provided name, or 0 upon success.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic int _clkdm_register(struct clockdomain *clkdm)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct powerdomain *pwrdm;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!clkdm || !clkdm->name)
7862306a36Sopenharmony_ci		return -EINVAL;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
8162306a36Sopenharmony_ci	if (!pwrdm) {
8262306a36Sopenharmony_ci		pr_err("clockdomain: %s: powerdomain %s does not exist\n",
8362306a36Sopenharmony_ci			clkdm->name, clkdm->pwrdm.name);
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci	clkdm->pwrdm.ptr = pwrdm;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Verify that the clockdomain is not already registered */
8962306a36Sopenharmony_ci	if (_clkdm_lookup(clkdm->name))
9062306a36Sopenharmony_ci		return -EEXIST;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	list_add(&clkdm->node, &clkdm_list);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	pwrdm_add_clkdm(pwrdm, clkdm);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	pr_debug("clockdomain: registered %s\n", clkdm->name);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */
10262306a36Sopenharmony_cistatic struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
10362306a36Sopenharmony_ci					    struct clkdm_dep *deps)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct clkdm_dep *cd;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!clkdm || !deps)
10862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for (cd = deps; cd->clkdm_name; cd++) {
11162306a36Sopenharmony_ci		if (!cd->clkdm && cd->clkdm_name)
11262306a36Sopenharmony_ci			cd->clkdm = _clkdm_lookup(cd->clkdm_name);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (cd->clkdm == clkdm)
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (!cd->clkdm_name)
11962306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return cd;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
12662306a36Sopenharmony_ci * @autodep: struct clkdm_autodep * to resolve
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * Resolve autodep clockdomain names to clockdomain pointers via
12962306a36Sopenharmony_ci * clkdm_lookup() and store the pointers in the autodep structure.  An
13062306a36Sopenharmony_ci * "autodep" is a clockdomain sleep/wakeup dependency that is
13162306a36Sopenharmony_ci * automatically added and removed whenever clocks in the associated
13262306a36Sopenharmony_ci * clockdomain are enabled or disabled (respectively) when the
13362306a36Sopenharmony_ci * clockdomain is in hardware-supervised mode.	Meant to be called
13462306a36Sopenharmony_ci * once at clockdomain layer initialization, since these should remain
13562306a36Sopenharmony_ci * fixed for a particular architecture.  No return value.
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
13862306a36Sopenharmony_ci * opportunity
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic void _autodep_lookup(struct clkdm_autodep *autodep)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct clockdomain *clkdm;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (!autodep)
14562306a36Sopenharmony_ci		return;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	clkdm = clkdm_lookup(autodep->clkdm.name);
14862306a36Sopenharmony_ci	if (!clkdm) {
14962306a36Sopenharmony_ci		pr_err("clockdomain: autodeps: clockdomain %s does not exist\n",
15062306a36Sopenharmony_ci			 autodep->clkdm.name);
15162306a36Sopenharmony_ci		clkdm = ERR_PTR(-ENOENT);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	autodep->clkdm.ptr = clkdm;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
15862306a36Sopenharmony_ci * @clkdm: clockdomain that we are resolving dependencies for
15962306a36Sopenharmony_ci * @clkdm_deps: ptr to array of struct clkdm_deps to resolve
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Iterates through @clkdm_deps, looking up the struct clockdomain named by
16262306a36Sopenharmony_ci * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
16362306a36Sopenharmony_ci * No return value.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic void _resolve_clkdm_deps(struct clockdomain *clkdm,
16662306a36Sopenharmony_ci				struct clkdm_dep *clkdm_deps)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct clkdm_dep *cd;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
17162306a36Sopenharmony_ci		if (cd->clkdm)
17262306a36Sopenharmony_ci			continue;
17362306a36Sopenharmony_ci		cd->clkdm = _clkdm_lookup(cd->clkdm_name);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
17662306a36Sopenharmony_ci		     clkdm->name, cd->clkdm_name);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/**
18162306a36Sopenharmony_ci * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless)
18262306a36Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
18362306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * When the clockdomain represented by @clkdm2 wakes up, wake up
18662306a36Sopenharmony_ci * @clkdm1. Implemented in hardware on the OMAP, this feature is
18762306a36Sopenharmony_ci * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
18862306a36Sopenharmony_ci * Returns -EINVAL if presented with invalid clockdomain pointers,
18962306a36Sopenharmony_ci * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
19062306a36Sopenharmony_ci * success.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_cistatic int _clkdm_add_wkdep(struct clockdomain *clkdm1,
19362306a36Sopenharmony_ci			    struct clockdomain *clkdm2)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct clkdm_dep *cd;
19662306a36Sopenharmony_ci	int ret = 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
19962306a36Sopenharmony_ci		return -EINVAL;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
20262306a36Sopenharmony_ci	if (IS_ERR(cd))
20362306a36Sopenharmony_ci		ret = PTR_ERR(cd);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
20662306a36Sopenharmony_ci		ret = -EINVAL;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (ret) {
20962306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
21062306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
21162306a36Sopenharmony_ci		return ret;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	cd->wkdep_usecount++;
21562306a36Sopenharmony_ci	if (cd->wkdep_usecount == 1) {
21662306a36Sopenharmony_ci		pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
21762306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return ret;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/**
22662306a36Sopenharmony_ci * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless)
22762306a36Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
22862306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
23162306a36Sopenharmony_ci * wakes up.  Returns -EINVAL if presented with invalid clockdomain
23262306a36Sopenharmony_ci * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
23362306a36Sopenharmony_ci * 0 upon success.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int _clkdm_del_wkdep(struct clockdomain *clkdm1,
23662306a36Sopenharmony_ci			    struct clockdomain *clkdm2)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct clkdm_dep *cd;
23962306a36Sopenharmony_ci	int ret = 0;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
24562306a36Sopenharmony_ci	if (IS_ERR(cd))
24662306a36Sopenharmony_ci		ret = PTR_ERR(cd);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
24962306a36Sopenharmony_ci		ret = -EINVAL;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (ret) {
25262306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
25362306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
25462306a36Sopenharmony_ci		return ret;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	cd->wkdep_usecount--;
25862306a36Sopenharmony_ci	if (cd->wkdep_usecount == 0) {
25962306a36Sopenharmony_ci		pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
26062306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return ret;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/**
26962306a36Sopenharmony_ci * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless)
27062306a36Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
27162306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * Prevent @clkdm1 from automatically going inactive (and then to
27462306a36Sopenharmony_ci * retention or off) if @clkdm2 is active.  Returns -EINVAL if
27562306a36Sopenharmony_ci * presented with invalid clockdomain pointers or called on a machine
27662306a36Sopenharmony_ci * that does not support software-configurable hardware sleep
27762306a36Sopenharmony_ci * dependencies, -ENOENT if the specified dependency cannot be set in
27862306a36Sopenharmony_ci * hardware, or 0 upon success.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic int _clkdm_add_sleepdep(struct clockdomain *clkdm1,
28162306a36Sopenharmony_ci			       struct clockdomain *clkdm2)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct clkdm_dep *cd;
28462306a36Sopenharmony_ci	int ret = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
29062306a36Sopenharmony_ci	if (IS_ERR(cd))
29162306a36Sopenharmony_ci		ret = PTR_ERR(cd);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
29462306a36Sopenharmony_ci		ret = -EINVAL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (ret) {
29762306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
29862306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
29962306a36Sopenharmony_ci		return ret;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	cd->sleepdep_usecount++;
30362306a36Sopenharmony_ci	if (cd->sleepdep_usecount == 1) {
30462306a36Sopenharmony_ci		pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
30562306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return ret;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless)
31562306a36Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
31662306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * Allow @clkdm1 to automatically go inactive (and then to retention or
31962306a36Sopenharmony_ci * off), independent of the activity state of @clkdm2.  Returns -EINVAL
32062306a36Sopenharmony_ci * if presented with invalid clockdomain pointers or called on a machine
32162306a36Sopenharmony_ci * that does not support software-configurable hardware sleep dependencies,
32262306a36Sopenharmony_ci * -ENOENT if the specified dependency cannot be cleared in hardware, or
32362306a36Sopenharmony_ci * 0 upon success.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic int _clkdm_del_sleepdep(struct clockdomain *clkdm1,
32662306a36Sopenharmony_ci			       struct clockdomain *clkdm2)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct clkdm_dep *cd;
32962306a36Sopenharmony_ci	int ret = 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
33262306a36Sopenharmony_ci		return -EINVAL;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
33562306a36Sopenharmony_ci	if (IS_ERR(cd))
33662306a36Sopenharmony_ci		ret = PTR_ERR(cd);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
33962306a36Sopenharmony_ci		ret = -EINVAL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (ret) {
34262306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
34362306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
34462306a36Sopenharmony_ci		return ret;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	cd->sleepdep_usecount--;
34862306a36Sopenharmony_ci	if (cd->sleepdep_usecount == 0) {
34962306a36Sopenharmony_ci		pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
35062306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return ret;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/* Public functions */
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/**
36162306a36Sopenharmony_ci * clkdm_register_platform_funcs - register clockdomain implementation fns
36262306a36Sopenharmony_ci * @co: func pointers for arch specific implementations
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci * Register the list of function pointers used to implement the
36562306a36Sopenharmony_ci * clockdomain functions on different OMAP SoCs.  Should be called
36662306a36Sopenharmony_ci * before any other clkdm_register*() function.  Returns -EINVAL if
36762306a36Sopenharmony_ci * @co is null, -EEXIST if platform functions have already been
36862306a36Sopenharmony_ci * registered, or 0 upon success.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_ciint clkdm_register_platform_funcs(struct clkdm_ops *co)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	if (!co)
37362306a36Sopenharmony_ci		return -EINVAL;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (arch_clkdm)
37662306a36Sopenharmony_ci		return -EEXIST;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	arch_clkdm = co;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/**
38462306a36Sopenharmony_ci * clkdm_register_clkdms - register SoC clockdomains
38562306a36Sopenharmony_ci * @cs: pointer to an array of struct clockdomain to register
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * Register the clockdomains available on a particular OMAP SoC.  Must
38862306a36Sopenharmony_ci * be called after clkdm_register_platform_funcs().  May be called
38962306a36Sopenharmony_ci * multiple times.  Returns -EACCES if called before
39062306a36Sopenharmony_ci * clkdm_register_platform_funcs(); -EINVAL if the argument @cs is
39162306a36Sopenharmony_ci * null; or 0 upon success.
39262306a36Sopenharmony_ci */
39362306a36Sopenharmony_ciint clkdm_register_clkdms(struct clockdomain **cs)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct clockdomain **c = NULL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (!arch_clkdm)
39862306a36Sopenharmony_ci		return -EACCES;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (!cs)
40162306a36Sopenharmony_ci		return -EINVAL;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	for (c = cs; *c; c++)
40462306a36Sopenharmony_ci		_clkdm_register(*c);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/**
41062306a36Sopenharmony_ci * clkdm_register_autodeps - register autodeps (if required)
41162306a36Sopenharmony_ci * @ia: pointer to a static array of struct clkdm_autodep to register
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * Register clockdomain "automatic dependencies."  These are
41462306a36Sopenharmony_ci * clockdomain wakeup and sleep dependencies that are automatically
41562306a36Sopenharmony_ci * added whenever the first clock inside a clockdomain is enabled, and
41662306a36Sopenharmony_ci * removed whenever the last clock inside a clockdomain is disabled.
41762306a36Sopenharmony_ci * These are currently only used on OMAP3 devices, and are deprecated,
41862306a36Sopenharmony_ci * since they waste energy.  However, until the OMAP2/3 IP block
41962306a36Sopenharmony_ci * enable/disable sequence can be converted to match the OMAP4
42062306a36Sopenharmony_ci * sequence, they are needed.
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * Must be called only after all of the SoC clockdomains are
42362306a36Sopenharmony_ci * registered, since the function will resolve autodep clockdomain
42462306a36Sopenharmony_ci * names into clockdomain pointers.
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci * The struct clkdm_autodep @ia array must be static, as this function
42762306a36Sopenharmony_ci * does not copy the array elements.
42862306a36Sopenharmony_ci *
42962306a36Sopenharmony_ci * Returns -EACCES if called before any clockdomains have been
43062306a36Sopenharmony_ci * registered, -EINVAL if called with a null @ia argument, -EEXIST if
43162306a36Sopenharmony_ci * autodeps have already been registered, or 0 upon success.
43262306a36Sopenharmony_ci */
43362306a36Sopenharmony_ciint clkdm_register_autodeps(struct clkdm_autodep *ia)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct clkdm_autodep *a = NULL;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (list_empty(&clkdm_list))
43862306a36Sopenharmony_ci		return -EACCES;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!ia)
44162306a36Sopenharmony_ci		return -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (autodeps)
44462306a36Sopenharmony_ci		return -EEXIST;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	autodeps = ia;
44762306a36Sopenharmony_ci	for (a = autodeps; a->clkdm.ptr; a++)
44862306a36Sopenharmony_ci		_autodep_lookup(a);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	switch (cmd) {
45662306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
45762306a36Sopenharmony_ci		if (enable_off_mode)
45862306a36Sopenharmony_ci			clkdm_save_context();
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
46162306a36Sopenharmony_ci		if (enable_off_mode)
46262306a36Sopenharmony_ci			clkdm_restore_context();
46362306a36Sopenharmony_ci		break;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return NOTIFY_OK;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/**
47062306a36Sopenharmony_ci * clkdm_complete_init - set up the clockdomain layer
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * Put all clockdomains into software-supervised mode; PM code should
47362306a36Sopenharmony_ci * later enable hardware-supervised mode as appropriate.  Must be
47462306a36Sopenharmony_ci * called after clkdm_register_clkdms().  Returns -EACCES if called
47562306a36Sopenharmony_ci * before clkdm_register_clkdms(), or 0 upon success.
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_ciint clkdm_complete_init(void)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct clockdomain *clkdm;
48062306a36Sopenharmony_ci	static struct notifier_block nb;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (list_empty(&clkdm_list))
48362306a36Sopenharmony_ci		return -EACCES;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	list_for_each_entry(clkdm, &clkdm_list, node) {
48662306a36Sopenharmony_ci		clkdm_deny_idle(clkdm);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		_resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
48962306a36Sopenharmony_ci		clkdm_clear_all_wkdeps(clkdm);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		_resolve_clkdm_deps(clkdm, clkdm->sleepdep_srcs);
49262306a36Sopenharmony_ci		clkdm_clear_all_sleepdeps(clkdm);
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Only AM43XX can lose clkdm context during rtc-ddr suspend */
49662306a36Sopenharmony_ci	if (soc_is_am43xx()) {
49762306a36Sopenharmony_ci		nb.notifier_call = cpu_notifier;
49862306a36Sopenharmony_ci		cpu_pm_register_notifier(&nb);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * clkdm_lookup - look up a clockdomain by name, return a pointer
50662306a36Sopenharmony_ci * @name: name of clockdomain
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * Find a registered clockdomain by its name @name.  Returns a pointer
50962306a36Sopenharmony_ci * to the struct clockdomain if found, or NULL otherwise.
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_cistruct clockdomain *clkdm_lookup(const char *name)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct clockdomain *clkdm, *temp_clkdm;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (!name)
51662306a36Sopenharmony_ci		return NULL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	clkdm = NULL;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
52162306a36Sopenharmony_ci		if (!strcmp(name, temp_clkdm->name)) {
52262306a36Sopenharmony_ci			clkdm = temp_clkdm;
52362306a36Sopenharmony_ci			break;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return clkdm;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/**
53162306a36Sopenharmony_ci * clkdm_for_each - call function on each registered clockdomain
53262306a36Sopenharmony_ci * @fn: callback function *
53362306a36Sopenharmony_ci *
53462306a36Sopenharmony_ci * Call the supplied function @fn for each registered clockdomain.
53562306a36Sopenharmony_ci * The callback function @fn can return anything but 0 to bail
53662306a36Sopenharmony_ci * out early from the iterator.  The callback function is called with
53762306a36Sopenharmony_ci * the clkdm_mutex held, so no clockdomain structure manipulation
53862306a36Sopenharmony_ci * functions should be called from the callback, although hardware
53962306a36Sopenharmony_ci * clockdomain control functions are fine.  Returns the last return
54062306a36Sopenharmony_ci * value of the callback function, which should be 0 for success or
54162306a36Sopenharmony_ci * anything else to indicate failure; or -EINVAL if the function pointer
54262306a36Sopenharmony_ci * is null.
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_ciint clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user),
54562306a36Sopenharmony_ci			void *user)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct clockdomain *clkdm;
54862306a36Sopenharmony_ci	int ret = 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (!fn)
55162306a36Sopenharmony_ci		return -EINVAL;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	list_for_each_entry(clkdm, &clkdm_list, node) {
55462306a36Sopenharmony_ci		ret = (*fn)(clkdm, user);
55562306a36Sopenharmony_ci		if (ret)
55662306a36Sopenharmony_ci			break;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return ret;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/**
56462306a36Sopenharmony_ci * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
56562306a36Sopenharmony_ci * @clkdm: struct clockdomain *
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * Return a pointer to the struct powerdomain that the specified clockdomain
56862306a36Sopenharmony_ci * @clkdm exists in, or returns NULL if @clkdm is NULL.
56962306a36Sopenharmony_ci */
57062306a36Sopenharmony_cistruct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	if (!clkdm)
57362306a36Sopenharmony_ci		return NULL;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return clkdm->pwrdm.ptr;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/* Hardware clockdomain control */
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/**
58262306a36Sopenharmony_ci * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
58362306a36Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
58462306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
58562306a36Sopenharmony_ci *
58662306a36Sopenharmony_ci * When the clockdomain represented by @clkdm2 wakes up, wake up
58762306a36Sopenharmony_ci * @clkdm1. Implemented in hardware on the OMAP, this feature is
58862306a36Sopenharmony_ci * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
58962306a36Sopenharmony_ci * Returns -EINVAL if presented with invalid clockdomain pointers,
59062306a36Sopenharmony_ci * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
59162306a36Sopenharmony_ci * success.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_ciint clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct clkdm_dep *cd;
59662306a36Sopenharmony_ci	int ret;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
59962306a36Sopenharmony_ci		return -EINVAL;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
60262306a36Sopenharmony_ci	if (IS_ERR(cd))
60362306a36Sopenharmony_ci		return PTR_ERR(cd);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
60662306a36Sopenharmony_ci	ret = _clkdm_add_wkdep(clkdm1, clkdm2);
60762306a36Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return ret;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/**
61362306a36Sopenharmony_ci * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
61462306a36Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
61562306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
61862306a36Sopenharmony_ci * wakes up.  Returns -EINVAL if presented with invalid clockdomain
61962306a36Sopenharmony_ci * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
62062306a36Sopenharmony_ci * 0 upon success.
62162306a36Sopenharmony_ci */
62262306a36Sopenharmony_ciint clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct clkdm_dep *cd;
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
62862306a36Sopenharmony_ci		return -EINVAL;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
63162306a36Sopenharmony_ci	if (IS_ERR(cd))
63262306a36Sopenharmony_ci		return PTR_ERR(cd);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
63562306a36Sopenharmony_ci	ret = _clkdm_del_wkdep(clkdm1, clkdm2);
63662306a36Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return ret;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/**
64262306a36Sopenharmony_ci * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
64362306a36Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
64462306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
64562306a36Sopenharmony_ci *
64662306a36Sopenharmony_ci * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
64762306a36Sopenharmony_ci * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
64862306a36Sopenharmony_ci * if either clockdomain pointer is invalid; or -ENOENT if the hardware
64962306a36Sopenharmony_ci * is incapable.
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * REVISIT: Currently this function only represents software-controllable
65262306a36Sopenharmony_ci * wakeup dependencies.  Wakeup dependencies fixed in hardware are not
65362306a36Sopenharmony_ci * yet handled here.
65462306a36Sopenharmony_ci */
65562306a36Sopenharmony_ciint clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct clkdm_dep *cd;
65862306a36Sopenharmony_ci	int ret = 0;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
66162306a36Sopenharmony_ci		return -EINVAL;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
66462306a36Sopenharmony_ci	if (IS_ERR(cd))
66562306a36Sopenharmony_ci		ret = PTR_ERR(cd);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep)
66862306a36Sopenharmony_ci		ret = -EINVAL;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (ret) {
67162306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
67262306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
67362306a36Sopenharmony_ci		return ret;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* XXX It's faster to return the wkdep_usecount */
67762306a36Sopenharmony_ci	return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci/**
68162306a36Sopenharmony_ci * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm
68262306a36Sopenharmony_ci * @clkdm: struct clockdomain * to remove all wakeup dependencies from
68362306a36Sopenharmony_ci *
68462306a36Sopenharmony_ci * Remove all inter-clockdomain wakeup dependencies that could cause
68562306a36Sopenharmony_ci * @clkdm to wake.  Intended to be used during boot to initialize the
68662306a36Sopenharmony_ci * PRCM to a known state, after all clockdomains are put into swsup idle
68762306a36Sopenharmony_ci * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
68862306a36Sopenharmony_ci * 0 upon success.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_ciint clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	if (!clkdm)
69362306a36Sopenharmony_ci		return -EINVAL;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps)
69662306a36Sopenharmony_ci		return -EINVAL;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return arch_clkdm->clkdm_clear_all_wkdeps(clkdm);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/**
70262306a36Sopenharmony_ci * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
70362306a36Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
70462306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
70562306a36Sopenharmony_ci *
70662306a36Sopenharmony_ci * Prevent @clkdm1 from automatically going inactive (and then to
70762306a36Sopenharmony_ci * retention or off) if @clkdm2 is active.  Returns -EINVAL if
70862306a36Sopenharmony_ci * presented with invalid clockdomain pointers or called on a machine
70962306a36Sopenharmony_ci * that does not support software-configurable hardware sleep
71062306a36Sopenharmony_ci * dependencies, -ENOENT if the specified dependency cannot be set in
71162306a36Sopenharmony_ci * hardware, or 0 upon success.
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_ciint clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct clkdm_dep *cd;
71662306a36Sopenharmony_ci	int ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
71962306a36Sopenharmony_ci		return -EINVAL;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
72262306a36Sopenharmony_ci	if (IS_ERR(cd))
72362306a36Sopenharmony_ci		return PTR_ERR(cd);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
72662306a36Sopenharmony_ci	ret = _clkdm_add_sleepdep(clkdm1, clkdm2);
72762306a36Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return ret;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/**
73362306a36Sopenharmony_ci * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1
73462306a36Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
73562306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * Allow @clkdm1 to automatically go inactive (and then to retention or
73862306a36Sopenharmony_ci * off), independent of the activity state of @clkdm2.  Returns -EINVAL
73962306a36Sopenharmony_ci * if presented with invalid clockdomain pointers or called on a machine
74062306a36Sopenharmony_ci * that does not support software-configurable hardware sleep dependencies,
74162306a36Sopenharmony_ci * -ENOENT if the specified dependency cannot be cleared in hardware, or
74262306a36Sopenharmony_ci * 0 upon success.
74362306a36Sopenharmony_ci */
74462306a36Sopenharmony_ciint clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct clkdm_dep *cd;
74762306a36Sopenharmony_ci	int ret;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
75062306a36Sopenharmony_ci		return -EINVAL;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
75362306a36Sopenharmony_ci	if (IS_ERR(cd))
75462306a36Sopenharmony_ci		return PTR_ERR(cd);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
75762306a36Sopenharmony_ci	ret = _clkdm_del_sleepdep(clkdm1, clkdm2);
75862306a36Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return ret;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/**
76462306a36Sopenharmony_ci * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1
76562306a36Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
76662306a36Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
76762306a36Sopenharmony_ci *
76862306a36Sopenharmony_ci * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will
76962306a36Sopenharmony_ci * not be allowed to automatically go inactive if @clkdm2 is active;
77062306a36Sopenharmony_ci * 0 if @clkdm1's automatic power state inactivity transition is independent
77162306a36Sopenharmony_ci * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called
77262306a36Sopenharmony_ci * on a machine that does not support software-configurable hardware sleep
77362306a36Sopenharmony_ci * dependencies; or -ENOENT if the hardware is incapable.
77462306a36Sopenharmony_ci *
77562306a36Sopenharmony_ci * REVISIT: Currently this function only represents software-controllable
77662306a36Sopenharmony_ci * sleep dependencies.	Sleep dependencies fixed in hardware are not
77762306a36Sopenharmony_ci * yet handled here.
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_ciint clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct clkdm_dep *cd;
78262306a36Sopenharmony_ci	int ret = 0;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!clkdm1 || !clkdm2)
78562306a36Sopenharmony_ci		return -EINVAL;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
78862306a36Sopenharmony_ci	if (IS_ERR(cd))
78962306a36Sopenharmony_ci		ret = PTR_ERR(cd);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_read_sleepdep)
79262306a36Sopenharmony_ci		ret = -EINVAL;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (ret) {
79562306a36Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
79662306a36Sopenharmony_ci			 clkdm1->name, clkdm2->name);
79762306a36Sopenharmony_ci		return ret;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* XXX It's faster to return the sleepdep_usecount */
80162306a36Sopenharmony_ci	return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci/**
80562306a36Sopenharmony_ci * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm
80662306a36Sopenharmony_ci * @clkdm: struct clockdomain * to remove all sleep dependencies from
80762306a36Sopenharmony_ci *
80862306a36Sopenharmony_ci * Remove all inter-clockdomain sleep dependencies that could prevent
80962306a36Sopenharmony_ci * @clkdm from idling.  Intended to be used during boot to initialize the
81062306a36Sopenharmony_ci * PRCM to a known state, after all clockdomains are put into swsup idle
81162306a36Sopenharmony_ci * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
81262306a36Sopenharmony_ci * 0 upon success.
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_ciint clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	if (!clkdm)
81762306a36Sopenharmony_ci		return -EINVAL;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_sleepdeps)
82062306a36Sopenharmony_ci		return -EINVAL;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return arch_clkdm->clkdm_clear_all_sleepdeps(clkdm);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/**
82662306a36Sopenharmony_ci * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)
82762306a36Sopenharmony_ci * @clkdm: struct clockdomain *
82862306a36Sopenharmony_ci *
82962306a36Sopenharmony_ci * Instruct the CM to force a sleep transition on the specified
83062306a36Sopenharmony_ci * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
83162306a36Sopenharmony_ci * -EINVAL if @clkdm is NULL or if clockdomain does not support
83262306a36Sopenharmony_ci * software-initiated sleep; 0 upon success.
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cistatic int clkdm_sleep_nolock(struct clockdomain *clkdm)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	int ret;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (!clkdm)
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
84262306a36Sopenharmony_ci		pr_debug("clockdomain: %s does not support forcing sleep via software\n",
84362306a36Sopenharmony_ci			 clkdm->name);
84462306a36Sopenharmony_ci		return -EINVAL;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_sleep)
84862306a36Sopenharmony_ci		return -EINVAL;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
85362306a36Sopenharmony_ci	ret = arch_clkdm->clkdm_sleep(clkdm);
85462306a36Sopenharmony_ci	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	return ret;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci/**
86062306a36Sopenharmony_ci * clkdm_sleep - force clockdomain sleep transition
86162306a36Sopenharmony_ci * @clkdm: struct clockdomain *
86262306a36Sopenharmony_ci *
86362306a36Sopenharmony_ci * Instruct the CM to force a sleep transition on the specified
86462306a36Sopenharmony_ci * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if
86562306a36Sopenharmony_ci * clockdomain does not support software-initiated sleep; 0 upon
86662306a36Sopenharmony_ci * success.
86762306a36Sopenharmony_ci */
86862306a36Sopenharmony_ciint clkdm_sleep(struct clockdomain *clkdm)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	int ret;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
87362306a36Sopenharmony_ci	ret = clkdm_sleep_nolock(clkdm);
87462306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return ret;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/**
88062306a36Sopenharmony_ci * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)
88162306a36Sopenharmony_ci * @clkdm: struct clockdomain *
88262306a36Sopenharmony_ci *
88362306a36Sopenharmony_ci * Instruct the CM to force a wakeup transition on the specified
88462306a36Sopenharmony_ci * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
88562306a36Sopenharmony_ci * -EINVAL if @clkdm is NULL or if the clockdomain does not support
88662306a36Sopenharmony_ci * software-controlled wakeup; 0 upon success.
88762306a36Sopenharmony_ci */
88862306a36Sopenharmony_cistatic int clkdm_wakeup_nolock(struct clockdomain *clkdm)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	int ret;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (!clkdm)
89362306a36Sopenharmony_ci		return -EINVAL;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
89662306a36Sopenharmony_ci		pr_debug("clockdomain: %s does not support forcing wakeup via software\n",
89762306a36Sopenharmony_ci			 clkdm->name);
89862306a36Sopenharmony_ci		return -EINVAL;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_wakeup)
90262306a36Sopenharmony_ci		return -EINVAL;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
90762306a36Sopenharmony_ci	ret = arch_clkdm->clkdm_wakeup(clkdm);
90862306a36Sopenharmony_ci	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return ret;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci/**
91462306a36Sopenharmony_ci * clkdm_wakeup - force clockdomain wakeup transition
91562306a36Sopenharmony_ci * @clkdm: struct clockdomain *
91662306a36Sopenharmony_ci *
91762306a36Sopenharmony_ci * Instruct the CM to force a wakeup transition on the specified
91862306a36Sopenharmony_ci * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the
91962306a36Sopenharmony_ci * clockdomain does not support software-controlled wakeup; 0 upon
92062306a36Sopenharmony_ci * success.
92162306a36Sopenharmony_ci */
92262306a36Sopenharmony_ciint clkdm_wakeup(struct clockdomain *clkdm)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	int ret;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
92762306a36Sopenharmony_ci	ret = clkdm_wakeup_nolock(clkdm);
92862306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return ret;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci/**
93462306a36Sopenharmony_ci * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm
93562306a36Sopenharmony_ci * @clkdm: struct clockdomain *
93662306a36Sopenharmony_ci *
93762306a36Sopenharmony_ci * Allow the hardware to automatically switch the clockdomain @clkdm
93862306a36Sopenharmony_ci * into active or idle states, as needed by downstream clocks.  If the
93962306a36Sopenharmony_ci * clockdomain has any downstream clocks enabled in the clock
94062306a36Sopenharmony_ci * framework, wkdep/sleepdep autodependencies are added; this is so
94162306a36Sopenharmony_ci * device drivers can read and write to the device.  Only for use by
94262306a36Sopenharmony_ci * the powerdomain code.  No return value.
94362306a36Sopenharmony_ci */
94462306a36Sopenharmony_civoid clkdm_allow_idle_nolock(struct clockdomain *clkdm)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	if (!clkdm)
94762306a36Sopenharmony_ci		return;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (!WARN_ON(!clkdm->forcewake_count))
95062306a36Sopenharmony_ci		clkdm->forcewake_count--;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (clkdm->forcewake_count)
95362306a36Sopenharmony_ci		return;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
95662306a36Sopenharmony_ci		clkdm_sleep_nolock(clkdm);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
95962306a36Sopenharmony_ci		return;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
96262306a36Sopenharmony_ci		return;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
96562306a36Sopenharmony_ci		return;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
96862306a36Sopenharmony_ci		 clkdm->name);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
97162306a36Sopenharmony_ci	arch_clkdm->clkdm_allow_idle(clkdm);
97262306a36Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci/**
97662306a36Sopenharmony_ci * clkdm_allow_idle - enable hwsup idle transitions for clkdm
97762306a36Sopenharmony_ci * @clkdm: struct clockdomain *
97862306a36Sopenharmony_ci *
97962306a36Sopenharmony_ci * Allow the hardware to automatically switch the clockdomain @clkdm into
98062306a36Sopenharmony_ci * active or idle states, as needed by downstream clocks.  If the
98162306a36Sopenharmony_ci * clockdomain has any downstream clocks enabled in the clock
98262306a36Sopenharmony_ci * framework, wkdep/sleepdep autodependencies are added; this is so
98362306a36Sopenharmony_ci * device drivers can read and write to the device.  No return value.
98462306a36Sopenharmony_ci */
98562306a36Sopenharmony_civoid clkdm_allow_idle(struct clockdomain *clkdm)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
98862306a36Sopenharmony_ci	clkdm_allow_idle_nolock(clkdm);
98962306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/**
99362306a36Sopenharmony_ci * clkdm_deny_idle - disable hwsup idle transitions for clkdm
99462306a36Sopenharmony_ci * @clkdm: struct clockdomain *
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * Prevent the hardware from automatically switching the clockdomain
99762306a36Sopenharmony_ci * @clkdm into inactive or idle states.  If the clockdomain has
99862306a36Sopenharmony_ci * downstream clocks enabled in the clock framework, wkdep/sleepdep
99962306a36Sopenharmony_ci * autodependencies are removed.  Only for use by the powerdomain
100062306a36Sopenharmony_ci * code.  No return value.
100162306a36Sopenharmony_ci */
100262306a36Sopenharmony_civoid clkdm_deny_idle_nolock(struct clockdomain *clkdm)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	if (!clkdm)
100562306a36Sopenharmony_ci		return;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (clkdm->forcewake_count++)
100862306a36Sopenharmony_ci		return;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
101162306a36Sopenharmony_ci		clkdm_wakeup_nolock(clkdm);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
101462306a36Sopenharmony_ci		return;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
101762306a36Sopenharmony_ci		return;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
102062306a36Sopenharmony_ci		return;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
102362306a36Sopenharmony_ci		 clkdm->name);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
102662306a36Sopenharmony_ci	arch_clkdm->clkdm_deny_idle(clkdm);
102762306a36Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci/**
103162306a36Sopenharmony_ci * clkdm_deny_idle - disable hwsup idle transitions for clkdm
103262306a36Sopenharmony_ci * @clkdm: struct clockdomain *
103362306a36Sopenharmony_ci *
103462306a36Sopenharmony_ci * Prevent the hardware from automatically switching the clockdomain
103562306a36Sopenharmony_ci * @clkdm into inactive or idle states.  If the clockdomain has
103662306a36Sopenharmony_ci * downstream clocks enabled in the clock framework, wkdep/sleepdep
103762306a36Sopenharmony_ci * autodependencies are removed.  No return value.
103862306a36Sopenharmony_ci */
103962306a36Sopenharmony_civoid clkdm_deny_idle(struct clockdomain *clkdm)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
104262306a36Sopenharmony_ci	clkdm_deny_idle_nolock(clkdm);
104362306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci/* Public autodep handling functions (deprecated) */
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci/**
104962306a36Sopenharmony_ci * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
105062306a36Sopenharmony_ci * @clkdm: struct clockdomain *
105162306a36Sopenharmony_ci *
105262306a36Sopenharmony_ci * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
105362306a36Sopenharmony_ci * in hardware-supervised mode.  Meant to be called from clock framework
105462306a36Sopenharmony_ci * when a clock inside clockdomain 'clkdm' is enabled.	No return value.
105562306a36Sopenharmony_ci *
105662306a36Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
105762306a36Sopenharmony_ci * opportunity
105862306a36Sopenharmony_ci */
105962306a36Sopenharmony_civoid clkdm_add_autodeps(struct clockdomain *clkdm)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct clkdm_autodep *autodep;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
106462306a36Sopenharmony_ci		return;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
106762306a36Sopenharmony_ci		if (IS_ERR(autodep->clkdm.ptr))
106862306a36Sopenharmony_ci			continue;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
107162306a36Sopenharmony_ci			 clkdm->name, autodep->clkdm.ptr->name);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		_clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
107462306a36Sopenharmony_ci		_clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci/**
107962306a36Sopenharmony_ci * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm
108062306a36Sopenharmony_ci * @clkdm: struct clockdomain *
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
108362306a36Sopenharmony_ci * in hardware-supervised mode.  Meant to be called from clock framework
108462306a36Sopenharmony_ci * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
108562306a36Sopenharmony_ci *
108662306a36Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
108762306a36Sopenharmony_ci * opportunity
108862306a36Sopenharmony_ci */
108962306a36Sopenharmony_civoid clkdm_del_autodeps(struct clockdomain *clkdm)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	struct clkdm_autodep *autodep;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
109462306a36Sopenharmony_ci		return;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
109762306a36Sopenharmony_ci		if (IS_ERR(autodep->clkdm.ptr))
109862306a36Sopenharmony_ci			continue;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
110162306a36Sopenharmony_ci			 clkdm->name, autodep->clkdm.ptr->name);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		_clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
110462306a36Sopenharmony_ci		_clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/* Clockdomain-to-clock/hwmod framework interface code */
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci/**
111162306a36Sopenharmony_ci * clkdm_clk_enable - add an enabled downstream clock to this clkdm
111262306a36Sopenharmony_ci * @clkdm: struct clockdomain *
111362306a36Sopenharmony_ci * @clk: struct clk * of the enabled downstream clock
111462306a36Sopenharmony_ci *
111562306a36Sopenharmony_ci * Increment the usecount of the clockdomain @clkdm and ensure that it
111662306a36Sopenharmony_ci * is awake before @clk is enabled.  Intended to be called by
111762306a36Sopenharmony_ci * clk_enable() code.  If the clockdomain is in software-supervised
111862306a36Sopenharmony_ci * idle mode, force the clockdomain to wake.  If the clockdomain is in
111962306a36Sopenharmony_ci * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to
112062306a36Sopenharmony_ci * ensure that devices in the clockdomain can be read from/written to
112162306a36Sopenharmony_ci * by on-chip processors.  Returns -EINVAL if passed null pointers;
112262306a36Sopenharmony_ci * returns 0 upon success or if the clockdomain is in hwsup idle mode.
112362306a36Sopenharmony_ci */
112462306a36Sopenharmony_ciint clkdm_clk_enable(struct clockdomain *clkdm, struct clk *unused)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
112762306a36Sopenharmony_ci		return -EINVAL;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/*
113262306a36Sopenharmony_ci	 * For arch's with no autodeps, clkcm_clk_enable
113362306a36Sopenharmony_ci	 * should be called for every clock instance or hwmod that is
113462306a36Sopenharmony_ci	 * enabled, so the clkdm can be force woken up.
113562306a36Sopenharmony_ci	 */
113662306a36Sopenharmony_ci	clkdm->usecount++;
113762306a36Sopenharmony_ci	if (clkdm->usecount > 1 && autodeps) {
113862306a36Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
113962306a36Sopenharmony_ci		return 0;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	arch_clkdm->clkdm_clk_enable(clkdm);
114362306a36Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
114462306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	pr_debug("clockdomain: %s: enabled\n", clkdm->name);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	return 0;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci/**
115262306a36Sopenharmony_ci * clkdm_clk_disable - remove an enabled downstream clock from this clkdm
115362306a36Sopenharmony_ci * @clkdm: struct clockdomain *
115462306a36Sopenharmony_ci * @clk: struct clk * of the disabled downstream clock
115562306a36Sopenharmony_ci *
115662306a36Sopenharmony_ci * Decrement the usecount of this clockdomain @clkdm when @clk is
115762306a36Sopenharmony_ci * disabled.  Intended to be called by clk_disable() code.  If the
115862306a36Sopenharmony_ci * clockdomain usecount goes to 0, put the clockdomain to sleep
115962306a36Sopenharmony_ci * (software-supervised mode) or remove the clkdm autodependencies
116062306a36Sopenharmony_ci * (hardware-supervised mode).  Returns -EINVAL if passed null
116162306a36Sopenharmony_ci * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
116262306a36Sopenharmony_ci * upon success or if the clockdomain is in hwsup idle mode.
116362306a36Sopenharmony_ci */
116462306a36Sopenharmony_ciint clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
116762306a36Sopenharmony_ci		return -EINVAL;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* corner case: disabling unused clocks */
117262306a36Sopenharmony_ci	if (clk && (__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0)
117362306a36Sopenharmony_ci		goto ccd_exit;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (clkdm->usecount == 0) {
117662306a36Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
117762306a36Sopenharmony_ci		WARN_ON(1); /* underflow */
117862306a36Sopenharmony_ci		return -ERANGE;
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	clkdm->usecount--;
118262306a36Sopenharmony_ci	if (clkdm->usecount > 0) {
118362306a36Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
118462306a36Sopenharmony_ci		return 0;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	arch_clkdm->clkdm_clk_disable(clkdm);
118862306a36Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	pr_debug("clockdomain: %s: disabled\n", clkdm->name);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ciccd_exit:
119362306a36Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	return 0;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci/**
119962306a36Sopenharmony_ci * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
120062306a36Sopenharmony_ci * @clkdm: struct clockdomain *
120162306a36Sopenharmony_ci * @oh: struct omap_hwmod * of the enabled downstream hwmod
120262306a36Sopenharmony_ci *
120362306a36Sopenharmony_ci * Increment the usecount of the clockdomain @clkdm and ensure that it
120462306a36Sopenharmony_ci * is awake before @oh is enabled. Intended to be called by
120562306a36Sopenharmony_ci * module_enable() code.
120662306a36Sopenharmony_ci * If the clockdomain is in software-supervised idle mode, force the
120762306a36Sopenharmony_ci * clockdomain to wake.  If the clockdomain is in hardware-supervised idle
120862306a36Sopenharmony_ci * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
120962306a36Sopenharmony_ci * clockdomain can be read from/written to by on-chip processors.
121062306a36Sopenharmony_ci * Returns -EINVAL if passed null pointers;
121162306a36Sopenharmony_ci * returns 0 upon success or if the clockdomain is in hwsup idle mode.
121262306a36Sopenharmony_ci */
121362306a36Sopenharmony_ciint clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	/* The clkdm attribute does not exist yet prior OMAP4 */
121662306a36Sopenharmony_ci	if (cpu_is_omap24xx() || cpu_is_omap34xx())
121762306a36Sopenharmony_ci		return 0;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	/*
122062306a36Sopenharmony_ci	 * XXX Rewrite this code to maintain a list of enabled
122162306a36Sopenharmony_ci	 * downstream hwmods for debugging purposes?
122262306a36Sopenharmony_ci	 */
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (!oh)
122562306a36Sopenharmony_ci		return -EINVAL;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return clkdm_clk_enable(clkdm, NULL);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci/**
123162306a36Sopenharmony_ci * clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
123262306a36Sopenharmony_ci * @clkdm: struct clockdomain *
123362306a36Sopenharmony_ci * @oh: struct omap_hwmod * of the disabled downstream hwmod
123462306a36Sopenharmony_ci *
123562306a36Sopenharmony_ci * Decrement the usecount of this clockdomain @clkdm when @oh is
123662306a36Sopenharmony_ci * disabled. Intended to be called by module_disable() code.
123762306a36Sopenharmony_ci * If the clockdomain usecount goes to 0, put the clockdomain to sleep
123862306a36Sopenharmony_ci * (software-supervised mode) or remove the clkdm autodependencies
123962306a36Sopenharmony_ci * (hardware-supervised mode).
124062306a36Sopenharmony_ci * Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
124162306a36Sopenharmony_ci * underflows; or returns 0 upon success or if the clockdomain is in hwsup
124262306a36Sopenharmony_ci * idle mode.
124362306a36Sopenharmony_ci */
124462306a36Sopenharmony_ciint clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	/* The clkdm attribute does not exist yet prior OMAP4 */
124762306a36Sopenharmony_ci	if (cpu_is_omap24xx() || cpu_is_omap34xx())
124862306a36Sopenharmony_ci		return 0;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	if (!oh)
125162306a36Sopenharmony_ci		return -EINVAL;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	return clkdm_clk_disable(clkdm, NULL);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci/**
125762306a36Sopenharmony_ci * _clkdm_save_context - save the context for the control of this clkdm
125862306a36Sopenharmony_ci *
125962306a36Sopenharmony_ci * Due to a suspend or hibernation operation, the state of the registers
126062306a36Sopenharmony_ci * controlling this clkdm will be lost, save their context.
126162306a36Sopenharmony_ci */
126262306a36Sopenharmony_cistatic int _clkdm_save_context(struct clockdomain *clkdm, void *unused)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_save_context)
126562306a36Sopenharmony_ci		return -EINVAL;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return arch_clkdm->clkdm_save_context(clkdm);
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci/**
127162306a36Sopenharmony_ci * _clkdm_restore_context - restore context for control of this clkdm
127262306a36Sopenharmony_ci *
127362306a36Sopenharmony_ci * Restore the register values for this clockdomain.
127462306a36Sopenharmony_ci */
127562306a36Sopenharmony_cistatic int _clkdm_restore_context(struct clockdomain *clkdm, void *unused)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_restore_context)
127862306a36Sopenharmony_ci		return -EINVAL;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return arch_clkdm->clkdm_restore_context(clkdm);
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci/**
128462306a36Sopenharmony_ci * clkdm_save_context - Saves the context for each registered clkdm
128562306a36Sopenharmony_ci *
128662306a36Sopenharmony_ci * Save the context for each registered clockdomain.
128762306a36Sopenharmony_ci */
128862306a36Sopenharmony_civoid clkdm_save_context(void)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	clkdm_for_each(_clkdm_save_context, NULL);
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci/**
129462306a36Sopenharmony_ci * clkdm_restore_context - Restores the context for each registered clkdm
129562306a36Sopenharmony_ci *
129662306a36Sopenharmony_ci * Restore the context for each registered clockdomain.
129762306a36Sopenharmony_ci */
129862306a36Sopenharmony_civoid clkdm_restore_context(void)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	clkdm_for_each(_clkdm_restore_context, NULL);
130162306a36Sopenharmony_ci}
1302