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