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