18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OMAP2/3/4 clockdomain framework functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2011 Texas Instruments, Inc.
68c2ecf20Sopenharmony_ci * Copyright (C) 2008-2011 Nokia Corporation
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Written by Paul Walmsley and Jouni Högander
98c2ecf20Sopenharmony_ci * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#undef DEBUG
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/clk.h>
208c2ecf20Sopenharmony_ci#include <linux/limits.h>
218c2ecf20Sopenharmony_ci#include <linux/err.h>
228c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
238c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/io.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/bitops.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "soc.h"
308c2ecf20Sopenharmony_ci#include "clock.h"
318c2ecf20Sopenharmony_ci#include "clockdomain.h"
328c2ecf20Sopenharmony_ci#include "pm.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* clkdm_list contains all registered struct clockdomains */
358c2ecf20Sopenharmony_cistatic LIST_HEAD(clkdm_list);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* array of clockdomain deps to be added/removed when clkdm in hwsup mode */
388c2ecf20Sopenharmony_cistatic struct clkdm_autodep *autodeps;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct clkdm_ops *arch_clkdm;
418c2ecf20Sopenharmony_civoid clkdm_save_context(void);
428c2ecf20Sopenharmony_civoid clkdm_restore_context(void);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Private functions */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct clockdomain *_clkdm_lookup(const char *name)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct clockdomain *clkdm, *temp_clkdm;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (!name)
518c2ecf20Sopenharmony_ci		return NULL;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	clkdm = NULL;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
568c2ecf20Sopenharmony_ci		if (!strcmp(name, temp_clkdm->name)) {
578c2ecf20Sopenharmony_ci			clkdm = temp_clkdm;
588c2ecf20Sopenharmony_ci			break;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return clkdm;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * _clkdm_register - register a clockdomain
678c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain * to register
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * Adds a clockdomain to the internal clockdomain list.
708c2ecf20Sopenharmony_ci * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
718c2ecf20Sopenharmony_ci * already registered by the provided name, or 0 upon success.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistatic int _clkdm_register(struct clockdomain *clkdm)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct powerdomain *pwrdm;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!clkdm || !clkdm->name)
788c2ecf20Sopenharmony_ci		return -EINVAL;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
818c2ecf20Sopenharmony_ci	if (!pwrdm) {
828c2ecf20Sopenharmony_ci		pr_err("clockdomain: %s: powerdomain %s does not exist\n",
838c2ecf20Sopenharmony_ci			clkdm->name, clkdm->pwrdm.name);
848c2ecf20Sopenharmony_ci		return -EINVAL;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	clkdm->pwrdm.ptr = pwrdm;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Verify that the clockdomain is not already registered */
898c2ecf20Sopenharmony_ci	if (_clkdm_lookup(clkdm->name))
908c2ecf20Sopenharmony_ci		return -EEXIST;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	list_add(&clkdm->node, &clkdm_list);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	pwrdm_add_clkdm(pwrdm, clkdm);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	pr_debug("clockdomain: registered %s\n", clkdm->name);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */
1028c2ecf20Sopenharmony_cistatic struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
1038c2ecf20Sopenharmony_ci					    struct clkdm_dep *deps)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!clkdm || !deps)
1088c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (cd = deps; cd->clkdm_name; cd++) {
1118c2ecf20Sopenharmony_ci		if (!cd->clkdm && cd->clkdm_name)
1128c2ecf20Sopenharmony_ci			cd->clkdm = _clkdm_lookup(cd->clkdm_name);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (cd->clkdm == clkdm)
1158c2ecf20Sopenharmony_ci			break;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (!cd->clkdm_name)
1198c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return cd;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/**
1258c2ecf20Sopenharmony_ci * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
1268c2ecf20Sopenharmony_ci * @autodep: struct clkdm_autodep * to resolve
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * Resolve autodep clockdomain names to clockdomain pointers via
1298c2ecf20Sopenharmony_ci * clkdm_lookup() and store the pointers in the autodep structure.  An
1308c2ecf20Sopenharmony_ci * "autodep" is a clockdomain sleep/wakeup dependency that is
1318c2ecf20Sopenharmony_ci * automatically added and removed whenever clocks in the associated
1328c2ecf20Sopenharmony_ci * clockdomain are enabled or disabled (respectively) when the
1338c2ecf20Sopenharmony_ci * clockdomain is in hardware-supervised mode.	Meant to be called
1348c2ecf20Sopenharmony_ci * once at clockdomain layer initialization, since these should remain
1358c2ecf20Sopenharmony_ci * fixed for a particular architecture.  No return value.
1368c2ecf20Sopenharmony_ci *
1378c2ecf20Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
1388c2ecf20Sopenharmony_ci * opportunity
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic void _autodep_lookup(struct clkdm_autodep *autodep)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct clockdomain *clkdm;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!autodep)
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	clkdm = clkdm_lookup(autodep->clkdm.name);
1488c2ecf20Sopenharmony_ci	if (!clkdm) {
1498c2ecf20Sopenharmony_ci		pr_err("clockdomain: autodeps: clockdomain %s does not exist\n",
1508c2ecf20Sopenharmony_ci			 autodep->clkdm.name);
1518c2ecf20Sopenharmony_ci		clkdm = ERR_PTR(-ENOENT);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	autodep->clkdm.ptr = clkdm;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/**
1578c2ecf20Sopenharmony_ci * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
1588c2ecf20Sopenharmony_ci * @clkdm: clockdomain that we are resolving dependencies for
1598c2ecf20Sopenharmony_ci * @clkdm_deps: ptr to array of struct clkdm_deps to resolve
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * Iterates through @clkdm_deps, looking up the struct clockdomain named by
1628c2ecf20Sopenharmony_ci * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
1638c2ecf20Sopenharmony_ci * No return value.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic void _resolve_clkdm_deps(struct clockdomain *clkdm,
1668c2ecf20Sopenharmony_ci				struct clkdm_dep *clkdm_deps)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
1718c2ecf20Sopenharmony_ci		if (cd->clkdm)
1728c2ecf20Sopenharmony_ci			continue;
1738c2ecf20Sopenharmony_ci		cd->clkdm = _clkdm_lookup(cd->clkdm_name);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
1768c2ecf20Sopenharmony_ci		     clkdm->name, cd->clkdm_name);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/**
1818c2ecf20Sopenharmony_ci * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless)
1828c2ecf20Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
1838c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * When the clockdomain represented by @clkdm2 wakes up, wake up
1868c2ecf20Sopenharmony_ci * @clkdm1. Implemented in hardware on the OMAP, this feature is
1878c2ecf20Sopenharmony_ci * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
1888c2ecf20Sopenharmony_ci * Returns -EINVAL if presented with invalid clockdomain pointers,
1898c2ecf20Sopenharmony_ci * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
1908c2ecf20Sopenharmony_ci * success.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic int _clkdm_add_wkdep(struct clockdomain *clkdm1,
1938c2ecf20Sopenharmony_ci			    struct clockdomain *clkdm2)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
1968c2ecf20Sopenharmony_ci	int ret = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
2028c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
2038c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
2068c2ecf20Sopenharmony_ci		ret = -EINVAL;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (ret) {
2098c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
2108c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	cd->wkdep_usecount++;
2158c2ecf20Sopenharmony_ci	if (cd->wkdep_usecount == 1) {
2168c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
2178c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return ret;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/**
2268c2ecf20Sopenharmony_ci * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless)
2278c2ecf20Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
2288c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
2318c2ecf20Sopenharmony_ci * wakes up.  Returns -EINVAL if presented with invalid clockdomain
2328c2ecf20Sopenharmony_ci * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
2338c2ecf20Sopenharmony_ci * 0 upon success.
2348c2ecf20Sopenharmony_ci */
2358c2ecf20Sopenharmony_cistatic int _clkdm_del_wkdep(struct clockdomain *clkdm1,
2368c2ecf20Sopenharmony_ci			    struct clockdomain *clkdm2)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
2398c2ecf20Sopenharmony_ci	int ret = 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
2428c2ecf20Sopenharmony_ci		return -EINVAL;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
2458c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
2468c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
2498c2ecf20Sopenharmony_ci		ret = -EINVAL;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (ret) {
2528c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
2538c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
2548c2ecf20Sopenharmony_ci		return ret;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	cd->wkdep_usecount--;
2588c2ecf20Sopenharmony_ci	if (cd->wkdep_usecount == 0) {
2598c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
2608c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return ret;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/**
2698c2ecf20Sopenharmony_ci * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless)
2708c2ecf20Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
2718c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Prevent @clkdm1 from automatically going inactive (and then to
2748c2ecf20Sopenharmony_ci * retention or off) if @clkdm2 is active.  Returns -EINVAL if
2758c2ecf20Sopenharmony_ci * presented with invalid clockdomain pointers or called on a machine
2768c2ecf20Sopenharmony_ci * that does not support software-configurable hardware sleep
2778c2ecf20Sopenharmony_ci * dependencies, -ENOENT if the specified dependency cannot be set in
2788c2ecf20Sopenharmony_ci * hardware, or 0 upon success.
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_cistatic int _clkdm_add_sleepdep(struct clockdomain *clkdm1,
2818c2ecf20Sopenharmony_ci			       struct clockdomain *clkdm2)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
2848c2ecf20Sopenharmony_ci	int ret = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
2878c2ecf20Sopenharmony_ci		return -EINVAL;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
2908c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
2918c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
2948c2ecf20Sopenharmony_ci		ret = -EINVAL;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (ret) {
2978c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
2988c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	cd->sleepdep_usecount++;
3038c2ecf20Sopenharmony_ci	if (cd->sleepdep_usecount == 1) {
3048c2ecf20Sopenharmony_ci		pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
3058c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return ret;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/**
3148c2ecf20Sopenharmony_ci * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless)
3158c2ecf20Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
3168c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
3178c2ecf20Sopenharmony_ci *
3188c2ecf20Sopenharmony_ci * Allow @clkdm1 to automatically go inactive (and then to retention or
3198c2ecf20Sopenharmony_ci * off), independent of the activity state of @clkdm2.  Returns -EINVAL
3208c2ecf20Sopenharmony_ci * if presented with invalid clockdomain pointers or called on a machine
3218c2ecf20Sopenharmony_ci * that does not support software-configurable hardware sleep dependencies,
3228c2ecf20Sopenharmony_ci * -ENOENT if the specified dependency cannot be cleared in hardware, or
3238c2ecf20Sopenharmony_ci * 0 upon success.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic int _clkdm_del_sleepdep(struct clockdomain *clkdm1,
3268c2ecf20Sopenharmony_ci			       struct clockdomain *clkdm2)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
3298c2ecf20Sopenharmony_ci	int ret = 0;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
3328c2ecf20Sopenharmony_ci		return -EINVAL;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
3358c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
3368c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
3398c2ecf20Sopenharmony_ci		ret = -EINVAL;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (ret) {
3428c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
3438c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
3448c2ecf20Sopenharmony_ci		return ret;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	cd->sleepdep_usecount--;
3488c2ecf20Sopenharmony_ci	if (cd->sleepdep_usecount == 0) {
3498c2ecf20Sopenharmony_ci		pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
3508c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return ret;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* Public functions */
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/**
3618c2ecf20Sopenharmony_ci * clkdm_register_platform_funcs - register clockdomain implementation fns
3628c2ecf20Sopenharmony_ci * @co: func pointers for arch specific implementations
3638c2ecf20Sopenharmony_ci *
3648c2ecf20Sopenharmony_ci * Register the list of function pointers used to implement the
3658c2ecf20Sopenharmony_ci * clockdomain functions on different OMAP SoCs.  Should be called
3668c2ecf20Sopenharmony_ci * before any other clkdm_register*() function.  Returns -EINVAL if
3678c2ecf20Sopenharmony_ci * @co is null, -EEXIST if platform functions have already been
3688c2ecf20Sopenharmony_ci * registered, or 0 upon success.
3698c2ecf20Sopenharmony_ci */
3708c2ecf20Sopenharmony_ciint clkdm_register_platform_funcs(struct clkdm_ops *co)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	if (!co)
3738c2ecf20Sopenharmony_ci		return -EINVAL;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (arch_clkdm)
3768c2ecf20Sopenharmony_ci		return -EEXIST;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	arch_clkdm = co;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci};
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/**
3848c2ecf20Sopenharmony_ci * clkdm_register_clkdms - register SoC clockdomains
3858c2ecf20Sopenharmony_ci * @cs: pointer to an array of struct clockdomain to register
3868c2ecf20Sopenharmony_ci *
3878c2ecf20Sopenharmony_ci * Register the clockdomains available on a particular OMAP SoC.  Must
3888c2ecf20Sopenharmony_ci * be called after clkdm_register_platform_funcs().  May be called
3898c2ecf20Sopenharmony_ci * multiple times.  Returns -EACCES if called before
3908c2ecf20Sopenharmony_ci * clkdm_register_platform_funcs(); -EINVAL if the argument @cs is
3918c2ecf20Sopenharmony_ci * null; or 0 upon success.
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_ciint clkdm_register_clkdms(struct clockdomain **cs)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct clockdomain **c = NULL;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (!arch_clkdm)
3988c2ecf20Sopenharmony_ci		return -EACCES;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (!cs)
4018c2ecf20Sopenharmony_ci		return -EINVAL;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	for (c = cs; *c; c++)
4048c2ecf20Sopenharmony_ci		_clkdm_register(*c);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/**
4108c2ecf20Sopenharmony_ci * clkdm_register_autodeps - register autodeps (if required)
4118c2ecf20Sopenharmony_ci * @ia: pointer to a static array of struct clkdm_autodep to register
4128c2ecf20Sopenharmony_ci *
4138c2ecf20Sopenharmony_ci * Register clockdomain "automatic dependencies."  These are
4148c2ecf20Sopenharmony_ci * clockdomain wakeup and sleep dependencies that are automatically
4158c2ecf20Sopenharmony_ci * added whenever the first clock inside a clockdomain is enabled, and
4168c2ecf20Sopenharmony_ci * removed whenever the last clock inside a clockdomain is disabled.
4178c2ecf20Sopenharmony_ci * These are currently only used on OMAP3 devices, and are deprecated,
4188c2ecf20Sopenharmony_ci * since they waste energy.  However, until the OMAP2/3 IP block
4198c2ecf20Sopenharmony_ci * enable/disable sequence can be converted to match the OMAP4
4208c2ecf20Sopenharmony_ci * sequence, they are needed.
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Must be called only after all of the SoC clockdomains are
4238c2ecf20Sopenharmony_ci * registered, since the function will resolve autodep clockdomain
4248c2ecf20Sopenharmony_ci * names into clockdomain pointers.
4258c2ecf20Sopenharmony_ci *
4268c2ecf20Sopenharmony_ci * The struct clkdm_autodep @ia array must be static, as this function
4278c2ecf20Sopenharmony_ci * does not copy the array elements.
4288c2ecf20Sopenharmony_ci *
4298c2ecf20Sopenharmony_ci * Returns -EACCES if called before any clockdomains have been
4308c2ecf20Sopenharmony_ci * registered, -EINVAL if called with a null @ia argument, -EEXIST if
4318c2ecf20Sopenharmony_ci * autodeps have already been registered, or 0 upon success.
4328c2ecf20Sopenharmony_ci */
4338c2ecf20Sopenharmony_ciint clkdm_register_autodeps(struct clkdm_autodep *ia)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct clkdm_autodep *a = NULL;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (list_empty(&clkdm_list))
4388c2ecf20Sopenharmony_ci		return -EACCES;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (!ia)
4418c2ecf20Sopenharmony_ci		return -EINVAL;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (autodeps)
4448c2ecf20Sopenharmony_ci		return -EEXIST;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	autodeps = ia;
4478c2ecf20Sopenharmony_ci	for (a = autodeps; a->clkdm.ptr; a++)
4488c2ecf20Sopenharmony_ci		_autodep_lookup(a);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	switch (cmd) {
4568c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
4578c2ecf20Sopenharmony_ci		if (enable_off_mode)
4588c2ecf20Sopenharmony_ci			clkdm_save_context();
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
4618c2ecf20Sopenharmony_ci		if (enable_off_mode)
4628c2ecf20Sopenharmony_ci			clkdm_restore_context();
4638c2ecf20Sopenharmony_ci		break;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return NOTIFY_OK;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci/**
4708c2ecf20Sopenharmony_ci * clkdm_complete_init - set up the clockdomain layer
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci * Put all clockdomains into software-supervised mode; PM code should
4738c2ecf20Sopenharmony_ci * later enable hardware-supervised mode as appropriate.  Must be
4748c2ecf20Sopenharmony_ci * called after clkdm_register_clkdms().  Returns -EACCES if called
4758c2ecf20Sopenharmony_ci * before clkdm_register_clkdms(), or 0 upon success.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_ciint clkdm_complete_init(void)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct clockdomain *clkdm;
4808c2ecf20Sopenharmony_ci	static struct notifier_block nb;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (list_empty(&clkdm_list))
4838c2ecf20Sopenharmony_ci		return -EACCES;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	list_for_each_entry(clkdm, &clkdm_list, node) {
4868c2ecf20Sopenharmony_ci		clkdm_deny_idle(clkdm);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		_resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
4898c2ecf20Sopenharmony_ci		clkdm_clear_all_wkdeps(clkdm);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		_resolve_clkdm_deps(clkdm, clkdm->sleepdep_srcs);
4928c2ecf20Sopenharmony_ci		clkdm_clear_all_sleepdeps(clkdm);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* Only AM43XX can lose clkdm context during rtc-ddr suspend */
4968c2ecf20Sopenharmony_ci	if (soc_is_am43xx()) {
4978c2ecf20Sopenharmony_ci		nb.notifier_call = cpu_notifier;
4988c2ecf20Sopenharmony_ci		cpu_pm_register_notifier(&nb);
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/**
5058c2ecf20Sopenharmony_ci * clkdm_lookup - look up a clockdomain by name, return a pointer
5068c2ecf20Sopenharmony_ci * @name: name of clockdomain
5078c2ecf20Sopenharmony_ci *
5088c2ecf20Sopenharmony_ci * Find a registered clockdomain by its name @name.  Returns a pointer
5098c2ecf20Sopenharmony_ci * to the struct clockdomain if found, or NULL otherwise.
5108c2ecf20Sopenharmony_ci */
5118c2ecf20Sopenharmony_cistruct clockdomain *clkdm_lookup(const char *name)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct clockdomain *clkdm, *temp_clkdm;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (!name)
5168c2ecf20Sopenharmony_ci		return NULL;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	clkdm = NULL;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	list_for_each_entry(temp_clkdm, &clkdm_list, node) {
5218c2ecf20Sopenharmony_ci		if (!strcmp(name, temp_clkdm->name)) {
5228c2ecf20Sopenharmony_ci			clkdm = temp_clkdm;
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return clkdm;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/**
5318c2ecf20Sopenharmony_ci * clkdm_for_each - call function on each registered clockdomain
5328c2ecf20Sopenharmony_ci * @fn: callback function *
5338c2ecf20Sopenharmony_ci *
5348c2ecf20Sopenharmony_ci * Call the supplied function @fn for each registered clockdomain.
5358c2ecf20Sopenharmony_ci * The callback function @fn can return anything but 0 to bail
5368c2ecf20Sopenharmony_ci * out early from the iterator.  The callback function is called with
5378c2ecf20Sopenharmony_ci * the clkdm_mutex held, so no clockdomain structure manipulation
5388c2ecf20Sopenharmony_ci * functions should be called from the callback, although hardware
5398c2ecf20Sopenharmony_ci * clockdomain control functions are fine.  Returns the last return
5408c2ecf20Sopenharmony_ci * value of the callback function, which should be 0 for success or
5418c2ecf20Sopenharmony_ci * anything else to indicate failure; or -EINVAL if the function pointer
5428c2ecf20Sopenharmony_ci * is null.
5438c2ecf20Sopenharmony_ci */
5448c2ecf20Sopenharmony_ciint clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user),
5458c2ecf20Sopenharmony_ci			void *user)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct clockdomain *clkdm;
5488c2ecf20Sopenharmony_ci	int ret = 0;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (!fn)
5518c2ecf20Sopenharmony_ci		return -EINVAL;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	list_for_each_entry(clkdm, &clkdm_list, node) {
5548c2ecf20Sopenharmony_ci		ret = (*fn)(clkdm, user);
5558c2ecf20Sopenharmony_ci		if (ret)
5568c2ecf20Sopenharmony_ci			break;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return ret;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci/**
5648c2ecf20Sopenharmony_ci * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
5658c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
5668c2ecf20Sopenharmony_ci *
5678c2ecf20Sopenharmony_ci * Return a pointer to the struct powerdomain that the specified clockdomain
5688c2ecf20Sopenharmony_ci * @clkdm exists in, or returns NULL if @clkdm is NULL.
5698c2ecf20Sopenharmony_ci */
5708c2ecf20Sopenharmony_cistruct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	if (!clkdm)
5738c2ecf20Sopenharmony_ci		return NULL;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return clkdm->pwrdm.ptr;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci/* Hardware clockdomain control */
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci/**
5828c2ecf20Sopenharmony_ci * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
5838c2ecf20Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
5848c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
5858c2ecf20Sopenharmony_ci *
5868c2ecf20Sopenharmony_ci * When the clockdomain represented by @clkdm2 wakes up, wake up
5878c2ecf20Sopenharmony_ci * @clkdm1. Implemented in hardware on the OMAP, this feature is
5888c2ecf20Sopenharmony_ci * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
5898c2ecf20Sopenharmony_ci * Returns -EINVAL if presented with invalid clockdomain pointers,
5908c2ecf20Sopenharmony_ci * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
5918c2ecf20Sopenharmony_ci * success.
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_ciint clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
5968c2ecf20Sopenharmony_ci	int ret;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
5998c2ecf20Sopenharmony_ci		return -EINVAL;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
6028c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
6038c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
6068c2ecf20Sopenharmony_ci	ret = _clkdm_add_wkdep(clkdm1, clkdm2);
6078c2ecf20Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return ret;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/**
6138c2ecf20Sopenharmony_ci * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
6148c2ecf20Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
6158c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
6168c2ecf20Sopenharmony_ci *
6178c2ecf20Sopenharmony_ci * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
6188c2ecf20Sopenharmony_ci * wakes up.  Returns -EINVAL if presented with invalid clockdomain
6198c2ecf20Sopenharmony_ci * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
6208c2ecf20Sopenharmony_ci * 0 upon success.
6218c2ecf20Sopenharmony_ci */
6228c2ecf20Sopenharmony_ciint clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
6258c2ecf20Sopenharmony_ci	int ret;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
6288c2ecf20Sopenharmony_ci		return -EINVAL;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
6318c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
6328c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
6358c2ecf20Sopenharmony_ci	ret = _clkdm_del_wkdep(clkdm1, clkdm2);
6368c2ecf20Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return ret;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci/**
6428c2ecf20Sopenharmony_ci * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
6438c2ecf20Sopenharmony_ci * @clkdm1: wake this struct clockdomain * up (dependent)
6448c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * wakes up (source)
6458c2ecf20Sopenharmony_ci *
6468c2ecf20Sopenharmony_ci * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
6478c2ecf20Sopenharmony_ci * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
6488c2ecf20Sopenharmony_ci * if either clockdomain pointer is invalid; or -ENOENT if the hardware
6498c2ecf20Sopenharmony_ci * is incapable.
6508c2ecf20Sopenharmony_ci *
6518c2ecf20Sopenharmony_ci * REVISIT: Currently this function only represents software-controllable
6528c2ecf20Sopenharmony_ci * wakeup dependencies.  Wakeup dependencies fixed in hardware are not
6538c2ecf20Sopenharmony_ci * yet handled here.
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_ciint clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
6588c2ecf20Sopenharmony_ci	int ret = 0;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
6618c2ecf20Sopenharmony_ci		return -EINVAL;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
6648c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
6658c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep)
6688c2ecf20Sopenharmony_ci		ret = -EINVAL;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (ret) {
6718c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
6728c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
6738c2ecf20Sopenharmony_ci		return ret;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* XXX It's faster to return the wkdep_usecount */
6778c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci/**
6818c2ecf20Sopenharmony_ci * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm
6828c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain * to remove all wakeup dependencies from
6838c2ecf20Sopenharmony_ci *
6848c2ecf20Sopenharmony_ci * Remove all inter-clockdomain wakeup dependencies that could cause
6858c2ecf20Sopenharmony_ci * @clkdm to wake.  Intended to be used during boot to initialize the
6868c2ecf20Sopenharmony_ci * PRCM to a known state, after all clockdomains are put into swsup idle
6878c2ecf20Sopenharmony_ci * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
6888c2ecf20Sopenharmony_ci * 0 upon success.
6898c2ecf20Sopenharmony_ci */
6908c2ecf20Sopenharmony_ciint clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	if (!clkdm)
6938c2ecf20Sopenharmony_ci		return -EINVAL;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps)
6968c2ecf20Sopenharmony_ci		return -EINVAL;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_clear_all_wkdeps(clkdm);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/**
7028c2ecf20Sopenharmony_ci * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
7038c2ecf20Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
7048c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
7058c2ecf20Sopenharmony_ci *
7068c2ecf20Sopenharmony_ci * Prevent @clkdm1 from automatically going inactive (and then to
7078c2ecf20Sopenharmony_ci * retention or off) if @clkdm2 is active.  Returns -EINVAL if
7088c2ecf20Sopenharmony_ci * presented with invalid clockdomain pointers or called on a machine
7098c2ecf20Sopenharmony_ci * that does not support software-configurable hardware sleep
7108c2ecf20Sopenharmony_ci * dependencies, -ENOENT if the specified dependency cannot be set in
7118c2ecf20Sopenharmony_ci * hardware, or 0 upon success.
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_ciint clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
7168c2ecf20Sopenharmony_ci	int ret;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
7198c2ecf20Sopenharmony_ci		return -EINVAL;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
7228c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
7238c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
7268c2ecf20Sopenharmony_ci	ret = _clkdm_add_sleepdep(clkdm1, clkdm2);
7278c2ecf20Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	return ret;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci/**
7338c2ecf20Sopenharmony_ci * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1
7348c2ecf20Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
7358c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
7368c2ecf20Sopenharmony_ci *
7378c2ecf20Sopenharmony_ci * Allow @clkdm1 to automatically go inactive (and then to retention or
7388c2ecf20Sopenharmony_ci * off), independent of the activity state of @clkdm2.  Returns -EINVAL
7398c2ecf20Sopenharmony_ci * if presented with invalid clockdomain pointers or called on a machine
7408c2ecf20Sopenharmony_ci * that does not support software-configurable hardware sleep dependencies,
7418c2ecf20Sopenharmony_ci * -ENOENT if the specified dependency cannot be cleared in hardware, or
7428c2ecf20Sopenharmony_ci * 0 upon success.
7438c2ecf20Sopenharmony_ci */
7448c2ecf20Sopenharmony_ciint clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
7478c2ecf20Sopenharmony_ci	int ret;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
7508c2ecf20Sopenharmony_ci		return -EINVAL;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
7538c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
7548c2ecf20Sopenharmony_ci		return PTR_ERR(cd);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	pwrdm_lock(cd->clkdm->pwrdm.ptr);
7578c2ecf20Sopenharmony_ci	ret = _clkdm_del_sleepdep(clkdm1, clkdm2);
7588c2ecf20Sopenharmony_ci	pwrdm_unlock(cd->clkdm->pwrdm.ptr);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return ret;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/**
7648c2ecf20Sopenharmony_ci * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1
7658c2ecf20Sopenharmony_ci * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
7668c2ecf20Sopenharmony_ci * @clkdm2: when this struct clockdomain * is active (source)
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will
7698c2ecf20Sopenharmony_ci * not be allowed to automatically go inactive if @clkdm2 is active;
7708c2ecf20Sopenharmony_ci * 0 if @clkdm1's automatic power state inactivity transition is independent
7718c2ecf20Sopenharmony_ci * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called
7728c2ecf20Sopenharmony_ci * on a machine that does not support software-configurable hardware sleep
7738c2ecf20Sopenharmony_ci * dependencies; or -ENOENT if the hardware is incapable.
7748c2ecf20Sopenharmony_ci *
7758c2ecf20Sopenharmony_ci * REVISIT: Currently this function only represents software-controllable
7768c2ecf20Sopenharmony_ci * sleep dependencies.	Sleep dependencies fixed in hardware are not
7778c2ecf20Sopenharmony_ci * yet handled here.
7788c2ecf20Sopenharmony_ci */
7798c2ecf20Sopenharmony_ciint clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct clkdm_dep *cd;
7828c2ecf20Sopenharmony_ci	int ret = 0;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (!clkdm1 || !clkdm2)
7858c2ecf20Sopenharmony_ci		return -EINVAL;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
7888c2ecf20Sopenharmony_ci	if (IS_ERR(cd))
7898c2ecf20Sopenharmony_ci		ret = PTR_ERR(cd);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_read_sleepdep)
7928c2ecf20Sopenharmony_ci		ret = -EINVAL;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (ret) {
7958c2ecf20Sopenharmony_ci		pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
7968c2ecf20Sopenharmony_ci			 clkdm1->name, clkdm2->name);
7978c2ecf20Sopenharmony_ci		return ret;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/* XXX It's faster to return the sleepdep_usecount */
8018c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/**
8058c2ecf20Sopenharmony_ci * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm
8068c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain * to remove all sleep dependencies from
8078c2ecf20Sopenharmony_ci *
8088c2ecf20Sopenharmony_ci * Remove all inter-clockdomain sleep dependencies that could prevent
8098c2ecf20Sopenharmony_ci * @clkdm from idling.  Intended to be used during boot to initialize the
8108c2ecf20Sopenharmony_ci * PRCM to a known state, after all clockdomains are put into swsup idle
8118c2ecf20Sopenharmony_ci * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or
8128c2ecf20Sopenharmony_ci * 0 upon success.
8138c2ecf20Sopenharmony_ci */
8148c2ecf20Sopenharmony_ciint clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	if (!clkdm)
8178c2ecf20Sopenharmony_ci		return -EINVAL;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_sleepdeps)
8208c2ecf20Sopenharmony_ci		return -EINVAL;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_clear_all_sleepdeps(clkdm);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/**
8268c2ecf20Sopenharmony_ci * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)
8278c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
8288c2ecf20Sopenharmony_ci *
8298c2ecf20Sopenharmony_ci * Instruct the CM to force a sleep transition on the specified
8308c2ecf20Sopenharmony_ci * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
8318c2ecf20Sopenharmony_ci * -EINVAL if @clkdm is NULL or if clockdomain does not support
8328c2ecf20Sopenharmony_ci * software-initiated sleep; 0 upon success.
8338c2ecf20Sopenharmony_ci */
8348c2ecf20Sopenharmony_ciint clkdm_sleep_nolock(struct clockdomain *clkdm)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	int ret;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (!clkdm)
8398c2ecf20Sopenharmony_ci		return -EINVAL;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
8428c2ecf20Sopenharmony_ci		pr_debug("clockdomain: %s does not support forcing sleep via software\n",
8438c2ecf20Sopenharmony_ci			 clkdm->name);
8448c2ecf20Sopenharmony_ci		return -EINVAL;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_sleep)
8488c2ecf20Sopenharmony_ci		return -EINVAL;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
8538c2ecf20Sopenharmony_ci	ret = arch_clkdm->clkdm_sleep(clkdm);
8548c2ecf20Sopenharmony_ci	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return ret;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci/**
8608c2ecf20Sopenharmony_ci * clkdm_sleep - force clockdomain sleep transition
8618c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
8628c2ecf20Sopenharmony_ci *
8638c2ecf20Sopenharmony_ci * Instruct the CM to force a sleep transition on the specified
8648c2ecf20Sopenharmony_ci * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if
8658c2ecf20Sopenharmony_ci * clockdomain does not support software-initiated sleep; 0 upon
8668c2ecf20Sopenharmony_ci * success.
8678c2ecf20Sopenharmony_ci */
8688c2ecf20Sopenharmony_ciint clkdm_sleep(struct clockdomain *clkdm)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	int ret;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
8738c2ecf20Sopenharmony_ci	ret = clkdm_sleep_nolock(clkdm);
8748c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return ret;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci/**
8808c2ecf20Sopenharmony_ci * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)
8818c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
8828c2ecf20Sopenharmony_ci *
8838c2ecf20Sopenharmony_ci * Instruct the CM to force a wakeup transition on the specified
8848c2ecf20Sopenharmony_ci * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
8858c2ecf20Sopenharmony_ci * -EINVAL if @clkdm is NULL or if the clockdomain does not support
8868c2ecf20Sopenharmony_ci * software-controlled wakeup; 0 upon success.
8878c2ecf20Sopenharmony_ci */
8888c2ecf20Sopenharmony_ciint clkdm_wakeup_nolock(struct clockdomain *clkdm)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	int ret;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (!clkdm)
8938c2ecf20Sopenharmony_ci		return -EINVAL;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
8968c2ecf20Sopenharmony_ci		pr_debug("clockdomain: %s does not support forcing wakeup via software\n",
8978c2ecf20Sopenharmony_ci			 clkdm->name);
8988c2ecf20Sopenharmony_ci		return -EINVAL;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_wakeup)
9028c2ecf20Sopenharmony_ci		return -EINVAL;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
9078c2ecf20Sopenharmony_ci	ret = arch_clkdm->clkdm_wakeup(clkdm);
9088c2ecf20Sopenharmony_ci	ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	return ret;
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci/**
9148c2ecf20Sopenharmony_ci * clkdm_wakeup - force clockdomain wakeup transition
9158c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
9168c2ecf20Sopenharmony_ci *
9178c2ecf20Sopenharmony_ci * Instruct the CM to force a wakeup transition on the specified
9188c2ecf20Sopenharmony_ci * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the
9198c2ecf20Sopenharmony_ci * clockdomain does not support software-controlled wakeup; 0 upon
9208c2ecf20Sopenharmony_ci * success.
9218c2ecf20Sopenharmony_ci */
9228c2ecf20Sopenharmony_ciint clkdm_wakeup(struct clockdomain *clkdm)
9238c2ecf20Sopenharmony_ci{
9248c2ecf20Sopenharmony_ci	int ret;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
9278c2ecf20Sopenharmony_ci	ret = clkdm_wakeup_nolock(clkdm);
9288c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	return ret;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/**
9348c2ecf20Sopenharmony_ci * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm
9358c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
9368c2ecf20Sopenharmony_ci *
9378c2ecf20Sopenharmony_ci * Allow the hardware to automatically switch the clockdomain @clkdm
9388c2ecf20Sopenharmony_ci * into active or idle states, as needed by downstream clocks.  If the
9398c2ecf20Sopenharmony_ci * clockdomain has any downstream clocks enabled in the clock
9408c2ecf20Sopenharmony_ci * framework, wkdep/sleepdep autodependencies are added; this is so
9418c2ecf20Sopenharmony_ci * device drivers can read and write to the device.  Only for use by
9428c2ecf20Sopenharmony_ci * the powerdomain code.  No return value.
9438c2ecf20Sopenharmony_ci */
9448c2ecf20Sopenharmony_civoid clkdm_allow_idle_nolock(struct clockdomain *clkdm)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	if (!clkdm)
9478c2ecf20Sopenharmony_ci		return;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (!WARN_ON(!clkdm->forcewake_count))
9508c2ecf20Sopenharmony_ci		clkdm->forcewake_count--;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	if (clkdm->forcewake_count)
9538c2ecf20Sopenharmony_ci		return;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
9568c2ecf20Sopenharmony_ci		clkdm_sleep_nolock(clkdm);
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
9598c2ecf20Sopenharmony_ci		return;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
9628c2ecf20Sopenharmony_ci		return;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
9658c2ecf20Sopenharmony_ci		return;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
9688c2ecf20Sopenharmony_ci		 clkdm->name);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
9718c2ecf20Sopenharmony_ci	arch_clkdm->clkdm_allow_idle(clkdm);
9728c2ecf20Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci/**
9768c2ecf20Sopenharmony_ci * clkdm_allow_idle - enable hwsup idle transitions for clkdm
9778c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
9788c2ecf20Sopenharmony_ci *
9798c2ecf20Sopenharmony_ci * Allow the hardware to automatically switch the clockdomain @clkdm into
9808c2ecf20Sopenharmony_ci * active or idle states, as needed by downstream clocks.  If the
9818c2ecf20Sopenharmony_ci * clockdomain has any downstream clocks enabled in the clock
9828c2ecf20Sopenharmony_ci * framework, wkdep/sleepdep autodependencies are added; this is so
9838c2ecf20Sopenharmony_ci * device drivers can read and write to the device.  No return value.
9848c2ecf20Sopenharmony_ci */
9858c2ecf20Sopenharmony_civoid clkdm_allow_idle(struct clockdomain *clkdm)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
9888c2ecf20Sopenharmony_ci	clkdm_allow_idle_nolock(clkdm);
9898c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci/**
9938c2ecf20Sopenharmony_ci * clkdm_deny_idle - disable hwsup idle transitions for clkdm
9948c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
9958c2ecf20Sopenharmony_ci *
9968c2ecf20Sopenharmony_ci * Prevent the hardware from automatically switching the clockdomain
9978c2ecf20Sopenharmony_ci * @clkdm into inactive or idle states.  If the clockdomain has
9988c2ecf20Sopenharmony_ci * downstream clocks enabled in the clock framework, wkdep/sleepdep
9998c2ecf20Sopenharmony_ci * autodependencies are removed.  Only for use by the powerdomain
10008c2ecf20Sopenharmony_ci * code.  No return value.
10018c2ecf20Sopenharmony_ci */
10028c2ecf20Sopenharmony_civoid clkdm_deny_idle_nolock(struct clockdomain *clkdm)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	if (!clkdm)
10058c2ecf20Sopenharmony_ci		return;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (clkdm->forcewake_count++)
10088c2ecf20Sopenharmony_ci		return;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
10118c2ecf20Sopenharmony_ci		clkdm_wakeup_nolock(clkdm);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
10148c2ecf20Sopenharmony_ci		return;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
10178c2ecf20Sopenharmony_ci		return;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
10208c2ecf20Sopenharmony_ci		return;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
10238c2ecf20Sopenharmony_ci		 clkdm->name);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
10268c2ecf20Sopenharmony_ci	arch_clkdm->clkdm_deny_idle(clkdm);
10278c2ecf20Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/**
10318c2ecf20Sopenharmony_ci * clkdm_deny_idle - disable hwsup idle transitions for clkdm
10328c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
10338c2ecf20Sopenharmony_ci *
10348c2ecf20Sopenharmony_ci * Prevent the hardware from automatically switching the clockdomain
10358c2ecf20Sopenharmony_ci * @clkdm into inactive or idle states.  If the clockdomain has
10368c2ecf20Sopenharmony_ci * downstream clocks enabled in the clock framework, wkdep/sleepdep
10378c2ecf20Sopenharmony_ci * autodependencies are removed.  No return value.
10388c2ecf20Sopenharmony_ci */
10398c2ecf20Sopenharmony_civoid clkdm_deny_idle(struct clockdomain *clkdm)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
10428c2ecf20Sopenharmony_ci	clkdm_deny_idle_nolock(clkdm);
10438c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci/**
10478c2ecf20Sopenharmony_ci * clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled?
10488c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
10498c2ecf20Sopenharmony_ci *
10508c2ecf20Sopenharmony_ci * Returns true if clockdomain @clkdm currently has
10518c2ecf20Sopenharmony_ci * hardware-supervised idle enabled, or false if it does not or if
10528c2ecf20Sopenharmony_ci * @clkdm is NULL.  It is only valid to call this function after
10538c2ecf20Sopenharmony_ci * clkdm_init() has been called.  This function does not actually read
10548c2ecf20Sopenharmony_ci * bits from the hardware; it instead tests an in-memory flag that is
10558c2ecf20Sopenharmony_ci * changed whenever the clockdomain code changes the auto-idle mode.
10568c2ecf20Sopenharmony_ci */
10578c2ecf20Sopenharmony_cibool clkdm_in_hwsup(struct clockdomain *clkdm)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	bool ret;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	if (!clkdm)
10628c2ecf20Sopenharmony_ci		return false;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	return ret;
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci/**
10708c2ecf20Sopenharmony_ci * clkdm_missing_idle_reporting - can @clkdm enter autoidle even if in use?
10718c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
10728c2ecf20Sopenharmony_ci *
10738c2ecf20Sopenharmony_ci * Returns true if clockdomain @clkdm has the
10748c2ecf20Sopenharmony_ci * CLKDM_MISSING_IDLE_REPORTING flag set, or false if not or @clkdm is
10758c2ecf20Sopenharmony_ci * null.  More information is available in the documentation for the
10768c2ecf20Sopenharmony_ci * CLKDM_MISSING_IDLE_REPORTING macro.
10778c2ecf20Sopenharmony_ci */
10788c2ecf20Sopenharmony_cibool clkdm_missing_idle_reporting(struct clockdomain *clkdm)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	if (!clkdm)
10818c2ecf20Sopenharmony_ci		return false;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci/* Public autodep handling functions (deprecated) */
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci/**
10898c2ecf20Sopenharmony_ci * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
10908c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
10918c2ecf20Sopenharmony_ci *
10928c2ecf20Sopenharmony_ci * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
10938c2ecf20Sopenharmony_ci * in hardware-supervised mode.  Meant to be called from clock framework
10948c2ecf20Sopenharmony_ci * when a clock inside clockdomain 'clkdm' is enabled.	No return value.
10958c2ecf20Sopenharmony_ci *
10968c2ecf20Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
10978c2ecf20Sopenharmony_ci * opportunity
10988c2ecf20Sopenharmony_ci */
10998c2ecf20Sopenharmony_civoid clkdm_add_autodeps(struct clockdomain *clkdm)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	struct clkdm_autodep *autodep;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
11048c2ecf20Sopenharmony_ci		return;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
11078c2ecf20Sopenharmony_ci		if (IS_ERR(autodep->clkdm.ptr))
11088c2ecf20Sopenharmony_ci			continue;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
11118c2ecf20Sopenharmony_ci			 clkdm->name, autodep->clkdm.ptr->name);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci		_clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
11148c2ecf20Sopenharmony_ci		_clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci/**
11198c2ecf20Sopenharmony_ci * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm
11208c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
11218c2ecf20Sopenharmony_ci *
11228c2ecf20Sopenharmony_ci * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
11238c2ecf20Sopenharmony_ci * in hardware-supervised mode.  Meant to be called from clock framework
11248c2ecf20Sopenharmony_ci * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
11258c2ecf20Sopenharmony_ci *
11268c2ecf20Sopenharmony_ci * XXX autodeps are deprecated and should be removed at the earliest
11278c2ecf20Sopenharmony_ci * opportunity
11288c2ecf20Sopenharmony_ci */
11298c2ecf20Sopenharmony_civoid clkdm_del_autodeps(struct clockdomain *clkdm)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	struct clkdm_autodep *autodep;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
11348c2ecf20Sopenharmony_ci		return;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
11378c2ecf20Sopenharmony_ci		if (IS_ERR(autodep->clkdm.ptr))
11388c2ecf20Sopenharmony_ci			continue;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci		pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
11418c2ecf20Sopenharmony_ci			 clkdm->name, autodep->clkdm.ptr->name);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		_clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
11448c2ecf20Sopenharmony_ci		_clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
11458c2ecf20Sopenharmony_ci	}
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci/* Clockdomain-to-clock/hwmod framework interface code */
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci/**
11518c2ecf20Sopenharmony_ci * clkdm_clk_enable - add an enabled downstream clock to this clkdm
11528c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
11538c2ecf20Sopenharmony_ci * @clk: struct clk * of the enabled downstream clock
11548c2ecf20Sopenharmony_ci *
11558c2ecf20Sopenharmony_ci * Increment the usecount of the clockdomain @clkdm and ensure that it
11568c2ecf20Sopenharmony_ci * is awake before @clk is enabled.  Intended to be called by
11578c2ecf20Sopenharmony_ci * clk_enable() code.  If the clockdomain is in software-supervised
11588c2ecf20Sopenharmony_ci * idle mode, force the clockdomain to wake.  If the clockdomain is in
11598c2ecf20Sopenharmony_ci * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to
11608c2ecf20Sopenharmony_ci * ensure that devices in the clockdomain can be read from/written to
11618c2ecf20Sopenharmony_ci * by on-chip processors.  Returns -EINVAL if passed null pointers;
11628c2ecf20Sopenharmony_ci * returns 0 upon success or if the clockdomain is in hwsup idle mode.
11638c2ecf20Sopenharmony_ci */
11648c2ecf20Sopenharmony_ciint clkdm_clk_enable(struct clockdomain *clkdm, struct clk *unused)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
11678c2ecf20Sopenharmony_ci		return -EINVAL;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	/*
11728c2ecf20Sopenharmony_ci	 * For arch's with no autodeps, clkcm_clk_enable
11738c2ecf20Sopenharmony_ci	 * should be called for every clock instance or hwmod that is
11748c2ecf20Sopenharmony_ci	 * enabled, so the clkdm can be force woken up.
11758c2ecf20Sopenharmony_ci	 */
11768c2ecf20Sopenharmony_ci	clkdm->usecount++;
11778c2ecf20Sopenharmony_ci	if (clkdm->usecount > 1 && autodeps) {
11788c2ecf20Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
11798c2ecf20Sopenharmony_ci		return 0;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	arch_clkdm->clkdm_clk_enable(clkdm);
11838c2ecf20Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
11848c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	pr_debug("clockdomain: %s: enabled\n", clkdm->name);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	return 0;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci/**
11928c2ecf20Sopenharmony_ci * clkdm_clk_disable - remove an enabled downstream clock from this clkdm
11938c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
11948c2ecf20Sopenharmony_ci * @clk: struct clk * of the disabled downstream clock
11958c2ecf20Sopenharmony_ci *
11968c2ecf20Sopenharmony_ci * Decrement the usecount of this clockdomain @clkdm when @clk is
11978c2ecf20Sopenharmony_ci * disabled.  Intended to be called by clk_disable() code.  If the
11988c2ecf20Sopenharmony_ci * clockdomain usecount goes to 0, put the clockdomain to sleep
11998c2ecf20Sopenharmony_ci * (software-supervised mode) or remove the clkdm autodependencies
12008c2ecf20Sopenharmony_ci * (hardware-supervised mode).  Returns -EINVAL if passed null
12018c2ecf20Sopenharmony_ci * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
12028c2ecf20Sopenharmony_ci * upon success or if the clockdomain is in hwsup idle mode.
12038c2ecf20Sopenharmony_ci */
12048c2ecf20Sopenharmony_ciint clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
12078c2ecf20Sopenharmony_ci		return -EINVAL;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	pwrdm_lock(clkdm->pwrdm.ptr);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* corner case: disabling unused clocks */
12128c2ecf20Sopenharmony_ci	if (clk && (__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0)
12138c2ecf20Sopenharmony_ci		goto ccd_exit;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	if (clkdm->usecount == 0) {
12168c2ecf20Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
12178c2ecf20Sopenharmony_ci		WARN_ON(1); /* underflow */
12188c2ecf20Sopenharmony_ci		return -ERANGE;
12198c2ecf20Sopenharmony_ci	}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	clkdm->usecount--;
12228c2ecf20Sopenharmony_ci	if (clkdm->usecount > 0) {
12238c2ecf20Sopenharmony_ci		pwrdm_unlock(clkdm->pwrdm.ptr);
12248c2ecf20Sopenharmony_ci		return 0;
12258c2ecf20Sopenharmony_ci	}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	arch_clkdm->clkdm_clk_disable(clkdm);
12288c2ecf20Sopenharmony_ci	pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	pr_debug("clockdomain: %s: disabled\n", clkdm->name);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ciccd_exit:
12338c2ecf20Sopenharmony_ci	pwrdm_unlock(clkdm->pwrdm.ptr);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	return 0;
12368c2ecf20Sopenharmony_ci}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci/**
12398c2ecf20Sopenharmony_ci * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
12408c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
12418c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * of the enabled downstream hwmod
12428c2ecf20Sopenharmony_ci *
12438c2ecf20Sopenharmony_ci * Increment the usecount of the clockdomain @clkdm and ensure that it
12448c2ecf20Sopenharmony_ci * is awake before @oh is enabled. Intended to be called by
12458c2ecf20Sopenharmony_ci * module_enable() code.
12468c2ecf20Sopenharmony_ci * If the clockdomain is in software-supervised idle mode, force the
12478c2ecf20Sopenharmony_ci * clockdomain to wake.  If the clockdomain is in hardware-supervised idle
12488c2ecf20Sopenharmony_ci * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
12498c2ecf20Sopenharmony_ci * clockdomain can be read from/written to by on-chip processors.
12508c2ecf20Sopenharmony_ci * Returns -EINVAL if passed null pointers;
12518c2ecf20Sopenharmony_ci * returns 0 upon success or if the clockdomain is in hwsup idle mode.
12528c2ecf20Sopenharmony_ci */
12538c2ecf20Sopenharmony_ciint clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
12548c2ecf20Sopenharmony_ci{
12558c2ecf20Sopenharmony_ci	/* The clkdm attribute does not exist yet prior OMAP4 */
12568c2ecf20Sopenharmony_ci	if (cpu_is_omap24xx() || cpu_is_omap34xx())
12578c2ecf20Sopenharmony_ci		return 0;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/*
12608c2ecf20Sopenharmony_ci	 * XXX Rewrite this code to maintain a list of enabled
12618c2ecf20Sopenharmony_ci	 * downstream hwmods for debugging purposes?
12628c2ecf20Sopenharmony_ci	 */
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (!oh)
12658c2ecf20Sopenharmony_ci		return -EINVAL;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	return clkdm_clk_enable(clkdm, NULL);
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci/**
12718c2ecf20Sopenharmony_ci * clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
12728c2ecf20Sopenharmony_ci * @clkdm: struct clockdomain *
12738c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * of the disabled downstream hwmod
12748c2ecf20Sopenharmony_ci *
12758c2ecf20Sopenharmony_ci * Decrement the usecount of this clockdomain @clkdm when @oh is
12768c2ecf20Sopenharmony_ci * disabled. Intended to be called by module_disable() code.
12778c2ecf20Sopenharmony_ci * If the clockdomain usecount goes to 0, put the clockdomain to sleep
12788c2ecf20Sopenharmony_ci * (software-supervised mode) or remove the clkdm autodependencies
12798c2ecf20Sopenharmony_ci * (hardware-supervised mode).
12808c2ecf20Sopenharmony_ci * Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
12818c2ecf20Sopenharmony_ci * underflows; or returns 0 upon success or if the clockdomain is in hwsup
12828c2ecf20Sopenharmony_ci * idle mode.
12838c2ecf20Sopenharmony_ci */
12848c2ecf20Sopenharmony_ciint clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	/* The clkdm attribute does not exist yet prior OMAP4 */
12878c2ecf20Sopenharmony_ci	if (cpu_is_omap24xx() || cpu_is_omap34xx())
12888c2ecf20Sopenharmony_ci		return 0;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (!oh)
12918c2ecf20Sopenharmony_ci		return -EINVAL;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	return clkdm_clk_disable(clkdm, NULL);
12948c2ecf20Sopenharmony_ci}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci/**
12978c2ecf20Sopenharmony_ci * _clkdm_save_context - save the context for the control of this clkdm
12988c2ecf20Sopenharmony_ci *
12998c2ecf20Sopenharmony_ci * Due to a suspend or hibernation operation, the state of the registers
13008c2ecf20Sopenharmony_ci * controlling this clkdm will be lost, save their context.
13018c2ecf20Sopenharmony_ci */
13028c2ecf20Sopenharmony_cistatic int _clkdm_save_context(struct clockdomain *clkdm, void *ununsed)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_save_context)
13058c2ecf20Sopenharmony_ci		return -EINVAL;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_save_context(clkdm);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci/**
13118c2ecf20Sopenharmony_ci * _clkdm_restore_context - restore context for control of this clkdm
13128c2ecf20Sopenharmony_ci *
13138c2ecf20Sopenharmony_ci * Restore the register values for this clockdomain.
13148c2ecf20Sopenharmony_ci */
13158c2ecf20Sopenharmony_cistatic int _clkdm_restore_context(struct clockdomain *clkdm, void *ununsed)
13168c2ecf20Sopenharmony_ci{
13178c2ecf20Sopenharmony_ci	if (!arch_clkdm || !arch_clkdm->clkdm_restore_context)
13188c2ecf20Sopenharmony_ci		return -EINVAL;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	return arch_clkdm->clkdm_restore_context(clkdm);
13218c2ecf20Sopenharmony_ci}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci/**
13248c2ecf20Sopenharmony_ci * clkdm_save_context - Saves the context for each registered clkdm
13258c2ecf20Sopenharmony_ci *
13268c2ecf20Sopenharmony_ci * Save the context for each registered clockdomain.
13278c2ecf20Sopenharmony_ci */
13288c2ecf20Sopenharmony_civoid clkdm_save_context(void)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	clkdm_for_each(_clkdm_save_context, NULL);
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci/**
13348c2ecf20Sopenharmony_ci * clkdm_restore_context - Restores the context for each registered clkdm
13358c2ecf20Sopenharmony_ci *
13368c2ecf20Sopenharmony_ci * Restore the context for each registered clockdomain.
13378c2ecf20Sopenharmony_ci */
13388c2ecf20Sopenharmony_civoid clkdm_restore_context(void)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	clkdm_for_each(_clkdm_restore_context, NULL);
13418c2ecf20Sopenharmony_ci}
1342