xref: /openbmc/linux/arch/arm/mach-omap2/clockdomain.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d459bfe0SPaul Walmsley /*
38a3ddc75SAbhijit Pagare  * OMAP2/3/4 clockdomain framework functions
4d459bfe0SPaul Walmsley  *
532a363c0SPaul Walmsley  * Copyright (C) 2008-2011 Texas Instruments, Inc.
632a363c0SPaul Walmsley  * Copyright (C) 2008-2011 Nokia Corporation
7d459bfe0SPaul Walmsley  *
8d459bfe0SPaul Walmsley  * Written by Paul Walmsley and Jouni Högander
98a3ddc75SAbhijit Pagare  * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
10d459bfe0SPaul Walmsley  */
1133903eb5SPaul Walmsley #undef DEBUG
12d459bfe0SPaul Walmsley 
13d459bfe0SPaul Walmsley #include <linux/kernel.h>
14d459bfe0SPaul Walmsley #include <linux/device.h>
15d459bfe0SPaul Walmsley #include <linux/list.h>
16d459bfe0SPaul Walmsley #include <linux/errno.h>
17d44b28c4SPaul Gortmaker #include <linux/string.h>
18d459bfe0SPaul Walmsley #include <linux/delay.h>
19d459bfe0SPaul Walmsley #include <linux/clk.h>
20d459bfe0SPaul Walmsley #include <linux/limits.h>
215b74c676SPaul Walmsley #include <linux/err.h>
22d043d87cSMike Turquette #include <linux/clk-provider.h>
239bcfb76bSKeerthy #include <linux/cpu_pm.h>
24d459bfe0SPaul Walmsley 
25d459bfe0SPaul Walmsley #include <linux/io.h>
26d459bfe0SPaul Walmsley 
27d459bfe0SPaul Walmsley #include <linux/bitops.h>
28d459bfe0SPaul Walmsley 
29e4c060dbSTony Lindgren #include "soc.h"
30a135eaaeSPaul Walmsley #include "clock.h"
311540f214SPaul Walmsley #include "clockdomain.h"
329bcfb76bSKeerthy #include "pm.h"
33d459bfe0SPaul Walmsley 
34d459bfe0SPaul Walmsley /* clkdm_list contains all registered struct clockdomains */
35d459bfe0SPaul Walmsley static LIST_HEAD(clkdm_list);
36d459bfe0SPaul Walmsley 
3755ed9694SPaul Walmsley /* array of clockdomain deps to be added/removed when clkdm in hwsup mode */
3855ed9694SPaul Walmsley static struct clkdm_autodep *autodeps;
39d459bfe0SPaul Walmsley 
4032d4034eSRajendra Nayak static struct clkdm_ops *arch_clkdm;
419bcfb76bSKeerthy void clkdm_save_context(void);
429bcfb76bSKeerthy void clkdm_restore_context(void);
43d459bfe0SPaul Walmsley 
44d459bfe0SPaul Walmsley /* Private functions */
45d459bfe0SPaul Walmsley 
_clkdm_lookup(const char * name)4655ed9694SPaul Walmsley static struct clockdomain *_clkdm_lookup(const char *name)
4755ed9694SPaul Walmsley {
4855ed9694SPaul Walmsley 	struct clockdomain *clkdm, *temp_clkdm;
4955ed9694SPaul Walmsley 
5055ed9694SPaul Walmsley 	if (!name)
5155ed9694SPaul Walmsley 		return NULL;
5255ed9694SPaul Walmsley 
5355ed9694SPaul Walmsley 	clkdm = NULL;
5455ed9694SPaul Walmsley 
5555ed9694SPaul Walmsley 	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
5655ed9694SPaul Walmsley 		if (!strcmp(name, temp_clkdm->name)) {
5755ed9694SPaul Walmsley 			clkdm = temp_clkdm;
5855ed9694SPaul Walmsley 			break;
5955ed9694SPaul Walmsley 		}
6055ed9694SPaul Walmsley 	}
6155ed9694SPaul Walmsley 
6255ed9694SPaul Walmsley 	return clkdm;
6355ed9694SPaul Walmsley }
6455ed9694SPaul Walmsley 
65e909d62aSPaul Walmsley /**
66e909d62aSPaul Walmsley  * _clkdm_register - register a clockdomain
67e909d62aSPaul Walmsley  * @clkdm: struct clockdomain * to register
68e909d62aSPaul Walmsley  *
69e909d62aSPaul Walmsley  * Adds a clockdomain to the internal clockdomain list.
70e909d62aSPaul Walmsley  * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
71e909d62aSPaul Walmsley  * already registered by the provided name, or 0 upon success.
72e909d62aSPaul Walmsley  */
_clkdm_register(struct clockdomain * clkdm)73e909d62aSPaul Walmsley static int _clkdm_register(struct clockdomain *clkdm)
74e909d62aSPaul Walmsley {
75e909d62aSPaul Walmsley 	struct powerdomain *pwrdm;
76e909d62aSPaul Walmsley 
77e909d62aSPaul Walmsley 	if (!clkdm || !clkdm->name)
78e909d62aSPaul Walmsley 		return -EINVAL;
79e909d62aSPaul Walmsley 
80e909d62aSPaul Walmsley 	pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
81e909d62aSPaul Walmsley 	if (!pwrdm) {
82e909d62aSPaul Walmsley 		pr_err("clockdomain: %s: powerdomain %s does not exist\n",
83e909d62aSPaul Walmsley 			clkdm->name, clkdm->pwrdm.name);
84e909d62aSPaul Walmsley 		return -EINVAL;
85e909d62aSPaul Walmsley 	}
86e909d62aSPaul Walmsley 	clkdm->pwrdm.ptr = pwrdm;
87e909d62aSPaul Walmsley 
88e909d62aSPaul Walmsley 	/* Verify that the clockdomain is not already registered */
89e909d62aSPaul Walmsley 	if (_clkdm_lookup(clkdm->name))
90e909d62aSPaul Walmsley 		return -EEXIST;
91e909d62aSPaul Walmsley 
92e909d62aSPaul Walmsley 	list_add(&clkdm->node, &clkdm_list);
93e909d62aSPaul Walmsley 
94e909d62aSPaul Walmsley 	pwrdm_add_clkdm(pwrdm, clkdm);
95e909d62aSPaul Walmsley 
96e909d62aSPaul Walmsley 	pr_debug("clockdomain: registered %s\n", clkdm->name);
97e909d62aSPaul Walmsley 
98e909d62aSPaul Walmsley 	return 0;
99e909d62aSPaul Walmsley }
100e909d62aSPaul Walmsley 
10155ed9694SPaul Walmsley /* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */
_clkdm_deps_lookup(struct clockdomain * clkdm,struct clkdm_dep * deps)10255ed9694SPaul Walmsley static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
10355ed9694SPaul Walmsley 					    struct clkdm_dep *deps)
10455ed9694SPaul Walmsley {
10555ed9694SPaul Walmsley 	struct clkdm_dep *cd;
10655ed9694SPaul Walmsley 
107a5ffef6aSPaul Walmsley 	if (!clkdm || !deps)
10855ed9694SPaul Walmsley 		return ERR_PTR(-EINVAL);
10955ed9694SPaul Walmsley 
11055ed9694SPaul Walmsley 	for (cd = deps; cd->clkdm_name; cd++) {
11155ed9694SPaul Walmsley 		if (!cd->clkdm && cd->clkdm_name)
11255ed9694SPaul Walmsley 			cd->clkdm = _clkdm_lookup(cd->clkdm_name);
11355ed9694SPaul Walmsley 
11455ed9694SPaul Walmsley 		if (cd->clkdm == clkdm)
11555ed9694SPaul Walmsley 			break;
11655ed9694SPaul Walmsley 	}
11755ed9694SPaul Walmsley 
11855ed9694SPaul Walmsley 	if (!cd->clkdm_name)
11955ed9694SPaul Walmsley 		return ERR_PTR(-ENOENT);
12055ed9694SPaul Walmsley 
12155ed9694SPaul Walmsley 	return cd;
12255ed9694SPaul Walmsley }
12355ed9694SPaul Walmsley 
12465958fb6SPaul Walmsley /**
12555ed9694SPaul Walmsley  * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
12655ed9694SPaul Walmsley  * @autodep: struct clkdm_autodep * to resolve
127d459bfe0SPaul Walmsley  *
12855ed9694SPaul Walmsley  * Resolve autodep clockdomain names to clockdomain pointers via
12955ed9694SPaul Walmsley  * clkdm_lookup() and store the pointers in the autodep structure.  An
13055ed9694SPaul Walmsley  * "autodep" is a clockdomain sleep/wakeup dependency that is
131d459bfe0SPaul Walmsley  * automatically added and removed whenever clocks in the associated
132d459bfe0SPaul Walmsley  * clockdomain are enabled or disabled (respectively) when the
133d459bfe0SPaul Walmsley  * clockdomain is in hardware-supervised mode.	Meant to be called
134d459bfe0SPaul Walmsley  * once at clockdomain layer initialization, since these should remain
135d459bfe0SPaul Walmsley  * fixed for a particular architecture.  No return value.
136b170fbe1SPaul Walmsley  *
137b170fbe1SPaul Walmsley  * XXX autodeps are deprecated and should be removed at the earliest
138b170fbe1SPaul Walmsley  * opportunity
139d459bfe0SPaul Walmsley  */
_autodep_lookup(struct clkdm_autodep * autodep)14055ed9694SPaul Walmsley static void _autodep_lookup(struct clkdm_autodep *autodep)
141d459bfe0SPaul Walmsley {
14255ed9694SPaul Walmsley 	struct clockdomain *clkdm;
143d459bfe0SPaul Walmsley 
144d459bfe0SPaul Walmsley 	if (!autodep)
145d459bfe0SPaul Walmsley 		return;
146d459bfe0SPaul Walmsley 
14755ed9694SPaul Walmsley 	clkdm = clkdm_lookup(autodep->clkdm.name);
14855ed9694SPaul Walmsley 	if (!clkdm) {
14955ed9694SPaul Walmsley 		pr_err("clockdomain: autodeps: clockdomain %s does not exist\n",
15055ed9694SPaul Walmsley 			 autodep->clkdm.name);
15155ed9694SPaul Walmsley 		clkdm = ERR_PTR(-ENOENT);
152d459bfe0SPaul Walmsley 	}
15355ed9694SPaul Walmsley 	autodep->clkdm.ptr = clkdm;
154d459bfe0SPaul Walmsley }
155d459bfe0SPaul Walmsley 
156b170fbe1SPaul Walmsley /**
1574aef7a2aSRajendra Nayak  * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
1584aef7a2aSRajendra Nayak  * @clkdm: clockdomain that we are resolving dependencies for
1594aef7a2aSRajendra Nayak  * @clkdm_deps: ptr to array of struct clkdm_deps to resolve
160a0219fbdSKalle Jokiniemi  *
1614aef7a2aSRajendra Nayak  * Iterates through @clkdm_deps, looking up the struct clockdomain named by
1624aef7a2aSRajendra Nayak  * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
163b170fbe1SPaul Walmsley  * No return value.
164b170fbe1SPaul Walmsley  */
_resolve_clkdm_deps(struct clockdomain * clkdm,struct clkdm_dep * clkdm_deps)1654aef7a2aSRajendra Nayak static void _resolve_clkdm_deps(struct clockdomain *clkdm,
1664aef7a2aSRajendra Nayak 				struct clkdm_dep *clkdm_deps)
167a0219fbdSKalle Jokiniemi {
1684aef7a2aSRajendra Nayak 	struct clkdm_dep *cd;
1694aef7a2aSRajendra Nayak 
1704aef7a2aSRajendra Nayak 	for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
1714aef7a2aSRajendra Nayak 		if (cd->clkdm)
1724aef7a2aSRajendra Nayak 			continue;
1734aef7a2aSRajendra Nayak 		cd->clkdm = _clkdm_lookup(cd->clkdm_name);
1744aef7a2aSRajendra Nayak 
1754aef7a2aSRajendra Nayak 		WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
1764aef7a2aSRajendra Nayak 		     clkdm->name, cd->clkdm_name);
1774aef7a2aSRajendra Nayak 	}
178a0219fbdSKalle Jokiniemi }
179d459bfe0SPaul Walmsley 
18065958fb6SPaul Walmsley /**
18165958fb6SPaul Walmsley  * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless)
18265958fb6SPaul Walmsley  * @clkdm1: wake this struct clockdomain * up (dependent)
18365958fb6SPaul Walmsley  * @clkdm2: when this struct clockdomain * wakes up (source)
18465958fb6SPaul Walmsley  *
18565958fb6SPaul Walmsley  * When the clockdomain represented by @clkdm2 wakes up, wake up
18665958fb6SPaul Walmsley  * @clkdm1. Implemented in hardware on the OMAP, this feature is
18765958fb6SPaul Walmsley  * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
18865958fb6SPaul Walmsley  * Returns -EINVAL if presented with invalid clockdomain pointers,
18965958fb6SPaul Walmsley  * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
19065958fb6SPaul Walmsley  * success.
19165958fb6SPaul Walmsley  */
_clkdm_add_wkdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)19265958fb6SPaul Walmsley static int _clkdm_add_wkdep(struct clockdomain *clkdm1,
19365958fb6SPaul Walmsley 			    struct clockdomain *clkdm2)
19465958fb6SPaul Walmsley {
19565958fb6SPaul Walmsley 	struct clkdm_dep *cd;
19665958fb6SPaul Walmsley 	int ret = 0;
19765958fb6SPaul Walmsley 
19865958fb6SPaul Walmsley 	if (!clkdm1 || !clkdm2)
19965958fb6SPaul Walmsley 		return -EINVAL;
20065958fb6SPaul Walmsley 
20165958fb6SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
20265958fb6SPaul Walmsley 	if (IS_ERR(cd))
20365958fb6SPaul Walmsley 		ret = PTR_ERR(cd);
20465958fb6SPaul Walmsley 
20565958fb6SPaul Walmsley 	if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
20665958fb6SPaul Walmsley 		ret = -EINVAL;
20765958fb6SPaul Walmsley 
20865958fb6SPaul Walmsley 	if (ret) {
20965958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
21065958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
21165958fb6SPaul Walmsley 		return ret;
21265958fb6SPaul Walmsley 	}
21365958fb6SPaul Walmsley 
21492493870SPaul Walmsley 	cd->wkdep_usecount++;
21592493870SPaul Walmsley 	if (cd->wkdep_usecount == 1) {
21665958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
21765958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
21865958fb6SPaul Walmsley 
21965958fb6SPaul Walmsley 		ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
22065958fb6SPaul Walmsley 	}
22165958fb6SPaul Walmsley 
22265958fb6SPaul Walmsley 	return ret;
22365958fb6SPaul Walmsley }
22465958fb6SPaul Walmsley 
22565958fb6SPaul Walmsley /**
22665958fb6SPaul Walmsley  * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless)
22765958fb6SPaul Walmsley  * @clkdm1: wake this struct clockdomain * up (dependent)
22865958fb6SPaul Walmsley  * @clkdm2: when this struct clockdomain * wakes up (source)
22965958fb6SPaul Walmsley  *
23065958fb6SPaul Walmsley  * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
23165958fb6SPaul Walmsley  * wakes up.  Returns -EINVAL if presented with invalid clockdomain
23265958fb6SPaul Walmsley  * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
23365958fb6SPaul Walmsley  * 0 upon success.
23465958fb6SPaul Walmsley  */
_clkdm_del_wkdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)23565958fb6SPaul Walmsley static int _clkdm_del_wkdep(struct clockdomain *clkdm1,
23665958fb6SPaul Walmsley 			    struct clockdomain *clkdm2)
23765958fb6SPaul Walmsley {
23865958fb6SPaul Walmsley 	struct clkdm_dep *cd;
23965958fb6SPaul Walmsley 	int ret = 0;
24065958fb6SPaul Walmsley 
24165958fb6SPaul Walmsley 	if (!clkdm1 || !clkdm2)
24265958fb6SPaul Walmsley 		return -EINVAL;
24365958fb6SPaul Walmsley 
24465958fb6SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
24565958fb6SPaul Walmsley 	if (IS_ERR(cd))
24665958fb6SPaul Walmsley 		ret = PTR_ERR(cd);
24765958fb6SPaul Walmsley 
24865958fb6SPaul Walmsley 	if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
24965958fb6SPaul Walmsley 		ret = -EINVAL;
25065958fb6SPaul Walmsley 
25165958fb6SPaul Walmsley 	if (ret) {
25265958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
25365958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
25465958fb6SPaul Walmsley 		return ret;
25565958fb6SPaul Walmsley 	}
25665958fb6SPaul Walmsley 
25792493870SPaul Walmsley 	cd->wkdep_usecount--;
25892493870SPaul Walmsley 	if (cd->wkdep_usecount == 0) {
25965958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
26065958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
26165958fb6SPaul Walmsley 
26265958fb6SPaul Walmsley 		ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
26365958fb6SPaul Walmsley 	}
26465958fb6SPaul Walmsley 
26565958fb6SPaul Walmsley 	return ret;
26665958fb6SPaul Walmsley }
26765958fb6SPaul Walmsley 
26865958fb6SPaul Walmsley /**
26965958fb6SPaul Walmsley  * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless)
27065958fb6SPaul Walmsley  * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
27165958fb6SPaul Walmsley  * @clkdm2: when this struct clockdomain * is active (source)
27265958fb6SPaul Walmsley  *
27365958fb6SPaul Walmsley  * Prevent @clkdm1 from automatically going inactive (and then to
27465958fb6SPaul Walmsley  * retention or off) if @clkdm2 is active.  Returns -EINVAL if
27565958fb6SPaul Walmsley  * presented with invalid clockdomain pointers or called on a machine
27665958fb6SPaul Walmsley  * that does not support software-configurable hardware sleep
27765958fb6SPaul Walmsley  * dependencies, -ENOENT if the specified dependency cannot be set in
27865958fb6SPaul Walmsley  * hardware, or 0 upon success.
27965958fb6SPaul Walmsley  */
_clkdm_add_sleepdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)28065958fb6SPaul Walmsley static int _clkdm_add_sleepdep(struct clockdomain *clkdm1,
28165958fb6SPaul Walmsley 			       struct clockdomain *clkdm2)
28265958fb6SPaul Walmsley {
28365958fb6SPaul Walmsley 	struct clkdm_dep *cd;
28465958fb6SPaul Walmsley 	int ret = 0;
28565958fb6SPaul Walmsley 
28665958fb6SPaul Walmsley 	if (!clkdm1 || !clkdm2)
28765958fb6SPaul Walmsley 		return -EINVAL;
28865958fb6SPaul Walmsley 
28965958fb6SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
29065958fb6SPaul Walmsley 	if (IS_ERR(cd))
29165958fb6SPaul Walmsley 		ret = PTR_ERR(cd);
29265958fb6SPaul Walmsley 
29365958fb6SPaul Walmsley 	if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
29465958fb6SPaul Walmsley 		ret = -EINVAL;
29565958fb6SPaul Walmsley 
29665958fb6SPaul Walmsley 	if (ret) {
29765958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
29865958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
29965958fb6SPaul Walmsley 		return ret;
30065958fb6SPaul Walmsley 	}
30165958fb6SPaul Walmsley 
30292493870SPaul Walmsley 	cd->sleepdep_usecount++;
30392493870SPaul Walmsley 	if (cd->sleepdep_usecount == 1) {
30465958fb6SPaul Walmsley 		pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
30565958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
30665958fb6SPaul Walmsley 
30765958fb6SPaul Walmsley 		ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
30865958fb6SPaul Walmsley 	}
30965958fb6SPaul Walmsley 
31065958fb6SPaul Walmsley 	return ret;
31165958fb6SPaul Walmsley }
31265958fb6SPaul Walmsley 
31365958fb6SPaul Walmsley /**
31465958fb6SPaul Walmsley  * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless)
31565958fb6SPaul Walmsley  * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
31665958fb6SPaul Walmsley  * @clkdm2: when this struct clockdomain * is active (source)
31765958fb6SPaul Walmsley  *
31865958fb6SPaul Walmsley  * Allow @clkdm1 to automatically go inactive (and then to retention or
31965958fb6SPaul Walmsley  * off), independent of the activity state of @clkdm2.  Returns -EINVAL
32065958fb6SPaul Walmsley  * if presented with invalid clockdomain pointers or called on a machine
32165958fb6SPaul Walmsley  * that does not support software-configurable hardware sleep dependencies,
32265958fb6SPaul Walmsley  * -ENOENT if the specified dependency cannot be cleared in hardware, or
32365958fb6SPaul Walmsley  * 0 upon success.
32465958fb6SPaul Walmsley  */
_clkdm_del_sleepdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)32565958fb6SPaul Walmsley static int _clkdm_del_sleepdep(struct clockdomain *clkdm1,
32665958fb6SPaul Walmsley 			       struct clockdomain *clkdm2)
32765958fb6SPaul Walmsley {
32865958fb6SPaul Walmsley 	struct clkdm_dep *cd;
32965958fb6SPaul Walmsley 	int ret = 0;
33065958fb6SPaul Walmsley 
33165958fb6SPaul Walmsley 	if (!clkdm1 || !clkdm2)
33265958fb6SPaul Walmsley 		return -EINVAL;
33365958fb6SPaul Walmsley 
33465958fb6SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
33565958fb6SPaul Walmsley 	if (IS_ERR(cd))
33665958fb6SPaul Walmsley 		ret = PTR_ERR(cd);
33765958fb6SPaul Walmsley 
33865958fb6SPaul Walmsley 	if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
33965958fb6SPaul Walmsley 		ret = -EINVAL;
34065958fb6SPaul Walmsley 
34165958fb6SPaul Walmsley 	if (ret) {
34265958fb6SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
34365958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
34465958fb6SPaul Walmsley 		return ret;
34565958fb6SPaul Walmsley 	}
34665958fb6SPaul Walmsley 
34792493870SPaul Walmsley 	cd->sleepdep_usecount--;
34892493870SPaul Walmsley 	if (cd->sleepdep_usecount == 0) {
34965958fb6SPaul Walmsley 		pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
35065958fb6SPaul Walmsley 			 clkdm1->name, clkdm2->name);
35165958fb6SPaul Walmsley 
35265958fb6SPaul Walmsley 		ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
35365958fb6SPaul Walmsley 	}
35465958fb6SPaul Walmsley 
35565958fb6SPaul Walmsley 	return ret;
35665958fb6SPaul Walmsley }
35765958fb6SPaul Walmsley 
358d459bfe0SPaul Walmsley /* Public functions */
359d459bfe0SPaul Walmsley 
360d459bfe0SPaul Walmsley /**
36108cb9703SPaul Walmsley  * clkdm_register_platform_funcs - register clockdomain implementation fns
36208cb9703SPaul Walmsley  * @co: func pointers for arch specific implementations
363d459bfe0SPaul Walmsley  *
36408cb9703SPaul Walmsley  * Register the list of function pointers used to implement the
36508cb9703SPaul Walmsley  * clockdomain functions on different OMAP SoCs.  Should be called
36608cb9703SPaul Walmsley  * before any other clkdm_register*() function.  Returns -EINVAL if
36708cb9703SPaul Walmsley  * @co is null, -EEXIST if platform functions have already been
36808cb9703SPaul Walmsley  * registered, or 0 upon success.
369d459bfe0SPaul Walmsley  */
clkdm_register_platform_funcs(struct clkdm_ops * co)37008cb9703SPaul Walmsley int clkdm_register_platform_funcs(struct clkdm_ops *co)
37108cb9703SPaul Walmsley {
37208cb9703SPaul Walmsley 	if (!co)
37308cb9703SPaul Walmsley 		return -EINVAL;
37408cb9703SPaul Walmsley 
37508cb9703SPaul Walmsley 	if (arch_clkdm)
37608cb9703SPaul Walmsley 		return -EEXIST;
37708cb9703SPaul Walmsley 
37808cb9703SPaul Walmsley 	arch_clkdm = co;
37908cb9703SPaul Walmsley 
38008cb9703SPaul Walmsley 	return 0;
38108cb9703SPaul Walmsley };
38208cb9703SPaul Walmsley 
38308cb9703SPaul Walmsley /**
38408cb9703SPaul Walmsley  * clkdm_register_clkdms - register SoC clockdomains
38508cb9703SPaul Walmsley  * @cs: pointer to an array of struct clockdomain to register
38608cb9703SPaul Walmsley  *
38708cb9703SPaul Walmsley  * Register the clockdomains available on a particular OMAP SoC.  Must
38808cb9703SPaul Walmsley  * be called after clkdm_register_platform_funcs().  May be called
38908cb9703SPaul Walmsley  * multiple times.  Returns -EACCES if called before
39008cb9703SPaul Walmsley  * clkdm_register_platform_funcs(); -EINVAL if the argument @cs is
39108cb9703SPaul Walmsley  * null; or 0 upon success.
39208cb9703SPaul Walmsley  */
clkdm_register_clkdms(struct clockdomain ** cs)39308cb9703SPaul Walmsley int clkdm_register_clkdms(struct clockdomain **cs)
394d459bfe0SPaul Walmsley {
395d459bfe0SPaul Walmsley 	struct clockdomain **c = NULL;
396d459bfe0SPaul Walmsley 
39708cb9703SPaul Walmsley 	if (!arch_clkdm)
39808cb9703SPaul Walmsley 		return -EACCES;
39932d4034eSRajendra Nayak 
40008cb9703SPaul Walmsley 	if (!cs)
40108cb9703SPaul Walmsley 		return -EINVAL;
40208cb9703SPaul Walmsley 
40308cb9703SPaul Walmsley 	for (c = cs; *c; c++)
404e909d62aSPaul Walmsley 		_clkdm_register(*c);
405d459bfe0SPaul Walmsley 
40608cb9703SPaul Walmsley 	return 0;
40708cb9703SPaul Walmsley }
408369d5614SPaul Walmsley 
40908cb9703SPaul Walmsley /**
41008cb9703SPaul Walmsley  * clkdm_register_autodeps - register autodeps (if required)
41108cb9703SPaul Walmsley  * @ia: pointer to a static array of struct clkdm_autodep to register
41208cb9703SPaul Walmsley  *
41308cb9703SPaul Walmsley  * Register clockdomain "automatic dependencies."  These are
41408cb9703SPaul Walmsley  * clockdomain wakeup and sleep dependencies that are automatically
41508cb9703SPaul Walmsley  * added whenever the first clock inside a clockdomain is enabled, and
41608cb9703SPaul Walmsley  * removed whenever the last clock inside a clockdomain is disabled.
41708cb9703SPaul Walmsley  * These are currently only used on OMAP3 devices, and are deprecated,
41808cb9703SPaul Walmsley  * since they waste energy.  However, until the OMAP2/3 IP block
41908cb9703SPaul Walmsley  * enable/disable sequence can be converted to match the OMAP4
42008cb9703SPaul Walmsley  * sequence, they are needed.
42108cb9703SPaul Walmsley  *
42208cb9703SPaul Walmsley  * Must be called only after all of the SoC clockdomains are
42308cb9703SPaul Walmsley  * registered, since the function will resolve autodep clockdomain
42408cb9703SPaul Walmsley  * names into clockdomain pointers.
42508cb9703SPaul Walmsley  *
42608cb9703SPaul Walmsley  * The struct clkdm_autodep @ia array must be static, as this function
42708cb9703SPaul Walmsley  * does not copy the array elements.
42808cb9703SPaul Walmsley  *
42908cb9703SPaul Walmsley  * Returns -EACCES if called before any clockdomains have been
43008cb9703SPaul Walmsley  * registered, -EINVAL if called with a null @ia argument, -EEXIST if
43108cb9703SPaul Walmsley  * autodeps have already been registered, or 0 upon success.
432369d5614SPaul Walmsley  */
clkdm_register_autodeps(struct clkdm_autodep * ia)43308cb9703SPaul Walmsley int clkdm_register_autodeps(struct clkdm_autodep *ia)
43408cb9703SPaul Walmsley {
43508cb9703SPaul Walmsley 	struct clkdm_autodep *a = NULL;
43608cb9703SPaul Walmsley 
43708cb9703SPaul Walmsley 	if (list_empty(&clkdm_list))
43808cb9703SPaul Walmsley 		return -EACCES;
43908cb9703SPaul Walmsley 
44008cb9703SPaul Walmsley 	if (!ia)
44108cb9703SPaul Walmsley 		return -EINVAL;
44208cb9703SPaul Walmsley 
44308cb9703SPaul Walmsley 	if (autodeps)
44408cb9703SPaul Walmsley 		return -EEXIST;
44508cb9703SPaul Walmsley 
44608cb9703SPaul Walmsley 	autodeps = ia;
44708cb9703SPaul Walmsley 	for (a = autodeps; a->clkdm.ptr; a++)
44808cb9703SPaul Walmsley 		_autodep_lookup(a);
44908cb9703SPaul Walmsley 
45008cb9703SPaul Walmsley 	return 0;
45108cb9703SPaul Walmsley }
45208cb9703SPaul Walmsley 
cpu_notifier(struct notifier_block * nb,unsigned long cmd,void * v)4539bcfb76bSKeerthy static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v)
4549bcfb76bSKeerthy {
4559bcfb76bSKeerthy 	switch (cmd) {
4569bcfb76bSKeerthy 	case CPU_CLUSTER_PM_ENTER:
4579bcfb76bSKeerthy 		if (enable_off_mode)
4589bcfb76bSKeerthy 			clkdm_save_context();
4599bcfb76bSKeerthy 		break;
4609bcfb76bSKeerthy 	case CPU_CLUSTER_PM_EXIT:
4619bcfb76bSKeerthy 		if (enable_off_mode)
4629bcfb76bSKeerthy 			clkdm_restore_context();
4639bcfb76bSKeerthy 		break;
4649bcfb76bSKeerthy 	}
4659bcfb76bSKeerthy 
4669bcfb76bSKeerthy 	return NOTIFY_OK;
4679bcfb76bSKeerthy }
4689bcfb76bSKeerthy 
46908cb9703SPaul Walmsley /**
47008cb9703SPaul Walmsley  * clkdm_complete_init - set up the clockdomain layer
47108cb9703SPaul Walmsley  *
47208cb9703SPaul Walmsley  * Put all clockdomains into software-supervised mode; PM code should
47308cb9703SPaul Walmsley  * later enable hardware-supervised mode as appropriate.  Must be
47408cb9703SPaul Walmsley  * called after clkdm_register_clkdms().  Returns -EACCES if called
47508cb9703SPaul Walmsley  * before clkdm_register_clkdms(), or 0 upon success.
47608cb9703SPaul Walmsley  */
clkdm_complete_init(void)47708cb9703SPaul Walmsley int clkdm_complete_init(void)
47808cb9703SPaul Walmsley {
47908cb9703SPaul Walmsley 	struct clockdomain *clkdm;
4809bcfb76bSKeerthy 	static struct notifier_block nb;
48108cb9703SPaul Walmsley 
48208cb9703SPaul Walmsley 	if (list_empty(&clkdm_list))
48308cb9703SPaul Walmsley 		return -EACCES;
48408cb9703SPaul Walmsley 
485369d5614SPaul Walmsley 	list_for_each_entry(clkdm, &clkdm_list, node) {
4865cd1937bSRajendra Nayak 		clkdm_deny_idle(clkdm);
4876f7f63ccSPaul Walmsley 
4884aef7a2aSRajendra Nayak 		_resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
4896f7f63ccSPaul Walmsley 		clkdm_clear_all_wkdeps(clkdm);
4904aef7a2aSRajendra Nayak 
4914aef7a2aSRajendra Nayak 		_resolve_clkdm_deps(clkdm, clkdm->sleepdep_srcs);
4926f7f63ccSPaul Walmsley 		clkdm_clear_all_sleepdeps(clkdm);
493369d5614SPaul Walmsley 	}
49408cb9703SPaul Walmsley 
4959bcfb76bSKeerthy 	/* Only AM43XX can lose clkdm context during rtc-ddr suspend */
4969bcfb76bSKeerthy 	if (soc_is_am43xx()) {
4979bcfb76bSKeerthy 		nb.notifier_call = cpu_notifier;
4989bcfb76bSKeerthy 		cpu_pm_register_notifier(&nb);
4999bcfb76bSKeerthy 	}
5009bcfb76bSKeerthy 
50108cb9703SPaul Walmsley 	return 0;
502d459bfe0SPaul Walmsley }
503d459bfe0SPaul Walmsley 
504d459bfe0SPaul Walmsley /**
505d459bfe0SPaul Walmsley  * clkdm_lookup - look up a clockdomain by name, return a pointer
506d459bfe0SPaul Walmsley  * @name: name of clockdomain
507d459bfe0SPaul Walmsley  *
508f0271d65SPaul Walmsley  * Find a registered clockdomain by its name @name.  Returns a pointer
509f0271d65SPaul Walmsley  * to the struct clockdomain if found, or NULL otherwise.
510d459bfe0SPaul Walmsley  */
clkdm_lookup(const char * name)511d459bfe0SPaul Walmsley struct clockdomain *clkdm_lookup(const char *name)
512d459bfe0SPaul Walmsley {
513d459bfe0SPaul Walmsley 	struct clockdomain *clkdm, *temp_clkdm;
514d459bfe0SPaul Walmsley 
515d459bfe0SPaul Walmsley 	if (!name)
516d459bfe0SPaul Walmsley 		return NULL;
517d459bfe0SPaul Walmsley 
518d459bfe0SPaul Walmsley 	clkdm = NULL;
519d459bfe0SPaul Walmsley 
520d459bfe0SPaul Walmsley 	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
521d459bfe0SPaul Walmsley 		if (!strcmp(name, temp_clkdm->name)) {
522d459bfe0SPaul Walmsley 			clkdm = temp_clkdm;
523d459bfe0SPaul Walmsley 			break;
524d459bfe0SPaul Walmsley 		}
525d459bfe0SPaul Walmsley 	}
526d459bfe0SPaul Walmsley 
527d459bfe0SPaul Walmsley 	return clkdm;
528d459bfe0SPaul Walmsley }
529d459bfe0SPaul Walmsley 
530d459bfe0SPaul Walmsley /**
531d459bfe0SPaul Walmsley  * clkdm_for_each - call function on each registered clockdomain
532d459bfe0SPaul Walmsley  * @fn: callback function *
533d459bfe0SPaul Walmsley  *
534f0271d65SPaul Walmsley  * Call the supplied function @fn for each registered clockdomain.
535f0271d65SPaul Walmsley  * The callback function @fn can return anything but 0 to bail
536d459bfe0SPaul Walmsley  * out early from the iterator.  The callback function is called with
537d459bfe0SPaul Walmsley  * the clkdm_mutex held, so no clockdomain structure manipulation
538d459bfe0SPaul Walmsley  * functions should be called from the callback, although hardware
539d459bfe0SPaul Walmsley  * clockdomain control functions are fine.  Returns the last return
540d459bfe0SPaul Walmsley  * value of the callback function, which should be 0 for success or
541d459bfe0SPaul Walmsley  * anything else to indicate failure; or -EINVAL if the function pointer
542d459bfe0SPaul Walmsley  * is null.
543d459bfe0SPaul Walmsley  */
clkdm_for_each(int (* fn)(struct clockdomain * clkdm,void * user),void * user)544a23456e9SPeter 'p2' De Schrijver int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user),
545a23456e9SPeter 'p2' De Schrijver 			void *user)
546d459bfe0SPaul Walmsley {
547d459bfe0SPaul Walmsley 	struct clockdomain *clkdm;
548d459bfe0SPaul Walmsley 	int ret = 0;
549d459bfe0SPaul Walmsley 
550d459bfe0SPaul Walmsley 	if (!fn)
551d459bfe0SPaul Walmsley 		return -EINVAL;
552d459bfe0SPaul Walmsley 
553d459bfe0SPaul Walmsley 	list_for_each_entry(clkdm, &clkdm_list, node) {
554a23456e9SPeter 'p2' De Schrijver 		ret = (*fn)(clkdm, user);
555d459bfe0SPaul Walmsley 		if (ret)
556d459bfe0SPaul Walmsley 			break;
557d459bfe0SPaul Walmsley 	}
558d459bfe0SPaul Walmsley 
559d459bfe0SPaul Walmsley 	return ret;
560d459bfe0SPaul Walmsley }
561d459bfe0SPaul Walmsley 
562d459bfe0SPaul Walmsley 
563e89087c9SPaul Walmsley /**
564e89087c9SPaul Walmsley  * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
565e89087c9SPaul Walmsley  * @clkdm: struct clockdomain *
566e89087c9SPaul Walmsley  *
567e89087c9SPaul Walmsley  * Return a pointer to the struct powerdomain that the specified clockdomain
568f0271d65SPaul Walmsley  * @clkdm exists in, or returns NULL if @clkdm is NULL.
569e89087c9SPaul Walmsley  */
clkdm_get_pwrdm(struct clockdomain * clkdm)570e89087c9SPaul Walmsley struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
571e89087c9SPaul Walmsley {
572e89087c9SPaul Walmsley 	if (!clkdm)
573e89087c9SPaul Walmsley 		return NULL;
574e89087c9SPaul Walmsley 
5755b74c676SPaul Walmsley 	return clkdm->pwrdm.ptr;
576e89087c9SPaul Walmsley }
577e89087c9SPaul Walmsley 
578e89087c9SPaul Walmsley 
579d459bfe0SPaul Walmsley /* Hardware clockdomain control */
580d459bfe0SPaul Walmsley 
581d459bfe0SPaul Walmsley /**
58255ed9694SPaul Walmsley  * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
58355ed9694SPaul Walmsley  * @clkdm1: wake this struct clockdomain * up (dependent)
58455ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * wakes up (source)
58555ed9694SPaul Walmsley  *
58655ed9694SPaul Walmsley  * When the clockdomain represented by @clkdm2 wakes up, wake up
58755ed9694SPaul Walmsley  * @clkdm1. Implemented in hardware on the OMAP, this feature is
58855ed9694SPaul Walmsley  * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
58955ed9694SPaul Walmsley  * Returns -EINVAL if presented with invalid clockdomain pointers,
59055ed9694SPaul Walmsley  * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
59155ed9694SPaul Walmsley  * success.
59255ed9694SPaul Walmsley  */
clkdm_add_wkdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)59355ed9694SPaul Walmsley int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
59455ed9694SPaul Walmsley {
59592493870SPaul Walmsley 	struct clkdm_dep *cd;
59692493870SPaul Walmsley 	int ret;
59792493870SPaul Walmsley 
59892493870SPaul Walmsley 	if (!clkdm1 || !clkdm2)
59992493870SPaul Walmsley 		return -EINVAL;
60092493870SPaul Walmsley 
60192493870SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
60292493870SPaul Walmsley 	if (IS_ERR(cd))
60392493870SPaul Walmsley 		return PTR_ERR(cd);
60492493870SPaul Walmsley 
60592493870SPaul Walmsley 	pwrdm_lock(cd->clkdm->pwrdm.ptr);
60692493870SPaul Walmsley 	ret = _clkdm_add_wkdep(clkdm1, clkdm2);
60792493870SPaul Walmsley 	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
60892493870SPaul Walmsley 
60992493870SPaul Walmsley 	return ret;
61055ed9694SPaul Walmsley }
61155ed9694SPaul Walmsley 
61255ed9694SPaul Walmsley /**
61355ed9694SPaul Walmsley  * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
61455ed9694SPaul Walmsley  * @clkdm1: wake this struct clockdomain * up (dependent)
61555ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * wakes up (source)
61655ed9694SPaul Walmsley  *
61755ed9694SPaul Walmsley  * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
61855ed9694SPaul Walmsley  * wakes up.  Returns -EINVAL if presented with invalid clockdomain
61955ed9694SPaul Walmsley  * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
62055ed9694SPaul Walmsley  * 0 upon success.
62155ed9694SPaul Walmsley  */
clkdm_del_wkdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)62255ed9694SPaul Walmsley int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
62355ed9694SPaul Walmsley {
62492493870SPaul Walmsley 	struct clkdm_dep *cd;
62592493870SPaul Walmsley 	int ret;
62692493870SPaul Walmsley 
62792493870SPaul Walmsley 	if (!clkdm1 || !clkdm2)
62892493870SPaul Walmsley 		return -EINVAL;
62992493870SPaul Walmsley 
63092493870SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
63192493870SPaul Walmsley 	if (IS_ERR(cd))
63292493870SPaul Walmsley 		return PTR_ERR(cd);
63392493870SPaul Walmsley 
63492493870SPaul Walmsley 	pwrdm_lock(cd->clkdm->pwrdm.ptr);
63592493870SPaul Walmsley 	ret = _clkdm_del_wkdep(clkdm1, clkdm2);
63692493870SPaul Walmsley 	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
63792493870SPaul Walmsley 
63892493870SPaul Walmsley 	return ret;
63955ed9694SPaul Walmsley }
64055ed9694SPaul Walmsley 
64155ed9694SPaul Walmsley /**
64255ed9694SPaul Walmsley  * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
64355ed9694SPaul Walmsley  * @clkdm1: wake this struct clockdomain * up (dependent)
64455ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * wakes up (source)
64555ed9694SPaul Walmsley  *
64655ed9694SPaul Walmsley  * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
64755ed9694SPaul Walmsley  * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
64855ed9694SPaul Walmsley  * if either clockdomain pointer is invalid; or -ENOENT if the hardware
64955ed9694SPaul Walmsley  * is incapable.
65055ed9694SPaul Walmsley  *
65155ed9694SPaul Walmsley  * REVISIT: Currently this function only represents software-controllable
65255ed9694SPaul Walmsley  * wakeup dependencies.  Wakeup dependencies fixed in hardware are not
65355ed9694SPaul Walmsley  * yet handled here.
65455ed9694SPaul Walmsley  */
clkdm_read_wkdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)65555ed9694SPaul Walmsley int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
65655ed9694SPaul Walmsley {
65755ed9694SPaul Walmsley 	struct clkdm_dep *cd;
6584aef7a2aSRajendra Nayak 	int ret = 0;
65955ed9694SPaul Walmsley 
66055ed9694SPaul Walmsley 	if (!clkdm1 || !clkdm2)
66155ed9694SPaul Walmsley 		return -EINVAL;
66255ed9694SPaul Walmsley 
66355ed9694SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
6644aef7a2aSRajendra Nayak 	if (IS_ERR(cd))
6654aef7a2aSRajendra Nayak 		ret = PTR_ERR(cd);
6664aef7a2aSRajendra Nayak 
6674aef7a2aSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep)
6684aef7a2aSRajendra Nayak 		ret = -EINVAL;
6694aef7a2aSRajendra Nayak 
6704aef7a2aSRajendra Nayak 	if (ret) {
6717852ec05SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
6727852ec05SPaul Walmsley 			 clkdm1->name, clkdm2->name);
6734aef7a2aSRajendra Nayak 		return ret;
67455ed9694SPaul Walmsley 	}
67555ed9694SPaul Walmsley 
67692493870SPaul Walmsley 	/* XXX It's faster to return the wkdep_usecount */
6774aef7a2aSRajendra Nayak 	return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
67855ed9694SPaul Walmsley }
67955ed9694SPaul Walmsley 
68055ed9694SPaul Walmsley /**
681369d5614SPaul Walmsley  * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm
682369d5614SPaul Walmsley  * @clkdm: struct clockdomain * to remove all wakeup dependencies from
683369d5614SPaul Walmsley  *
684369d5614SPaul Walmsley  * Remove all inter-clockdomain wakeup dependencies that could cause
685369d5614SPaul Walmsley  * @clkdm to wake.  Intended to be used during boot to initialize the
686369d5614SPaul Walmsley  * PRCM to a known state, after all clockdomains are put into swsup idle
687369d5614SPaul Walmsley  * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
688369d5614SPaul Walmsley  * 0 upon success.
689369d5614SPaul Walmsley  */
clkdm_clear_all_wkdeps(struct clockdomain * clkdm)690369d5614SPaul Walmsley int clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
691369d5614SPaul Walmsley {
692369d5614SPaul Walmsley 	if (!clkdm)
693369d5614SPaul Walmsley 		return -EINVAL;
694369d5614SPaul Walmsley 
6954aef7a2aSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps)
6964aef7a2aSRajendra Nayak 		return -EINVAL;
697369d5614SPaul Walmsley 
6984aef7a2aSRajendra Nayak 	return arch_clkdm->clkdm_clear_all_wkdeps(clkdm);
699369d5614SPaul Walmsley }
700369d5614SPaul Walmsley 
701369d5614SPaul Walmsley /**
70255ed9694SPaul Walmsley  * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
70355ed9694SPaul Walmsley  * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
70455ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * is active (source)
70555ed9694SPaul Walmsley  *
70655ed9694SPaul Walmsley  * Prevent @clkdm1 from automatically going inactive (and then to
70755ed9694SPaul Walmsley  * retention or off) if @clkdm2 is active.  Returns -EINVAL if
70855ed9694SPaul Walmsley  * presented with invalid clockdomain pointers or called on a machine
70955ed9694SPaul Walmsley  * that does not support software-configurable hardware sleep
71055ed9694SPaul Walmsley  * dependencies, -ENOENT if the specified dependency cannot be set in
71155ed9694SPaul Walmsley  * hardware, or 0 upon success.
71255ed9694SPaul Walmsley  */
clkdm_add_sleepdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)71355ed9694SPaul Walmsley int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
71455ed9694SPaul Walmsley {
71592493870SPaul Walmsley 	struct clkdm_dep *cd;
71692493870SPaul Walmsley 	int ret;
71792493870SPaul Walmsley 
71892493870SPaul Walmsley 	if (!clkdm1 || !clkdm2)
71992493870SPaul Walmsley 		return -EINVAL;
72092493870SPaul Walmsley 
72192493870SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
72292493870SPaul Walmsley 	if (IS_ERR(cd))
72392493870SPaul Walmsley 		return PTR_ERR(cd);
72492493870SPaul Walmsley 
72592493870SPaul Walmsley 	pwrdm_lock(cd->clkdm->pwrdm.ptr);
72692493870SPaul Walmsley 	ret = _clkdm_add_sleepdep(clkdm1, clkdm2);
72792493870SPaul Walmsley 	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
72892493870SPaul Walmsley 
72992493870SPaul Walmsley 	return ret;
73055ed9694SPaul Walmsley }
73155ed9694SPaul Walmsley 
73255ed9694SPaul Walmsley /**
73355ed9694SPaul Walmsley  * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1
73455ed9694SPaul Walmsley  * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
73555ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * is active (source)
73655ed9694SPaul Walmsley  *
73755ed9694SPaul Walmsley  * Allow @clkdm1 to automatically go inactive (and then to retention or
73855ed9694SPaul Walmsley  * off), independent of the activity state of @clkdm2.  Returns -EINVAL
73955ed9694SPaul Walmsley  * if presented with invalid clockdomain pointers or called on a machine
74055ed9694SPaul Walmsley  * that does not support software-configurable hardware sleep dependencies,
74155ed9694SPaul Walmsley  * -ENOENT if the specified dependency cannot be cleared in hardware, or
74255ed9694SPaul Walmsley  * 0 upon success.
74355ed9694SPaul Walmsley  */
clkdm_del_sleepdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)74455ed9694SPaul Walmsley int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
74555ed9694SPaul Walmsley {
74692493870SPaul Walmsley 	struct clkdm_dep *cd;
74792493870SPaul Walmsley 	int ret;
74892493870SPaul Walmsley 
74992493870SPaul Walmsley 	if (!clkdm1 || !clkdm2)
75092493870SPaul Walmsley 		return -EINVAL;
75192493870SPaul Walmsley 
75292493870SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
75392493870SPaul Walmsley 	if (IS_ERR(cd))
75492493870SPaul Walmsley 		return PTR_ERR(cd);
75592493870SPaul Walmsley 
75692493870SPaul Walmsley 	pwrdm_lock(cd->clkdm->pwrdm.ptr);
75792493870SPaul Walmsley 	ret = _clkdm_del_sleepdep(clkdm1, clkdm2);
75892493870SPaul Walmsley 	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
75992493870SPaul Walmsley 
76092493870SPaul Walmsley 	return ret;
76155ed9694SPaul Walmsley }
76255ed9694SPaul Walmsley 
76355ed9694SPaul Walmsley /**
76455ed9694SPaul Walmsley  * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1
76555ed9694SPaul Walmsley  * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
76655ed9694SPaul Walmsley  * @clkdm2: when this struct clockdomain * is active (source)
76755ed9694SPaul Walmsley  *
76855ed9694SPaul Walmsley  * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will
76955ed9694SPaul Walmsley  * not be allowed to automatically go inactive if @clkdm2 is active;
77055ed9694SPaul Walmsley  * 0 if @clkdm1's automatic power state inactivity transition is independent
77155ed9694SPaul Walmsley  * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called
77255ed9694SPaul Walmsley  * on a machine that does not support software-configurable hardware sleep
77355ed9694SPaul Walmsley  * dependencies; or -ENOENT if the hardware is incapable.
77455ed9694SPaul Walmsley  *
77555ed9694SPaul Walmsley  * REVISIT: Currently this function only represents software-controllable
77655ed9694SPaul Walmsley  * sleep dependencies.	Sleep dependencies fixed in hardware are not
77755ed9694SPaul Walmsley  * yet handled here.
77855ed9694SPaul Walmsley  */
clkdm_read_sleepdep(struct clockdomain * clkdm1,struct clockdomain * clkdm2)77955ed9694SPaul Walmsley int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
78055ed9694SPaul Walmsley {
78155ed9694SPaul Walmsley 	struct clkdm_dep *cd;
7824aef7a2aSRajendra Nayak 	int ret = 0;
78355ed9694SPaul Walmsley 
78455ed9694SPaul Walmsley 	if (!clkdm1 || !clkdm2)
78555ed9694SPaul Walmsley 		return -EINVAL;
78655ed9694SPaul Walmsley 
78755ed9694SPaul Walmsley 	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
7884aef7a2aSRajendra Nayak 	if (IS_ERR(cd))
7894aef7a2aSRajendra Nayak 		ret = PTR_ERR(cd);
7904aef7a2aSRajendra Nayak 
7914aef7a2aSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_read_sleepdep)
7924aef7a2aSRajendra Nayak 		ret = -EINVAL;
7934aef7a2aSRajendra Nayak 
7944aef7a2aSRajendra Nayak 	if (ret) {
7957852ec05SPaul Walmsley 		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
7967852ec05SPaul Walmsley 			 clkdm1->name, clkdm2->name);
7974aef7a2aSRajendra Nayak 		return ret;
79855ed9694SPaul Walmsley 	}
79955ed9694SPaul Walmsley 
80092493870SPaul Walmsley 	/* XXX It's faster to return the sleepdep_usecount */
8014aef7a2aSRajendra Nayak 	return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2);
80255ed9694SPaul Walmsley }
80355ed9694SPaul Walmsley 
804369d5614SPaul Walmsley /**
805369d5614SPaul Walmsley  * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm
806369d5614SPaul Walmsley  * @clkdm: struct clockdomain * to remove all sleep dependencies from
807369d5614SPaul Walmsley  *
808369d5614SPaul Walmsley  * Remove all inter-clockdomain sleep dependencies that could prevent
809369d5614SPaul Walmsley  * @clkdm from idling.  Intended to be used during boot to initialize the
810369d5614SPaul Walmsley  * PRCM to a known state, after all clockdomains are put into swsup idle
811369d5614SPaul Walmsley  * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
812369d5614SPaul Walmsley  * 0 upon success.
813369d5614SPaul Walmsley  */
clkdm_clear_all_sleepdeps(struct clockdomain * clkdm)814369d5614SPaul Walmsley int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
815369d5614SPaul Walmsley {
816369d5614SPaul Walmsley 	if (!clkdm)
817369d5614SPaul Walmsley 		return -EINVAL;
818369d5614SPaul Walmsley 
8194aef7a2aSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_sleepdeps)
8204aef7a2aSRajendra Nayak 		return -EINVAL;
821369d5614SPaul Walmsley 
8224aef7a2aSRajendra Nayak 	return arch_clkdm->clkdm_clear_all_sleepdeps(clkdm);
823369d5614SPaul Walmsley }
82455ed9694SPaul Walmsley 
82555ed9694SPaul Walmsley /**
8263a090284SPaul Walmsley  * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)
827d459bfe0SPaul Walmsley  * @clkdm: struct clockdomain *
828d459bfe0SPaul Walmsley  *
829d459bfe0SPaul Walmsley  * Instruct the CM to force a sleep transition on the specified
8303a090284SPaul Walmsley  * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
8313a090284SPaul Walmsley  * -EINVAL if @clkdm is NULL or if clockdomain does not support
8323a090284SPaul Walmsley  * software-initiated sleep; 0 upon success.
833d459bfe0SPaul Walmsley  */
clkdm_sleep_nolock(struct clockdomain * clkdm)834*6aeb51c1SArnd Bergmann static int clkdm_sleep_nolock(struct clockdomain *clkdm)
835d459bfe0SPaul Walmsley {
836555e74eaSRajendra Nayak 	int ret;
837555e74eaSRajendra Nayak 
838d459bfe0SPaul Walmsley 	if (!clkdm)
839d459bfe0SPaul Walmsley 		return -EINVAL;
840d459bfe0SPaul Walmsley 
841d459bfe0SPaul Walmsley 	if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
8427852ec05SPaul Walmsley 		pr_debug("clockdomain: %s does not support forcing sleep via software\n",
8437852ec05SPaul Walmsley 			 clkdm->name);
844d459bfe0SPaul Walmsley 		return -EINVAL;
845d459bfe0SPaul Walmsley 	}
846d459bfe0SPaul Walmsley 
84768b921adSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_sleep)
84868b921adSRajendra Nayak 		return -EINVAL;
84968b921adSRajendra Nayak 
850d459bfe0SPaul Walmsley 	pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
851d459bfe0SPaul Walmsley 
85232a363c0SPaul Walmsley 	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
853555e74eaSRajendra Nayak 	ret = arch_clkdm->clkdm_sleep(clkdm);
8543a090284SPaul Walmsley 	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
8553a090284SPaul Walmsley 
856555e74eaSRajendra Nayak 	return ret;
857d459bfe0SPaul Walmsley }
858d459bfe0SPaul Walmsley 
859d459bfe0SPaul Walmsley /**
8603a090284SPaul Walmsley  * clkdm_sleep - force clockdomain sleep transition
8613a090284SPaul Walmsley  * @clkdm: struct clockdomain *
8623a090284SPaul Walmsley  *
8633a090284SPaul Walmsley  * Instruct the CM to force a sleep transition on the specified
8643a090284SPaul Walmsley  * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if
8653a090284SPaul Walmsley  * clockdomain does not support software-initiated sleep; 0 upon
8663a090284SPaul Walmsley  * success.
8673a090284SPaul Walmsley  */
clkdm_sleep(struct clockdomain * clkdm)8683a090284SPaul Walmsley int clkdm_sleep(struct clockdomain *clkdm)
8693a090284SPaul Walmsley {
8703a090284SPaul Walmsley 	int ret;
8713a090284SPaul Walmsley 
8723a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
8733a090284SPaul Walmsley 	ret = clkdm_sleep_nolock(clkdm);
8743a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
8753a090284SPaul Walmsley 
8763a090284SPaul Walmsley 	return ret;
8773a090284SPaul Walmsley }
8783a090284SPaul Walmsley 
8793a090284SPaul Walmsley /**
8803a090284SPaul Walmsley  * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)
881d459bfe0SPaul Walmsley  * @clkdm: struct clockdomain *
882d459bfe0SPaul Walmsley  *
883d459bfe0SPaul Walmsley  * Instruct the CM to force a wakeup transition on the specified
8843a090284SPaul Walmsley  * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
8853a090284SPaul Walmsley  * -EINVAL if @clkdm is NULL or if the clockdomain does not support
8863a090284SPaul Walmsley  * software-controlled wakeup; 0 upon success.
887d459bfe0SPaul Walmsley  */
clkdm_wakeup_nolock(struct clockdomain * clkdm)888*6aeb51c1SArnd Bergmann static int clkdm_wakeup_nolock(struct clockdomain *clkdm)
889d459bfe0SPaul Walmsley {
890555e74eaSRajendra Nayak 	int ret;
891555e74eaSRajendra Nayak 
892d459bfe0SPaul Walmsley 	if (!clkdm)
893d459bfe0SPaul Walmsley 		return -EINVAL;
894d459bfe0SPaul Walmsley 
895d459bfe0SPaul Walmsley 	if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
8967852ec05SPaul Walmsley 		pr_debug("clockdomain: %s does not support forcing wakeup via software\n",
8977852ec05SPaul Walmsley 			 clkdm->name);
898d459bfe0SPaul Walmsley 		return -EINVAL;
899d459bfe0SPaul Walmsley 	}
900d459bfe0SPaul Walmsley 
90168b921adSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_wakeup)
90268b921adSRajendra Nayak 		return -EINVAL;
90368b921adSRajendra Nayak 
904d459bfe0SPaul Walmsley 	pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
905d459bfe0SPaul Walmsley 
90632a363c0SPaul Walmsley 	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
907555e74eaSRajendra Nayak 	ret = arch_clkdm->clkdm_wakeup(clkdm);
9083a090284SPaul Walmsley 	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
9093a090284SPaul Walmsley 
910555e74eaSRajendra Nayak 	return ret;
911d459bfe0SPaul Walmsley }
912d459bfe0SPaul Walmsley 
913d459bfe0SPaul Walmsley /**
9143a090284SPaul Walmsley  * clkdm_wakeup - force clockdomain wakeup transition
915d459bfe0SPaul Walmsley  * @clkdm: struct clockdomain *
916d459bfe0SPaul Walmsley  *
9173a090284SPaul Walmsley  * Instruct the CM to force a wakeup transition on the specified
9183a090284SPaul Walmsley  * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the
9193a090284SPaul Walmsley  * clockdomain does not support software-controlled wakeup; 0 upon
9203a090284SPaul Walmsley  * success.
9213a090284SPaul Walmsley  */
clkdm_wakeup(struct clockdomain * clkdm)9223a090284SPaul Walmsley int clkdm_wakeup(struct clockdomain *clkdm)
9233a090284SPaul Walmsley {
9243a090284SPaul Walmsley 	int ret;
9253a090284SPaul Walmsley 
9263a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
9273a090284SPaul Walmsley 	ret = clkdm_wakeup_nolock(clkdm);
9283a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
9293a090284SPaul Walmsley 
9303a090284SPaul Walmsley 	return ret;
9313a090284SPaul Walmsley }
9323a090284SPaul Walmsley 
9333a090284SPaul Walmsley /**
9343a090284SPaul Walmsley  * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm
9353a090284SPaul Walmsley  * @clkdm: struct clockdomain *
9363a090284SPaul Walmsley  *
9373a090284SPaul Walmsley  * Allow the hardware to automatically switch the clockdomain @clkdm
9383a090284SPaul Walmsley  * into active or idle states, as needed by downstream clocks.  If the
939d459bfe0SPaul Walmsley  * clockdomain has any downstream clocks enabled in the clock
940d459bfe0SPaul Walmsley  * framework, wkdep/sleepdep autodependencies are added; this is so
9413a090284SPaul Walmsley  * device drivers can read and write to the device.  Only for use by
9423a090284SPaul Walmsley  * the powerdomain code.  No return value.
943d459bfe0SPaul Walmsley  */
clkdm_allow_idle_nolock(struct clockdomain * clkdm)9443a090284SPaul Walmsley void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
945d459bfe0SPaul Walmsley {
946d459bfe0SPaul Walmsley 	if (!clkdm)
947d459bfe0SPaul Walmsley 		return;
948d459bfe0SPaul Walmsley 
9491d9a5425STero Kristo 	if (!WARN_ON(!clkdm->forcewake_count))
9501d9a5425STero Kristo 		clkdm->forcewake_count--;
9511d9a5425STero Kristo 
9521d9a5425STero Kristo 	if (clkdm->forcewake_count)
953d459bfe0SPaul Walmsley 		return;
9541d9a5425STero Kristo 
9551d9a5425STero Kristo 	if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
9561d9a5425STero Kristo 		clkdm_sleep_nolock(clkdm);
9571d9a5425STero Kristo 
9581d9a5425STero Kristo 	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
9591d9a5425STero Kristo 		return;
9601d9a5425STero Kristo 
9611d9a5425STero Kristo 	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
9621d9a5425STero Kristo 		return;
963d459bfe0SPaul Walmsley 
9645cd1937bSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
9655cd1937bSRajendra Nayak 		return;
9665cd1937bSRajendra Nayak 
967d459bfe0SPaul Walmsley 	pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
968d459bfe0SPaul Walmsley 		 clkdm->name);
969d459bfe0SPaul Walmsley 
97032a363c0SPaul Walmsley 	clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
9715cd1937bSRajendra Nayak 	arch_clkdm->clkdm_allow_idle(clkdm);
9723a090284SPaul Walmsley 	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
9733a090284SPaul Walmsley }
9743a090284SPaul Walmsley 
9753a090284SPaul Walmsley /**
9763a090284SPaul Walmsley  * clkdm_allow_idle - enable hwsup idle transitions for clkdm
9773a090284SPaul Walmsley  * @clkdm: struct clockdomain *
9783a090284SPaul Walmsley  *
9793a090284SPaul Walmsley  * Allow the hardware to automatically switch the clockdomain @clkdm into
9803a090284SPaul Walmsley  * active or idle states, as needed by downstream clocks.  If the
9813a090284SPaul Walmsley  * clockdomain has any downstream clocks enabled in the clock
9823a090284SPaul Walmsley  * framework, wkdep/sleepdep autodependencies are added; this is so
9833a090284SPaul Walmsley  * device drivers can read and write to the device.  No return value.
9843a090284SPaul Walmsley  */
clkdm_allow_idle(struct clockdomain * clkdm)9853a090284SPaul Walmsley void clkdm_allow_idle(struct clockdomain *clkdm)
9863a090284SPaul Walmsley {
9873a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
9883a090284SPaul Walmsley 	clkdm_allow_idle_nolock(clkdm);
9893a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
990d459bfe0SPaul Walmsley }
991d459bfe0SPaul Walmsley 
992d459bfe0SPaul Walmsley /**
9935cd1937bSRajendra Nayak  * clkdm_deny_idle - disable hwsup idle transitions for clkdm
994d459bfe0SPaul Walmsley  * @clkdm: struct clockdomain *
995d459bfe0SPaul Walmsley  *
996d459bfe0SPaul Walmsley  * Prevent the hardware from automatically switching the clockdomain
997f0271d65SPaul Walmsley  * @clkdm into inactive or idle states.  If the clockdomain has
998f0271d65SPaul Walmsley  * downstream clocks enabled in the clock framework, wkdep/sleepdep
9993a090284SPaul Walmsley  * autodependencies are removed.  Only for use by the powerdomain
10003a090284SPaul Walmsley  * code.  No return value.
1001d459bfe0SPaul Walmsley  */
clkdm_deny_idle_nolock(struct clockdomain * clkdm)10023a090284SPaul Walmsley void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
1003d459bfe0SPaul Walmsley {
1004d459bfe0SPaul Walmsley 	if (!clkdm)
1005d459bfe0SPaul Walmsley 		return;
1006d459bfe0SPaul Walmsley 
10071d9a5425STero Kristo 	if (clkdm->forcewake_count++)
1008d459bfe0SPaul Walmsley 		return;
10091d9a5425STero Kristo 
10101d9a5425STero Kristo 	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
10111d9a5425STero Kristo 		clkdm_wakeup_nolock(clkdm);
10121d9a5425STero Kristo 
10131d9a5425STero Kristo 	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
10141d9a5425STero Kristo 		return;
10151d9a5425STero Kristo 
10161d9a5425STero Kristo 	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
10171d9a5425STero Kristo 		return;
1018d459bfe0SPaul Walmsley 
10195cd1937bSRajendra Nayak 	if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
10205cd1937bSRajendra Nayak 		return;
10215cd1937bSRajendra Nayak 
1022d459bfe0SPaul Walmsley 	pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
1023d459bfe0SPaul Walmsley 		 clkdm->name);
1024d459bfe0SPaul Walmsley 
102532a363c0SPaul Walmsley 	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
10265cd1937bSRajendra Nayak 	arch_clkdm->clkdm_deny_idle(clkdm);
10273a090284SPaul Walmsley 	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
10283a090284SPaul Walmsley }
10293a090284SPaul Walmsley 
10303a090284SPaul Walmsley /**
10313a090284SPaul Walmsley  * clkdm_deny_idle - disable hwsup idle transitions for clkdm
10323a090284SPaul Walmsley  * @clkdm: struct clockdomain *
10333a090284SPaul Walmsley  *
10343a090284SPaul Walmsley  * Prevent the hardware from automatically switching the clockdomain
10353a090284SPaul Walmsley  * @clkdm into inactive or idle states.  If the clockdomain has
10363a090284SPaul Walmsley  * downstream clocks enabled in the clock framework, wkdep/sleepdep
10373a090284SPaul Walmsley  * autodependencies are removed.  No return value.
10383a090284SPaul Walmsley  */
clkdm_deny_idle(struct clockdomain * clkdm)10393a090284SPaul Walmsley void clkdm_deny_idle(struct clockdomain *clkdm)
10403a090284SPaul Walmsley {
10413a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
10423a090284SPaul Walmsley 	clkdm_deny_idle_nolock(clkdm);
10433a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
104491808a81SAbhijit Pagare }
1045d459bfe0SPaul Walmsley 
104665958fb6SPaul Walmsley /* Public autodep handling functions (deprecated) */
104765958fb6SPaul Walmsley 
104865958fb6SPaul Walmsley /**
104965958fb6SPaul Walmsley  * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
105065958fb6SPaul Walmsley  * @clkdm: struct clockdomain *
105165958fb6SPaul Walmsley  *
105265958fb6SPaul Walmsley  * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
105365958fb6SPaul Walmsley  * in hardware-supervised mode.  Meant to be called from clock framework
105465958fb6SPaul Walmsley  * when a clock inside clockdomain 'clkdm' is enabled.	No return value.
105565958fb6SPaul Walmsley  *
105665958fb6SPaul Walmsley  * XXX autodeps are deprecated and should be removed at the earliest
105765958fb6SPaul Walmsley  * opportunity
105865958fb6SPaul Walmsley  */
clkdm_add_autodeps(struct clockdomain * clkdm)105965958fb6SPaul Walmsley void clkdm_add_autodeps(struct clockdomain *clkdm)
106065958fb6SPaul Walmsley {
106165958fb6SPaul Walmsley 	struct clkdm_autodep *autodep;
106265958fb6SPaul Walmsley 
106365958fb6SPaul Walmsley 	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
106465958fb6SPaul Walmsley 		return;
106565958fb6SPaul Walmsley 
106665958fb6SPaul Walmsley 	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
106765958fb6SPaul Walmsley 		if (IS_ERR(autodep->clkdm.ptr))
106865958fb6SPaul Walmsley 			continue;
106965958fb6SPaul Walmsley 
107065958fb6SPaul Walmsley 		pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
107165958fb6SPaul Walmsley 			 clkdm->name, autodep->clkdm.ptr->name);
107265958fb6SPaul Walmsley 
107365958fb6SPaul Walmsley 		_clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
107465958fb6SPaul Walmsley 		_clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
107565958fb6SPaul Walmsley 	}
107665958fb6SPaul Walmsley }
107765958fb6SPaul Walmsley 
107865958fb6SPaul Walmsley /**
107965958fb6SPaul Walmsley  * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm
108065958fb6SPaul Walmsley  * @clkdm: struct clockdomain *
108165958fb6SPaul Walmsley  *
108265958fb6SPaul Walmsley  * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
108365958fb6SPaul Walmsley  * in hardware-supervised mode.  Meant to be called from clock framework
108465958fb6SPaul Walmsley  * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
108565958fb6SPaul Walmsley  *
108665958fb6SPaul Walmsley  * XXX autodeps are deprecated and should be removed at the earliest
108765958fb6SPaul Walmsley  * opportunity
108865958fb6SPaul Walmsley  */
clkdm_del_autodeps(struct clockdomain * clkdm)108965958fb6SPaul Walmsley void clkdm_del_autodeps(struct clockdomain *clkdm)
109065958fb6SPaul Walmsley {
109165958fb6SPaul Walmsley 	struct clkdm_autodep *autodep;
109265958fb6SPaul Walmsley 
109365958fb6SPaul Walmsley 	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
109465958fb6SPaul Walmsley 		return;
109565958fb6SPaul Walmsley 
109665958fb6SPaul Walmsley 	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
109765958fb6SPaul Walmsley 		if (IS_ERR(autodep->clkdm.ptr))
109865958fb6SPaul Walmsley 			continue;
109965958fb6SPaul Walmsley 
110065958fb6SPaul Walmsley 		pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
110165958fb6SPaul Walmsley 			 clkdm->name, autodep->clkdm.ptr->name);
110265958fb6SPaul Walmsley 
110365958fb6SPaul Walmsley 		_clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
110465958fb6SPaul Walmsley 		_clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
110565958fb6SPaul Walmsley 	}
110665958fb6SPaul Walmsley }
110765958fb6SPaul Walmsley 
1108113a7413SBenoit Cousson /* Clockdomain-to-clock/hwmod framework interface code */
1109113a7413SBenoit Cousson 
111021a18129STony Lindgren /**
111121a18129STony Lindgren  * clkdm_clk_enable - add an enabled downstream clock to this clkdm
111221a18129STony Lindgren  * @clkdm: struct clockdomain *
111321a18129STony Lindgren  * @clk: struct clk * of the enabled downstream clock
111421a18129STony Lindgren  *
111521a18129STony Lindgren  * Increment the usecount of the clockdomain @clkdm and ensure that it
111621a18129STony Lindgren  * is awake before @clk is enabled.  Intended to be called by
111721a18129STony Lindgren  * clk_enable() code.  If the clockdomain is in software-supervised
111821a18129STony Lindgren  * idle mode, force the clockdomain to wake.  If the clockdomain is in
111921a18129STony Lindgren  * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to
112021a18129STony Lindgren  * ensure that devices in the clockdomain can be read from/written to
112121a18129STony Lindgren  * by on-chip processors.  Returns -EINVAL if passed null pointers;
112221a18129STony Lindgren  * returns 0 upon success or if the clockdomain is in hwsup idle mode.
112321a18129STony Lindgren  */
clkdm_clk_enable(struct clockdomain * clkdm,struct clk * unused)112421a18129STony Lindgren int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *unused)
1125113a7413SBenoit Cousson {
1126113a7413SBenoit Cousson 	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
1127113a7413SBenoit Cousson 		return -EINVAL;
1128113a7413SBenoit Cousson 
11293a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
113064e29fd5STero Kristo 
1131113a7413SBenoit Cousson 	/*
1132113a7413SBenoit Cousson 	 * For arch's with no autodeps, clkcm_clk_enable
1133113a7413SBenoit Cousson 	 * should be called for every clock instance or hwmod that is
1134113a7413SBenoit Cousson 	 * enabled, so the clkdm can be force woken up.
1135113a7413SBenoit Cousson 	 */
113692493870SPaul Walmsley 	clkdm->usecount++;
113792493870SPaul Walmsley 	if (clkdm->usecount > 1 && autodeps) {
11383a090284SPaul Walmsley 		pwrdm_unlock(clkdm->pwrdm.ptr);
1139113a7413SBenoit Cousson 		return 0;
114064e29fd5STero Kristo 	}
1141113a7413SBenoit Cousson 
1142113a7413SBenoit Cousson 	arch_clkdm->clkdm_clk_enable(clkdm);
11433a090284SPaul Walmsley 	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
11443a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
1145113a7413SBenoit Cousson 
11467852ec05SPaul Walmsley 	pr_debug("clockdomain: %s: enabled\n", clkdm->name);
1147113a7413SBenoit Cousson 
1148113a7413SBenoit Cousson 	return 0;
1149113a7413SBenoit Cousson }
1150113a7413SBenoit Cousson 
1151d459bfe0SPaul Walmsley /**
11524da71ae6SRajendra Nayak  * clkdm_clk_disable - remove an enabled downstream clock from this clkdm
1153d459bfe0SPaul Walmsley  * @clkdm: struct clockdomain *
1154d459bfe0SPaul Walmsley  * @clk: struct clk * of the disabled downstream clock
1155d459bfe0SPaul Walmsley  *
1156f0271d65SPaul Walmsley  * Decrement the usecount of this clockdomain @clkdm when @clk is
1157f0271d65SPaul Walmsley  * disabled.  Intended to be called by clk_disable() code.  If the
1158f0271d65SPaul Walmsley  * clockdomain usecount goes to 0, put the clockdomain to sleep
1159f0271d65SPaul Walmsley  * (software-supervised mode) or remove the clkdm autodependencies
1160f0271d65SPaul Walmsley  * (hardware-supervised mode).  Returns -EINVAL if passed null
1161113a7413SBenoit Cousson  * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
1162113a7413SBenoit Cousson  * upon success or if the clockdomain is in hwsup idle mode.
1163d459bfe0SPaul Walmsley  */
clkdm_clk_disable(struct clockdomain * clkdm,struct clk * clk)11644da71ae6SRajendra Nayak int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
1165d459bfe0SPaul Walmsley {
116621a18129STony Lindgren 	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
1167d459bfe0SPaul Walmsley 		return -EINVAL;
1168d459bfe0SPaul Walmsley 
11693a090284SPaul Walmsley 	pwrdm_lock(clkdm->pwrdm.ptr);
1170d043d87cSMike Turquette 
1171d043d87cSMike Turquette 	/* corner case: disabling unused clocks */
117221a18129STony Lindgren 	if (clk && (__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0)
1173d043d87cSMike Turquette 		goto ccd_exit;
1174d043d87cSMike Turquette 
117592493870SPaul Walmsley 	if (clkdm->usecount == 0) {
11763a090284SPaul Walmsley 		pwrdm_unlock(clkdm->pwrdm.ptr);
1177d043d87cSMike Turquette 		WARN_ON(1); /* underflow */
1178d043d87cSMike Turquette 		return -ERANGE;
1179d043d87cSMike Turquette 	}
1180d043d87cSMike Turquette 
118192493870SPaul Walmsley 	clkdm->usecount--;
118292493870SPaul Walmsley 	if (clkdm->usecount > 0) {
11833a090284SPaul Walmsley 		pwrdm_unlock(clkdm->pwrdm.ptr);
1184d043d87cSMike Turquette 		return 0;
1185d043d87cSMike Turquette 	}
1186d043d87cSMike Turquette 
1187d043d87cSMike Turquette 	arch_clkdm->clkdm_clk_disable(clkdm);
11883a090284SPaul Walmsley 	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
1189d043d87cSMike Turquette 
1190d043d87cSMike Turquette 	pr_debug("clockdomain: %s: disabled\n", clkdm->name);
1191d043d87cSMike Turquette 
1192d043d87cSMike Turquette ccd_exit:
11933a090284SPaul Walmsley 	pwrdm_unlock(clkdm->pwrdm.ptr);
1194d043d87cSMike Turquette 
1195d043d87cSMike Turquette 	return 0;
1196d459bfe0SPaul Walmsley }
1197d459bfe0SPaul Walmsley 
1198113a7413SBenoit Cousson /**
1199113a7413SBenoit Cousson  * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
1200113a7413SBenoit Cousson  * @clkdm: struct clockdomain *
1201113a7413SBenoit Cousson  * @oh: struct omap_hwmod * of the enabled downstream hwmod
1202113a7413SBenoit Cousson  *
1203113a7413SBenoit Cousson  * Increment the usecount of the clockdomain @clkdm and ensure that it
1204113a7413SBenoit Cousson  * is awake before @oh is enabled. Intended to be called by
1205113a7413SBenoit Cousson  * module_enable() code.
1206113a7413SBenoit Cousson  * If the clockdomain is in software-supervised idle mode, force the
1207113a7413SBenoit Cousson  * clockdomain to wake.  If the clockdomain is in hardware-supervised idle
1208113a7413SBenoit Cousson  * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
1209113a7413SBenoit Cousson  * clockdomain can be read from/written to by on-chip processors.
1210113a7413SBenoit Cousson  * Returns -EINVAL if passed null pointers;
1211113a7413SBenoit Cousson  * returns 0 upon success or if the clockdomain is in hwsup idle mode.
1212113a7413SBenoit Cousson  */
clkdm_hwmod_enable(struct clockdomain * clkdm,struct omap_hwmod * oh)1213113a7413SBenoit Cousson int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
1214113a7413SBenoit Cousson {
1215113a7413SBenoit Cousson 	/* The clkdm attribute does not exist yet prior OMAP4 */
1216113a7413SBenoit Cousson 	if (cpu_is_omap24xx() || cpu_is_omap34xx())
1217d459bfe0SPaul Walmsley 		return 0;
1218d459bfe0SPaul Walmsley 
1219113a7413SBenoit Cousson 	/*
1220113a7413SBenoit Cousson 	 * XXX Rewrite this code to maintain a list of enabled
1221113a7413SBenoit Cousson 	 * downstream hwmods for debugging purposes?
1222113a7413SBenoit Cousson 	 */
1223d459bfe0SPaul Walmsley 
1224113a7413SBenoit Cousson 	if (!oh)
1225113a7413SBenoit Cousson 		return -EINVAL;
1226d459bfe0SPaul Walmsley 
122721a18129STony Lindgren 	return clkdm_clk_enable(clkdm, NULL);
1228113a7413SBenoit Cousson }
1229fe617af7SPeter 'p2' De Schrijver 
1230113a7413SBenoit Cousson /**
1231113a7413SBenoit Cousson  * clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
1232113a7413SBenoit Cousson  * @clkdm: struct clockdomain *
1233113a7413SBenoit Cousson  * @oh: struct omap_hwmod * of the disabled downstream hwmod
1234113a7413SBenoit Cousson  *
1235113a7413SBenoit Cousson  * Decrement the usecount of this clockdomain @clkdm when @oh is
1236113a7413SBenoit Cousson  * disabled. Intended to be called by module_disable() code.
1237113a7413SBenoit Cousson  * If the clockdomain usecount goes to 0, put the clockdomain to sleep
1238113a7413SBenoit Cousson  * (software-supervised mode) or remove the clkdm autodependencies
1239113a7413SBenoit Cousson  * (hardware-supervised mode).
1240113a7413SBenoit Cousson  * Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
1241113a7413SBenoit Cousson  * underflows; or returns 0 upon success or if the clockdomain is in hwsup
1242113a7413SBenoit Cousson  * idle mode.
1243113a7413SBenoit Cousson  */
clkdm_hwmod_disable(struct clockdomain * clkdm,struct omap_hwmod * oh)1244113a7413SBenoit Cousson int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
1245113a7413SBenoit Cousson {
1246113a7413SBenoit Cousson 	/* The clkdm attribute does not exist yet prior OMAP4 */
1247113a7413SBenoit Cousson 	if (cpu_is_omap24xx() || cpu_is_omap34xx())
1248d459bfe0SPaul Walmsley 		return 0;
1249113a7413SBenoit Cousson 
125021a18129STony Lindgren 	if (!oh)
1251113a7413SBenoit Cousson 		return -EINVAL;
1252113a7413SBenoit Cousson 
125321a18129STony Lindgren 	return clkdm_clk_disable(clkdm, NULL);
1254d459bfe0SPaul Walmsley }
1255d459bfe0SPaul Walmsley 
12561096d1c1SRuss Dill /**
12571096d1c1SRuss Dill  * _clkdm_save_context - save the context for the control of this clkdm
12581096d1c1SRuss Dill  *
12591096d1c1SRuss Dill  * Due to a suspend or hibernation operation, the state of the registers
12601096d1c1SRuss Dill  * controlling this clkdm will be lost, save their context.
12611096d1c1SRuss Dill  */
_clkdm_save_context(struct clockdomain * clkdm,void * unused)1262320f6f90SWang Qing static int _clkdm_save_context(struct clockdomain *clkdm, void *unused)
12631096d1c1SRuss Dill {
12641096d1c1SRuss Dill 	if (!arch_clkdm || !arch_clkdm->clkdm_save_context)
12651096d1c1SRuss Dill 		return -EINVAL;
12661096d1c1SRuss Dill 
12671096d1c1SRuss Dill 	return arch_clkdm->clkdm_save_context(clkdm);
12681096d1c1SRuss Dill }
12691096d1c1SRuss Dill 
12701096d1c1SRuss Dill /**
12711096d1c1SRuss Dill  * _clkdm_restore_context - restore context for control of this clkdm
12721096d1c1SRuss Dill  *
12731096d1c1SRuss Dill  * Restore the register values for this clockdomain.
12741096d1c1SRuss Dill  */
_clkdm_restore_context(struct clockdomain * clkdm,void * unused)1275320f6f90SWang Qing static int _clkdm_restore_context(struct clockdomain *clkdm, void *unused)
12761096d1c1SRuss Dill {
12771096d1c1SRuss Dill 	if (!arch_clkdm || !arch_clkdm->clkdm_restore_context)
12781096d1c1SRuss Dill 		return -EINVAL;
12791096d1c1SRuss Dill 
12801096d1c1SRuss Dill 	return arch_clkdm->clkdm_restore_context(clkdm);
12811096d1c1SRuss Dill }
12821096d1c1SRuss Dill 
12831096d1c1SRuss Dill /**
12841096d1c1SRuss Dill  * clkdm_save_context - Saves the context for each registered clkdm
12851096d1c1SRuss Dill  *
12861096d1c1SRuss Dill  * Save the context for each registered clockdomain.
12871096d1c1SRuss Dill  */
clkdm_save_context(void)12881096d1c1SRuss Dill void clkdm_save_context(void)
12891096d1c1SRuss Dill {
12901096d1c1SRuss Dill 	clkdm_for_each(_clkdm_save_context, NULL);
12911096d1c1SRuss Dill }
12921096d1c1SRuss Dill 
12931096d1c1SRuss Dill /**
12941096d1c1SRuss Dill  * clkdm_restore_context - Restores the context for each registered clkdm
12951096d1c1SRuss Dill  *
12961096d1c1SRuss Dill  * Restore the context for each registered clockdomain.
12971096d1c1SRuss Dill  */
clkdm_restore_context(void)12981096d1c1SRuss Dill void clkdm_restore_context(void)
12991096d1c1SRuss Dill {
13001096d1c1SRuss Dill 	clkdm_for_each(_clkdm_restore_context, NULL);
13011096d1c1SRuss Dill }
1302