| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1 | /* | 
| Abhijit Pagare | 8a3ddc7 | 2010-01-26 20:12:54 -0700 | [diff] [blame] | 2 | * OMAP2/3/4 clockdomain framework functions | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 3 | * | 
| Abhijit Pagare | 91808a8 | 2010-02-22 22:09:07 -0700 | [diff] [blame] | 4 | * Copyright (C) 2008-2010 Texas Instruments, Inc. | 
|  | 5 | * Copyright (C) 2008-2010 Nokia Corporation | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 6 | * | 
|  | 7 | * Written by Paul Walmsley and Jouni Högander | 
| Abhijit Pagare | 8a3ddc7 | 2010-01-26 20:12:54 -0700 | [diff] [blame] | 8 | * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 9 | * | 
|  | 10 | * This program is free software; you can redistribute it and/or modify | 
|  | 11 | * it under the terms of the GNU General Public License version 2 as | 
|  | 12 | * published by the Free Software Foundation. | 
|  | 13 | */ | 
| Paul Walmsley | 33903eb | 2009-12-08 16:33:10 -0700 | [diff] [blame] | 14 | #undef DEBUG | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 15 |  | 
|  | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/kernel.h> | 
|  | 18 | #include <linux/device.h> | 
|  | 19 | #include <linux/list.h> | 
|  | 20 | #include <linux/errno.h> | 
|  | 21 | #include <linux/delay.h> | 
|  | 22 | #include <linux/clk.h> | 
|  | 23 | #include <linux/limits.h> | 
| Paul Walmsley | 5b74c67 | 2009-02-03 02:10:03 -0700 | [diff] [blame] | 24 | #include <linux/err.h> | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 25 |  | 
|  | 26 | #include <linux/io.h> | 
|  | 27 |  | 
|  | 28 | #include <linux/bitops.h> | 
|  | 29 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 30 | #include "prm.h" | 
|  | 31 | #include "prm-regbits-24xx.h" | 
|  | 32 | #include "cm.h" | 
|  | 33 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 34 | #include <plat/clock.h> | 
| Tony Lindgren | ce491cf | 2009-10-20 09:40:47 -0700 | [diff] [blame] | 35 | #include <plat/powerdomain.h> | 
|  | 36 | #include <plat/clockdomain.h> | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 37 | #include <plat/prcm.h> | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 38 |  | 
|  | 39 | /* clkdm_list contains all registered struct clockdomains */ | 
|  | 40 | static LIST_HEAD(clkdm_list); | 
|  | 41 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 42 | /* array of clockdomain deps to be added/removed when clkdm in hwsup mode */ | 
|  | 43 | static struct clkdm_autodep *autodeps; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 44 |  | 
|  | 45 |  | 
|  | 46 | /* Private functions */ | 
|  | 47 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 48 | static struct clockdomain *_clkdm_lookup(const char *name) | 
|  | 49 | { | 
|  | 50 | struct clockdomain *clkdm, *temp_clkdm; | 
|  | 51 |  | 
|  | 52 | if (!name) | 
|  | 53 | return NULL; | 
|  | 54 |  | 
|  | 55 | clkdm = NULL; | 
|  | 56 |  | 
|  | 57 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | 
|  | 58 | if (!strcmp(name, temp_clkdm->name)) { | 
|  | 59 | clkdm = temp_clkdm; | 
|  | 60 | break; | 
|  | 61 | } | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | return clkdm; | 
|  | 65 | } | 
|  | 66 |  | 
| Paul Walmsley | e909d62 | 2010-01-26 20:13:00 -0700 | [diff] [blame] | 67 | /** | 
|  | 68 | * _clkdm_register - register a clockdomain | 
|  | 69 | * @clkdm: struct clockdomain * to register | 
|  | 70 | * | 
|  | 71 | * Adds a clockdomain to the internal clockdomain list. | 
|  | 72 | * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is | 
|  | 73 | * already registered by the provided name, or 0 upon success. | 
|  | 74 | */ | 
|  | 75 | static int _clkdm_register(struct clockdomain *clkdm) | 
|  | 76 | { | 
|  | 77 | struct powerdomain *pwrdm; | 
|  | 78 |  | 
|  | 79 | if (!clkdm || !clkdm->name) | 
|  | 80 | return -EINVAL; | 
|  | 81 |  | 
|  | 82 | if (!omap_chip_is(clkdm->omap_chip)) | 
|  | 83 | return -EINVAL; | 
|  | 84 |  | 
|  | 85 | pwrdm = pwrdm_lookup(clkdm->pwrdm.name); | 
|  | 86 | if (!pwrdm) { | 
|  | 87 | pr_err("clockdomain: %s: powerdomain %s does not exist\n", | 
|  | 88 | clkdm->name, clkdm->pwrdm.name); | 
|  | 89 | return -EINVAL; | 
|  | 90 | } | 
|  | 91 | clkdm->pwrdm.ptr = pwrdm; | 
|  | 92 |  | 
|  | 93 | /* Verify that the clockdomain is not already registered */ | 
|  | 94 | if (_clkdm_lookup(clkdm->name)) | 
|  | 95 | return -EEXIST; | 
|  | 96 |  | 
|  | 97 | list_add(&clkdm->node, &clkdm_list); | 
|  | 98 |  | 
|  | 99 | pwrdm_add_clkdm(pwrdm, clkdm); | 
|  | 100 |  | 
|  | 101 | pr_debug("clockdomain: registered %s\n", clkdm->name); | 
|  | 102 |  | 
|  | 103 | return 0; | 
|  | 104 | } | 
|  | 105 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 106 | /* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */ | 
|  | 107 | static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, | 
|  | 108 | struct clkdm_dep *deps) | 
|  | 109 | { | 
|  | 110 | struct clkdm_dep *cd; | 
|  | 111 |  | 
|  | 112 | if (!clkdm || !deps || !omap_chip_is(clkdm->omap_chip)) | 
|  | 113 | return ERR_PTR(-EINVAL); | 
|  | 114 |  | 
|  | 115 | for (cd = deps; cd->clkdm_name; cd++) { | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 116 | if (!omap_chip_is(cd->omap_chip)) | 
|  | 117 | continue; | 
|  | 118 |  | 
|  | 119 | if (!cd->clkdm && cd->clkdm_name) | 
|  | 120 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | 
|  | 121 |  | 
|  | 122 | if (cd->clkdm == clkdm) | 
|  | 123 | break; | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 124 | } | 
|  | 125 |  | 
|  | 126 | if (!cd->clkdm_name) | 
|  | 127 | return ERR_PTR(-ENOENT); | 
|  | 128 |  | 
|  | 129 | return cd; | 
|  | 130 | } | 
|  | 131 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 132 | /* | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 133 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store | 
|  | 134 | * @autodep: struct clkdm_autodep * to resolve | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 135 | * | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 136 | * Resolve autodep clockdomain names to clockdomain pointers via | 
|  | 137 | * clkdm_lookup() and store the pointers in the autodep structure.  An | 
|  | 138 | * "autodep" is a clockdomain sleep/wakeup dependency that is | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 139 | * automatically added and removed whenever clocks in the associated | 
|  | 140 | * clockdomain are enabled or disabled (respectively) when the | 
|  | 141 | * clockdomain is in hardware-supervised mode.	Meant to be called | 
|  | 142 | * once at clockdomain layer initialization, since these should remain | 
|  | 143 | * fixed for a particular architecture.  No return value. | 
|  | 144 | */ | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 145 | static void _autodep_lookup(struct clkdm_autodep *autodep) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 146 | { | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 147 | struct clockdomain *clkdm; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 148 |  | 
|  | 149 | if (!autodep) | 
|  | 150 | return; | 
|  | 151 |  | 
|  | 152 | if (!omap_chip_is(autodep->omap_chip)) | 
|  | 153 | return; | 
|  | 154 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 155 | clkdm = clkdm_lookup(autodep->clkdm.name); | 
|  | 156 | if (!clkdm) { | 
|  | 157 | pr_err("clockdomain: autodeps: clockdomain %s does not exist\n", | 
|  | 158 | autodep->clkdm.name); | 
|  | 159 | clkdm = ERR_PTR(-ENOENT); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 160 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 161 | autodep->clkdm.ptr = clkdm; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 162 | } | 
|  | 163 |  | 
|  | 164 | /* | 
|  | 165 | * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable | 
|  | 166 | * @clkdm: struct clockdomain * | 
|  | 167 | * | 
|  | 168 | * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' | 
|  | 169 | * in hardware-supervised mode.  Meant to be called from clock framework | 
|  | 170 | * when a clock inside clockdomain 'clkdm' is enabled.	No return value. | 
|  | 171 | */ | 
|  | 172 | static void _clkdm_add_autodeps(struct clockdomain *clkdm) | 
|  | 173 | { | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 174 | struct clkdm_autodep *autodep; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 175 |  | 
| Paul Walmsley | ad95616 | 2010-02-22 22:09:35 -0700 | [diff] [blame] | 176 | if (!autodeps) | 
|  | 177 | return; | 
|  | 178 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 179 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | 
|  | 180 | if (IS_ERR(autodep->clkdm.ptr)) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 181 | continue; | 
|  | 182 |  | 
| Paul Walmsley | d96df00 | 2009-01-27 19:44:35 -0700 | [diff] [blame] | 183 | if (!omap_chip_is(autodep->omap_chip)) | 
|  | 184 | continue; | 
|  | 185 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 186 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 187 | "clkdm %s\n", autodep->clkdm.ptr->name, | 
|  | 188 | clkdm->name); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 189 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 190 | clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); | 
|  | 191 | clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 192 | } | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | /* | 
|  | 196 | * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm | 
|  | 197 | * @clkdm: struct clockdomain * | 
|  | 198 | * | 
|  | 199 | * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' | 
|  | 200 | * in hardware-supervised mode.  Meant to be called from clock framework | 
|  | 201 | * when a clock inside clockdomain 'clkdm' is disabled.  No return value. | 
|  | 202 | */ | 
|  | 203 | static void _clkdm_del_autodeps(struct clockdomain *clkdm) | 
|  | 204 | { | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 205 | struct clkdm_autodep *autodep; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 206 |  | 
| Paul Walmsley | ad95616 | 2010-02-22 22:09:35 -0700 | [diff] [blame] | 207 | if (!autodeps) | 
|  | 208 | return; | 
|  | 209 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 210 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | 
|  | 211 | if (IS_ERR(autodep->clkdm.ptr)) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 212 | continue; | 
|  | 213 |  | 
| Paul Walmsley | d96df00 | 2009-01-27 19:44:35 -0700 | [diff] [blame] | 214 | if (!omap_chip_is(autodep->omap_chip)) | 
|  | 215 | continue; | 
|  | 216 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 217 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 218 | "clkdm %s\n", autodep->clkdm.ptr->name, | 
|  | 219 | clkdm->name); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 220 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 221 | clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); | 
|  | 222 | clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 223 | } | 
|  | 224 | } | 
|  | 225 |  | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 226 | /* | 
|  | 227 | * _omap2_clkdm_set_hwsup - set the hwsup idle transition bit | 
|  | 228 | * @clkdm: struct clockdomain * | 
|  | 229 | * @enable: int 0 to disable, 1 to enable | 
|  | 230 | * | 
|  | 231 | * Internal helper for actually switching the bit that controls hwsup | 
|  | 232 | * idle transitions for clkdm. | 
|  | 233 | */ | 
|  | 234 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) | 
|  | 235 | { | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 236 | u32 bits, v; | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 237 |  | 
|  | 238 | if (cpu_is_omap24xx()) { | 
|  | 239 | if (enable) | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 240 | bits = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 241 | else | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 242 | bits = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; | 
| Rajendra Nayak | 766d305 | 2010-03-31 04:16:30 -0600 | [diff] [blame] | 243 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 244 | if (enable) | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 245 | bits = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 246 | else | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 247 | bits = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 248 | } else { | 
|  | 249 | BUG(); | 
|  | 250 | } | 
|  | 251 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 252 | bits = bits << __ffs(clkdm->clktrctrl_mask); | 
|  | 253 |  | 
|  | 254 | v = __raw_readl(clkdm->clkstctrl_reg); | 
|  | 255 | v &= ~(clkdm->clktrctrl_mask); | 
|  | 256 | v |= bits; | 
|  | 257 | __raw_writel(v, clkdm->clkstctrl_reg); | 
|  | 258 |  | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 259 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 260 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 261 | /** | 
|  | 262 | * _init_wkdep_usecount - initialize wkdep usecounts to match hardware | 
|  | 263 | * @clkdm: clockdomain to initialize wkdep usecounts | 
|  | 264 | * | 
|  | 265 | * Initialize the wakeup dependency usecount variables for clockdomain @clkdm. | 
|  | 266 | * If a wakeup dependency is present in the hardware, the usecount will be | 
|  | 267 | * set to 1; otherwise, it will be set to 0.  Software should clear all | 
|  | 268 | * software wakeup dependencies prior to calling this function if it wishes | 
|  | 269 | * to ensure that all usecounts start at 0.  No return value. | 
|  | 270 | */ | 
|  | 271 | static void _init_wkdep_usecount(struct clockdomain *clkdm) | 
|  | 272 | { | 
|  | 273 | u32 v; | 
|  | 274 | struct clkdm_dep *cd; | 
|  | 275 |  | 
|  | 276 | if (!clkdm->wkdep_srcs) | 
|  | 277 | return; | 
|  | 278 |  | 
|  | 279 | for (cd = clkdm->wkdep_srcs; cd->clkdm_name; cd++) { | 
|  | 280 | if (!omap_chip_is(cd->omap_chip)) | 
|  | 281 | continue; | 
|  | 282 |  | 
|  | 283 | if (!cd->clkdm && cd->clkdm_name) | 
|  | 284 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | 
|  | 285 |  | 
|  | 286 | if (!cd->clkdm) { | 
|  | 287 | WARN(!cd->clkdm, "clockdomain: %s: wkdep clkdm %s not " | 
|  | 288 | "found\n", clkdm->name, cd->clkdm_name); | 
|  | 289 | continue; | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs, | 
|  | 293 | PM_WKDEP, | 
|  | 294 | (1 << cd->clkdm->dep_bit)); | 
|  | 295 |  | 
|  | 296 | if (v) | 
|  | 297 | pr_debug("clockdomain: %s: wakeup dependency already " | 
|  | 298 | "set to wake up when %s wakes\n", | 
|  | 299 | clkdm->name, cd->clkdm->name); | 
|  | 300 |  | 
|  | 301 | atomic_set(&cd->wkdep_usecount, (v) ? 1 : 0); | 
|  | 302 | } | 
|  | 303 | } | 
|  | 304 |  | 
|  | 305 | /** | 
|  | 306 | * _init_sleepdep_usecount - initialize sleepdep usecounts to match hardware | 
|  | 307 | * @clkdm: clockdomain to initialize sleepdep usecounts | 
|  | 308 | * | 
|  | 309 | * Initialize the sleep dependency usecount variables for clockdomain @clkdm. | 
|  | 310 | * If a sleep dependency is present in the hardware, the usecount will be | 
|  | 311 | * set to 1; otherwise, it will be set to 0.  Software should clear all | 
|  | 312 | * software sleep dependencies prior to calling this function if it wishes | 
|  | 313 | * to ensure that all usecounts start at 0.  No return value. | 
|  | 314 | */ | 
|  | 315 | static void _init_sleepdep_usecount(struct clockdomain *clkdm) | 
|  | 316 | { | 
|  | 317 | u32 v; | 
|  | 318 | struct clkdm_dep *cd; | 
|  | 319 |  | 
|  | 320 | if (!cpu_is_omap34xx()) | 
|  | 321 | return; | 
|  | 322 |  | 
|  | 323 | if (!clkdm->sleepdep_srcs) | 
|  | 324 | return; | 
|  | 325 |  | 
|  | 326 | for (cd = clkdm->sleepdep_srcs; cd->clkdm_name; cd++) { | 
|  | 327 | if (!omap_chip_is(cd->omap_chip)) | 
|  | 328 | continue; | 
|  | 329 |  | 
|  | 330 | if (!cd->clkdm && cd->clkdm_name) | 
|  | 331 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | 
|  | 332 |  | 
|  | 333 | if (!cd->clkdm) { | 
|  | 334 | WARN(!cd->clkdm, "clockdomain: %s: sleepdep clkdm %s " | 
|  | 335 | "not found\n", clkdm->name, cd->clkdm_name); | 
|  | 336 | continue; | 
|  | 337 | } | 
|  | 338 |  | 
|  | 339 | v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs, | 
|  | 340 | OMAP3430_CM_SLEEPDEP, | 
|  | 341 | (1 << cd->clkdm->dep_bit)); | 
|  | 342 |  | 
|  | 343 | if (v) | 
|  | 344 | pr_debug("clockdomain: %s: sleep dependency already " | 
|  | 345 | "set to prevent from idling until %s " | 
|  | 346 | "idles\n", clkdm->name, cd->clkdm->name); | 
|  | 347 |  | 
|  | 348 | atomic_set(&cd->sleepdep_usecount, (v) ? 1 : 0); | 
|  | 349 | } | 
|  | 350 | }; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 351 |  | 
|  | 352 | /* Public functions */ | 
|  | 353 |  | 
|  | 354 | /** | 
|  | 355 | * clkdm_init - set up the clockdomain layer | 
|  | 356 | * @clkdms: optional pointer to an array of clockdomains to register | 
|  | 357 | * @init_autodeps: optional pointer to an array of autodeps to register | 
|  | 358 | * | 
|  | 359 | * Set up internal state.  If a pointer to an array of clockdomains | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 360 | * @clkdms was supplied, loop through the list of clockdomains, | 
|  | 361 | * register all that are available on the current platform. Similarly, | 
|  | 362 | * if a pointer to an array of clockdomain autodependencies | 
|  | 363 | * @init_autodeps was provided, register those.  No return value. | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 364 | */ | 
|  | 365 | void clkdm_init(struct clockdomain **clkdms, | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 366 | struct clkdm_autodep *init_autodeps) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 367 | { | 
|  | 368 | struct clockdomain **c = NULL; | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 369 | struct clockdomain *clkdm; | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 370 | struct clkdm_autodep *autodep = NULL; | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 371 |  | 
|  | 372 | if (clkdms) | 
|  | 373 | for (c = clkdms; *c; c++) | 
| Paul Walmsley | e909d62 | 2010-01-26 20:13:00 -0700 | [diff] [blame] | 374 | _clkdm_register(*c); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 375 |  | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 376 | autodeps = init_autodeps; | 
|  | 377 | if (autodeps) | 
|  | 378 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) | 
|  | 379 | _autodep_lookup(autodep); | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 380 |  | 
|  | 381 | /* | 
|  | 382 | * Ensure that the *dep_usecount registers reflect the current | 
|  | 383 | * state of the PRCM. | 
|  | 384 | */ | 
|  | 385 | list_for_each_entry(clkdm, &clkdm_list, node) { | 
|  | 386 | _init_wkdep_usecount(clkdm); | 
|  | 387 | _init_sleepdep_usecount(clkdm); | 
|  | 388 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 389 | } | 
|  | 390 |  | 
|  | 391 | /** | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 392 | * clkdm_lookup - look up a clockdomain by name, return a pointer | 
|  | 393 | * @name: name of clockdomain | 
|  | 394 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 395 | * Find a registered clockdomain by its name @name.  Returns a pointer | 
|  | 396 | * to the struct clockdomain if found, or NULL otherwise. | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 397 | */ | 
|  | 398 | struct clockdomain *clkdm_lookup(const char *name) | 
|  | 399 | { | 
|  | 400 | struct clockdomain *clkdm, *temp_clkdm; | 
|  | 401 |  | 
|  | 402 | if (!name) | 
|  | 403 | return NULL; | 
|  | 404 |  | 
|  | 405 | clkdm = NULL; | 
|  | 406 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 407 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | 
|  | 408 | if (!strcmp(name, temp_clkdm->name)) { | 
|  | 409 | clkdm = temp_clkdm; | 
|  | 410 | break; | 
|  | 411 | } | 
|  | 412 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 413 |  | 
|  | 414 | return clkdm; | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | /** | 
|  | 418 | * clkdm_for_each - call function on each registered clockdomain | 
|  | 419 | * @fn: callback function * | 
|  | 420 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 421 | * Call the supplied function @fn for each registered clockdomain. | 
|  | 422 | * The callback function @fn can return anything but 0 to bail | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 423 | * out early from the iterator.  The callback function is called with | 
|  | 424 | * the clkdm_mutex held, so no clockdomain structure manipulation | 
|  | 425 | * functions should be called from the callback, although hardware | 
|  | 426 | * clockdomain control functions are fine.  Returns the last return | 
|  | 427 | * value of the callback function, which should be 0 for success or | 
|  | 428 | * anything else to indicate failure; or -EINVAL if the function pointer | 
|  | 429 | * is null. | 
|  | 430 | */ | 
| Peter 'p2' De Schrijver | a23456e | 2008-10-15 18:13:47 +0300 | [diff] [blame] | 431 | int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), | 
|  | 432 | void *user) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 433 | { | 
|  | 434 | struct clockdomain *clkdm; | 
|  | 435 | int ret = 0; | 
|  | 436 |  | 
|  | 437 | if (!fn) | 
|  | 438 | return -EINVAL; | 
|  | 439 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 440 | list_for_each_entry(clkdm, &clkdm_list, node) { | 
| Peter 'p2' De Schrijver | a23456e | 2008-10-15 18:13:47 +0300 | [diff] [blame] | 441 | ret = (*fn)(clkdm, user); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 442 | if (ret) | 
|  | 443 | break; | 
|  | 444 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 445 |  | 
|  | 446 | return ret; | 
|  | 447 | } | 
|  | 448 |  | 
|  | 449 |  | 
| Paul Walmsley | e89087c | 2008-05-20 18:41:35 -0600 | [diff] [blame] | 450 | /** | 
|  | 451 | * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in | 
|  | 452 | * @clkdm: struct clockdomain * | 
|  | 453 | * | 
|  | 454 | * Return a pointer to the struct powerdomain that the specified clockdomain | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 455 | * @clkdm exists in, or returns NULL if @clkdm is NULL. | 
| Paul Walmsley | e89087c | 2008-05-20 18:41:35 -0600 | [diff] [blame] | 456 | */ | 
|  | 457 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | 
|  | 458 | { | 
|  | 459 | if (!clkdm) | 
|  | 460 | return NULL; | 
|  | 461 |  | 
| Paul Walmsley | 5b74c67 | 2009-02-03 02:10:03 -0700 | [diff] [blame] | 462 | return clkdm->pwrdm.ptr; | 
| Paul Walmsley | e89087c | 2008-05-20 18:41:35 -0600 | [diff] [blame] | 463 | } | 
|  | 464 |  | 
|  | 465 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 466 | /* Hardware clockdomain control */ | 
|  | 467 |  | 
|  | 468 | /** | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 469 | * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 | 
|  | 470 | * @clkdm1: wake this struct clockdomain * up (dependent) | 
|  | 471 | * @clkdm2: when this struct clockdomain * wakes up (source) | 
|  | 472 | * | 
|  | 473 | * When the clockdomain represented by @clkdm2 wakes up, wake up | 
|  | 474 | * @clkdm1. Implemented in hardware on the OMAP, this feature is | 
|  | 475 | * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. | 
|  | 476 | * Returns -EINVAL if presented with invalid clockdomain pointers, | 
|  | 477 | * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon | 
|  | 478 | * success. | 
|  | 479 | */ | 
|  | 480 | int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 481 | { | 
|  | 482 | struct clkdm_dep *cd; | 
|  | 483 |  | 
|  | 484 | if (!clkdm1 || !clkdm2) | 
|  | 485 | return -EINVAL; | 
|  | 486 |  | 
|  | 487 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | 
|  | 488 | if (IS_ERR(cd)) { | 
|  | 489 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | 
|  | 490 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | 
|  | 491 | return PTR_ERR(cd); | 
|  | 492 | } | 
|  | 493 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 494 | if (atomic_inc_return(&cd->wkdep_usecount) == 1) { | 
|  | 495 | pr_debug("clockdomain: hardware will wake up %s when %s wakes " | 
|  | 496 | "up\n", clkdm1->name, clkdm2->name); | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 497 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 498 | prm_set_mod_reg_bits((1 << clkdm2->dep_bit), | 
|  | 499 | clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); | 
|  | 500 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 501 |  | 
|  | 502 | return 0; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | /** | 
|  | 506 | * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1 | 
|  | 507 | * @clkdm1: wake this struct clockdomain * up (dependent) | 
|  | 508 | * @clkdm2: when this struct clockdomain * wakes up (source) | 
|  | 509 | * | 
|  | 510 | * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 | 
|  | 511 | * wakes up.  Returns -EINVAL if presented with invalid clockdomain | 
|  | 512 | * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or | 
|  | 513 | * 0 upon success. | 
|  | 514 | */ | 
|  | 515 | int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 516 | { | 
|  | 517 | struct clkdm_dep *cd; | 
|  | 518 |  | 
|  | 519 | if (!clkdm1 || !clkdm2) | 
|  | 520 | return -EINVAL; | 
|  | 521 |  | 
|  | 522 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | 
|  | 523 | if (IS_ERR(cd)) { | 
|  | 524 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | 
|  | 525 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | 
|  | 526 | return PTR_ERR(cd); | 
|  | 527 | } | 
|  | 528 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 529 | if (atomic_dec_return(&cd->wkdep_usecount) == 0) { | 
|  | 530 | pr_debug("clockdomain: hardware will no longer wake up %s " | 
|  | 531 | "after %s wakes up\n", clkdm1->name, clkdm2->name); | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 532 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 533 | prm_clear_mod_reg_bits((1 << clkdm2->dep_bit), | 
|  | 534 | clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); | 
|  | 535 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 536 |  | 
|  | 537 | return 0; | 
|  | 538 | } | 
|  | 539 |  | 
|  | 540 | /** | 
|  | 541 | * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1 | 
|  | 542 | * @clkdm1: wake this struct clockdomain * up (dependent) | 
|  | 543 | * @clkdm2: when this struct clockdomain * wakes up (source) | 
|  | 544 | * | 
|  | 545 | * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be | 
|  | 546 | * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL | 
|  | 547 | * if either clockdomain pointer is invalid; or -ENOENT if the hardware | 
|  | 548 | * is incapable. | 
|  | 549 | * | 
|  | 550 | * REVISIT: Currently this function only represents software-controllable | 
|  | 551 | * wakeup dependencies.  Wakeup dependencies fixed in hardware are not | 
|  | 552 | * yet handled here. | 
|  | 553 | */ | 
|  | 554 | int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 555 | { | 
|  | 556 | struct clkdm_dep *cd; | 
|  | 557 |  | 
|  | 558 | if (!clkdm1 || !clkdm2) | 
|  | 559 | return -EINVAL; | 
|  | 560 |  | 
|  | 561 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | 
|  | 562 | if (IS_ERR(cd)) { | 
|  | 563 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | 
|  | 564 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | 
|  | 565 | return PTR_ERR(cd); | 
|  | 566 | } | 
|  | 567 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 568 | /* XXX It's faster to return the atomic wkdep_usecount */ | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 569 | return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP, | 
|  | 570 | (1 << clkdm2->dep_bit)); | 
|  | 571 | } | 
|  | 572 |  | 
|  | 573 | /** | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 574 | * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm | 
|  | 575 | * @clkdm: struct clockdomain * to remove all wakeup dependencies from | 
|  | 576 | * | 
|  | 577 | * Remove all inter-clockdomain wakeup dependencies that could cause | 
|  | 578 | * @clkdm to wake.  Intended to be used during boot to initialize the | 
|  | 579 | * PRCM to a known state, after all clockdomains are put into swsup idle | 
|  | 580 | * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or | 
|  | 581 | * 0 upon success. | 
|  | 582 | */ | 
|  | 583 | int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) | 
|  | 584 | { | 
|  | 585 | struct clkdm_dep *cd; | 
|  | 586 | u32 mask = 0; | 
|  | 587 |  | 
|  | 588 | if (!clkdm) | 
|  | 589 | return -EINVAL; | 
|  | 590 |  | 
|  | 591 | for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) { | 
|  | 592 | if (!omap_chip_is(cd->omap_chip)) | 
|  | 593 | continue; | 
|  | 594 |  | 
|  | 595 | /* PRM accesses are slow, so minimize them */ | 
|  | 596 | mask |= 1 << cd->clkdm->dep_bit; | 
|  | 597 | atomic_set(&cd->wkdep_usecount, 0); | 
|  | 598 | } | 
|  | 599 |  | 
|  | 600 | prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, PM_WKDEP); | 
|  | 601 |  | 
|  | 602 | return 0; | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | /** | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 606 | * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 | 
|  | 607 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | 
|  | 608 | * @clkdm2: when this struct clockdomain * is active (source) | 
|  | 609 | * | 
|  | 610 | * Prevent @clkdm1 from automatically going inactive (and then to | 
|  | 611 | * retention or off) if @clkdm2 is active.  Returns -EINVAL if | 
|  | 612 | * presented with invalid clockdomain pointers or called on a machine | 
|  | 613 | * that does not support software-configurable hardware sleep | 
|  | 614 | * dependencies, -ENOENT if the specified dependency cannot be set in | 
|  | 615 | * hardware, or 0 upon success. | 
|  | 616 | */ | 
|  | 617 | int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 618 | { | 
|  | 619 | struct clkdm_dep *cd; | 
|  | 620 |  | 
|  | 621 | if (!cpu_is_omap34xx()) | 
|  | 622 | return -EINVAL; | 
|  | 623 |  | 
|  | 624 | if (!clkdm1 || !clkdm2) | 
|  | 625 | return -EINVAL; | 
|  | 626 |  | 
|  | 627 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | 
|  | 628 | if (IS_ERR(cd)) { | 
|  | 629 | pr_debug("clockdomain: hardware cannot set/clear sleep " | 
|  | 630 | "dependency affecting %s from %s\n", clkdm1->name, | 
|  | 631 | clkdm2->name); | 
|  | 632 | return PTR_ERR(cd); | 
|  | 633 | } | 
|  | 634 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 635 | if (atomic_inc_return(&cd->sleepdep_usecount) == 1) { | 
|  | 636 | pr_debug("clockdomain: will prevent %s from sleeping if %s " | 
|  | 637 | "is active\n", clkdm1->name, clkdm2->name); | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 638 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 639 | cm_set_mod_reg_bits((1 << clkdm2->dep_bit), | 
|  | 640 | clkdm1->pwrdm.ptr->prcm_offs, | 
|  | 641 | OMAP3430_CM_SLEEPDEP); | 
|  | 642 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 643 |  | 
|  | 644 | return 0; | 
|  | 645 | } | 
|  | 646 |  | 
|  | 647 | /** | 
|  | 648 | * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1 | 
|  | 649 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | 
|  | 650 | * @clkdm2: when this struct clockdomain * is active (source) | 
|  | 651 | * | 
|  | 652 | * Allow @clkdm1 to automatically go inactive (and then to retention or | 
|  | 653 | * off), independent of the activity state of @clkdm2.  Returns -EINVAL | 
|  | 654 | * if presented with invalid clockdomain pointers or called on a machine | 
|  | 655 | * that does not support software-configurable hardware sleep dependencies, | 
|  | 656 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | 
|  | 657 | * 0 upon success. | 
|  | 658 | */ | 
|  | 659 | int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 660 | { | 
|  | 661 | struct clkdm_dep *cd; | 
|  | 662 |  | 
|  | 663 | if (!cpu_is_omap34xx()) | 
|  | 664 | return -EINVAL; | 
|  | 665 |  | 
|  | 666 | if (!clkdm1 || !clkdm2) | 
|  | 667 | return -EINVAL; | 
|  | 668 |  | 
|  | 669 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | 
|  | 670 | if (IS_ERR(cd)) { | 
|  | 671 | pr_debug("clockdomain: hardware cannot set/clear sleep " | 
|  | 672 | "dependency affecting %s from %s\n", clkdm1->name, | 
|  | 673 | clkdm2->name); | 
|  | 674 | return PTR_ERR(cd); | 
|  | 675 | } | 
|  | 676 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 677 | if (atomic_dec_return(&cd->sleepdep_usecount) == 0) { | 
|  | 678 | pr_debug("clockdomain: will no longer prevent %s from " | 
|  | 679 | "sleeping if %s is active\n", clkdm1->name, | 
|  | 680 | clkdm2->name); | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 681 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 682 | cm_clear_mod_reg_bits((1 << clkdm2->dep_bit), | 
|  | 683 | clkdm1->pwrdm.ptr->prcm_offs, | 
|  | 684 | OMAP3430_CM_SLEEPDEP); | 
|  | 685 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 686 |  | 
|  | 687 | return 0; | 
|  | 688 | } | 
|  | 689 |  | 
|  | 690 | /** | 
|  | 691 | * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1 | 
|  | 692 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | 
|  | 693 | * @clkdm2: when this struct clockdomain * is active (source) | 
|  | 694 | * | 
|  | 695 | * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will | 
|  | 696 | * not be allowed to automatically go inactive if @clkdm2 is active; | 
|  | 697 | * 0 if @clkdm1's automatic power state inactivity transition is independent | 
|  | 698 | * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called | 
|  | 699 | * on a machine that does not support software-configurable hardware sleep | 
|  | 700 | * dependencies; or -ENOENT if the hardware is incapable. | 
|  | 701 | * | 
|  | 702 | * REVISIT: Currently this function only represents software-controllable | 
|  | 703 | * sleep dependencies.	Sleep dependencies fixed in hardware are not | 
|  | 704 | * yet handled here. | 
|  | 705 | */ | 
|  | 706 | int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 
|  | 707 | { | 
|  | 708 | struct clkdm_dep *cd; | 
|  | 709 |  | 
|  | 710 | if (!cpu_is_omap34xx()) | 
|  | 711 | return -EINVAL; | 
|  | 712 |  | 
|  | 713 | if (!clkdm1 || !clkdm2) | 
|  | 714 | return -EINVAL; | 
|  | 715 |  | 
|  | 716 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | 
|  | 717 | if (IS_ERR(cd)) { | 
|  | 718 | pr_debug("clockdomain: hardware cannot set/clear sleep " | 
|  | 719 | "dependency affecting %s from %s\n", clkdm1->name, | 
|  | 720 | clkdm2->name); | 
|  | 721 | return PTR_ERR(cd); | 
|  | 722 | } | 
|  | 723 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 724 | /* XXX It's faster to return the atomic sleepdep_usecount */ | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 725 | return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, | 
|  | 726 | OMAP3430_CM_SLEEPDEP, | 
|  | 727 | (1 << clkdm2->dep_bit)); | 
|  | 728 | } | 
|  | 729 |  | 
| Paul Walmsley | 369d561 | 2010-01-26 20:13:01 -0700 | [diff] [blame] | 730 | /** | 
|  | 731 | * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm | 
|  | 732 | * @clkdm: struct clockdomain * to remove all sleep dependencies from | 
|  | 733 | * | 
|  | 734 | * Remove all inter-clockdomain sleep dependencies that could prevent | 
|  | 735 | * @clkdm from idling.  Intended to be used during boot to initialize the | 
|  | 736 | * PRCM to a known state, after all clockdomains are put into swsup idle | 
|  | 737 | * and woken up.  Returns -EINVAL if @clkdm pointer is invalid, or | 
|  | 738 | * 0 upon success. | 
|  | 739 | */ | 
|  | 740 | int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) | 
|  | 741 | { | 
|  | 742 | struct clkdm_dep *cd; | 
|  | 743 | u32 mask = 0; | 
|  | 744 |  | 
|  | 745 | if (!cpu_is_omap34xx()) | 
|  | 746 | return -EINVAL; | 
|  | 747 |  | 
|  | 748 | if (!clkdm) | 
|  | 749 | return -EINVAL; | 
|  | 750 |  | 
|  | 751 | for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) { | 
|  | 752 | if (!omap_chip_is(cd->omap_chip)) | 
|  | 753 | continue; | 
|  | 754 |  | 
|  | 755 | /* PRM accesses are slow, so minimize them */ | 
|  | 756 | mask |= 1 << cd->clkdm->dep_bit; | 
|  | 757 | atomic_set(&cd->sleepdep_usecount, 0); | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, | 
|  | 761 | OMAP3430_CM_SLEEPDEP); | 
|  | 762 |  | 
|  | 763 | return 0; | 
|  | 764 | } | 
| Paul Walmsley | 55ed969 | 2010-01-26 20:12:59 -0700 | [diff] [blame] | 765 |  | 
|  | 766 | /** | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 767 | * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 768 | * @clkdm: struct clkdm * of a clockdomain | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 769 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 770 | * Return the clockdomain @clkdm current state transition mode from the | 
|  | 771 | * corresponding domain CM_CLKSTCTRL register.	Returns -EINVAL if @clkdm | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 772 | * is NULL or the current mode upon success. | 
|  | 773 | */ | 
|  | 774 | static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | 
|  | 775 | { | 
|  | 776 | u32 v; | 
|  | 777 |  | 
|  | 778 | if (!clkdm) | 
|  | 779 | return -EINVAL; | 
|  | 780 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 781 | v = __raw_readl(clkdm->clkstctrl_reg); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 782 | v &= clkdm->clktrctrl_mask; | 
|  | 783 | v >>= __ffs(clkdm->clktrctrl_mask); | 
|  | 784 |  | 
|  | 785 | return v; | 
|  | 786 | } | 
|  | 787 |  | 
|  | 788 | /** | 
|  | 789 | * omap2_clkdm_sleep - force clockdomain sleep transition | 
|  | 790 | * @clkdm: struct clockdomain * | 
|  | 791 | * | 
|  | 792 | * Instruct the CM to force a sleep transition on the specified | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 793 | * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 794 | * clockdomain does not support software-initiated sleep; 0 upon | 
|  | 795 | * success. | 
|  | 796 | */ | 
|  | 797 | int omap2_clkdm_sleep(struct clockdomain *clkdm) | 
|  | 798 | { | 
|  | 799 | if (!clkdm) | 
|  | 800 | return -EINVAL; | 
|  | 801 |  | 
|  | 802 | if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) { | 
|  | 803 | pr_debug("clockdomain: %s does not support forcing " | 
|  | 804 | "sleep via software\n", clkdm->name); | 
|  | 805 | return -EINVAL; | 
|  | 806 | } | 
|  | 807 |  | 
|  | 808 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); | 
|  | 809 |  | 
|  | 810 | if (cpu_is_omap24xx()) { | 
|  | 811 |  | 
|  | 812 | cm_set_mod_reg_bits(OMAP24XX_FORCESTATE, | 
| Abhijit Pagare | 3790300 | 2010-01-26 20:12:51 -0700 | [diff] [blame] | 813 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 814 |  | 
| Rajendra Nayak | 766d305 | 2010-03-31 04:16:30 -0600 | [diff] [blame] | 815 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 816 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 817 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP << | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 818 | __ffs(clkdm->clktrctrl_mask)); | 
|  | 819 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 820 | u32 v = __raw_readl(clkdm->clkstctrl_reg); | 
|  | 821 | v &= ~(clkdm->clktrctrl_mask); | 
|  | 822 | v |= bits; | 
|  | 823 | __raw_writel(v, clkdm->clkstctrl_reg); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 824 |  | 
|  | 825 | } else { | 
|  | 826 | BUG(); | 
|  | 827 | }; | 
|  | 828 |  | 
|  | 829 | return 0; | 
|  | 830 | } | 
|  | 831 |  | 
|  | 832 | /** | 
|  | 833 | * omap2_clkdm_wakeup - force clockdomain wakeup transition | 
|  | 834 | * @clkdm: struct clockdomain * | 
|  | 835 | * | 
|  | 836 | * Instruct the CM to force a wakeup transition on the specified | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 837 | * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 838 | * clockdomain does not support software-controlled wakeup; 0 upon | 
|  | 839 | * success. | 
|  | 840 | */ | 
|  | 841 | int omap2_clkdm_wakeup(struct clockdomain *clkdm) | 
|  | 842 | { | 
|  | 843 | if (!clkdm) | 
|  | 844 | return -EINVAL; | 
|  | 845 |  | 
|  | 846 | if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) { | 
|  | 847 | pr_debug("clockdomain: %s does not support forcing " | 
|  | 848 | "wakeup via software\n", clkdm->name); | 
|  | 849 | return -EINVAL; | 
|  | 850 | } | 
|  | 851 |  | 
|  | 852 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); | 
|  | 853 |  | 
|  | 854 | if (cpu_is_omap24xx()) { | 
|  | 855 |  | 
|  | 856 | cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE, | 
| Abhijit Pagare | 3790300 | 2010-01-26 20:12:51 -0700 | [diff] [blame] | 857 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 858 |  | 
| Rajendra Nayak | 766d305 | 2010-03-31 04:16:30 -0600 | [diff] [blame] | 859 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 860 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 861 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP << | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 862 | __ffs(clkdm->clktrctrl_mask)); | 
|  | 863 |  | 
| Abhijit Pagare | b099474 | 2010-01-26 20:12:53 -0700 | [diff] [blame] | 864 | u32 v = __raw_readl(clkdm->clkstctrl_reg); | 
|  | 865 | v &= ~(clkdm->clktrctrl_mask); | 
|  | 866 | v |= bits; | 
|  | 867 | __raw_writel(v, clkdm->clkstctrl_reg); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 868 |  | 
|  | 869 | } else { | 
|  | 870 | BUG(); | 
|  | 871 | }; | 
|  | 872 |  | 
|  | 873 | return 0; | 
|  | 874 | } | 
|  | 875 |  | 
|  | 876 | /** | 
|  | 877 | * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm | 
|  | 878 | * @clkdm: struct clockdomain * | 
|  | 879 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 880 | * Allow the hardware to automatically switch the clockdomain @clkdm into | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 881 | * active or idle states, as needed by downstream clocks.  If the | 
|  | 882 | * clockdomain has any downstream clocks enabled in the clock | 
|  | 883 | * framework, wkdep/sleepdep autodependencies are added; this is so | 
|  | 884 | * device drivers can read and write to the device.  No return value. | 
|  | 885 | */ | 
|  | 886 | void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | 
|  | 887 | { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 888 | if (!clkdm) | 
|  | 889 | return; | 
|  | 890 |  | 
|  | 891 | if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) { | 
|  | 892 | pr_debug("clock: automatic idle transitions cannot be enabled " | 
|  | 893 | "on clockdomain %s\n", clkdm->name); | 
|  | 894 | return; | 
|  | 895 | } | 
|  | 896 |  | 
|  | 897 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", | 
|  | 898 | clkdm->name); | 
|  | 899 |  | 
| Abhijit Pagare | 91808a8 | 2010-02-22 22:09:07 -0700 | [diff] [blame] | 900 | /* | 
|  | 901 | * XXX This should be removed once TI adds wakeup/sleep | 
|  | 902 | * dependency code and data for OMAP4. | 
|  | 903 | */ | 
|  | 904 | if (cpu_is_omap44xx()) { | 
|  | 905 | WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " | 
|  | 906 | "support is not yet implemented\n"); | 
|  | 907 | } else { | 
|  | 908 | if (atomic_read(&clkdm->usecount) > 0) | 
|  | 909 | _clkdm_add_autodeps(clkdm); | 
|  | 910 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 911 |  | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 912 | _omap2_clkdm_set_hwsup(clkdm, 1); | 
| Peter 'p2' De Schrijver | ba20bb1 | 2008-10-15 17:48:43 +0300 | [diff] [blame] | 913 |  | 
|  | 914 | pwrdm_clkdm_state_switch(clkdm); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 915 | } | 
|  | 916 |  | 
|  | 917 | /** | 
|  | 918 | * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm | 
|  | 919 | * @clkdm: struct clockdomain * | 
|  | 920 | * | 
|  | 921 | * Prevent the hardware from automatically switching the clockdomain | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 922 | * @clkdm into inactive or idle states.  If the clockdomain has | 
|  | 923 | * downstream clocks enabled in the clock framework, wkdep/sleepdep | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 924 | * autodependencies are removed.  No return value. | 
|  | 925 | */ | 
|  | 926 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | 
|  | 927 | { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 928 | if (!clkdm) | 
|  | 929 | return; | 
|  | 930 |  | 
|  | 931 | if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) { | 
|  | 932 | pr_debug("clockdomain: automatic idle transitions cannot be " | 
|  | 933 | "disabled on %s\n", clkdm->name); | 
|  | 934 | return; | 
|  | 935 | } | 
|  | 936 |  | 
|  | 937 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", | 
|  | 938 | clkdm->name); | 
|  | 939 |  | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 940 | _omap2_clkdm_set_hwsup(clkdm, 0); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 941 |  | 
| Abhijit Pagare | 91808a8 | 2010-02-22 22:09:07 -0700 | [diff] [blame] | 942 | /* | 
|  | 943 | * XXX This should be removed once TI adds wakeup/sleep | 
|  | 944 | * dependency code and data for OMAP4. | 
|  | 945 | */ | 
|  | 946 | if (cpu_is_omap44xx()) { | 
|  | 947 | WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " | 
|  | 948 | "support is not yet implemented\n"); | 
|  | 949 | } else { | 
|  | 950 | if (atomic_read(&clkdm->usecount) > 0) | 
|  | 951 | _clkdm_del_autodeps(clkdm); | 
|  | 952 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 953 | } | 
|  | 954 |  | 
|  | 955 |  | 
|  | 956 | /* Clockdomain-to-clock framework interface code */ | 
|  | 957 |  | 
|  | 958 | /** | 
|  | 959 | * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm | 
|  | 960 | * @clkdm: struct clockdomain * | 
|  | 961 | * @clk: struct clk * of the enabled downstream clock | 
|  | 962 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 963 | * Increment the usecount of the clockdomain @clkdm and ensure that it | 
|  | 964 | * is awake before @clk is enabled.  Intended to be called by | 
|  | 965 | * clk_enable() code.  If the clockdomain is in software-supervised | 
|  | 966 | * idle mode, force the clockdomain to wake.  If the clockdomain is in | 
|  | 967 | * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to | 
|  | 968 | * ensure that devices in the clockdomain can be read from/written to | 
|  | 969 | * by on-chip processors.  Returns -EINVAL if passed null pointers; | 
|  | 970 | * returns 0 upon success or if the clockdomain is in hwsup idle mode. | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 971 | */ | 
|  | 972 | int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | 
|  | 973 | { | 
|  | 974 | int v; | 
|  | 975 |  | 
|  | 976 | /* | 
|  | 977 | * XXX Rewrite this code to maintain a list of enabled | 
|  | 978 | * downstream clocks for debugging purposes? | 
|  | 979 | */ | 
|  | 980 |  | 
| Paul Walmsley | 30962d9 | 2010-02-22 22:09:38 -0700 | [diff] [blame] | 981 | if (!clkdm || !clk) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 982 | return -EINVAL; | 
|  | 983 |  | 
|  | 984 | if (atomic_inc_return(&clkdm->usecount) > 1) | 
|  | 985 | return 0; | 
|  | 986 |  | 
|  | 987 | /* Clockdomain now has one enabled downstream clock */ | 
|  | 988 |  | 
|  | 989 | pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name, | 
|  | 990 | clk->name); | 
|  | 991 |  | 
| Paul Walmsley | 30962d9 | 2010-02-22 22:09:38 -0700 | [diff] [blame] | 992 | if (!clkdm->clkstctrl_reg) | 
|  | 993 | return 0; | 
|  | 994 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 995 | v = omap2_clkdm_clktrctrl_read(clkdm); | 
|  | 996 |  | 
|  | 997 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 998 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) { | 
|  | 999 | /* Disable HW transitions when we are changing deps */ | 
|  | 1000 | _omap2_clkdm_set_hwsup(clkdm, 0); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1001 | _clkdm_add_autodeps(clkdm); | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 1002 | _omap2_clkdm_set_hwsup(clkdm, 1); | 
|  | 1003 | } else { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1004 | omap2_clkdm_wakeup(clkdm); | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 1005 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1006 |  | 
| Tomi Valkeinen | 054ce50 | 2009-01-27 19:44:31 -0700 | [diff] [blame] | 1007 | pwrdm_wait_transition(clkdm->pwrdm.ptr); | 
| Peter 'p2' De Schrijver | fe617af | 2008-10-15 17:48:44 +0300 | [diff] [blame] | 1008 | pwrdm_clkdm_state_switch(clkdm); | 
| Tomi Valkeinen | 054ce50 | 2009-01-27 19:44:31 -0700 | [diff] [blame] | 1009 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1010 | return 0; | 
|  | 1011 | } | 
|  | 1012 |  | 
|  | 1013 | /** | 
|  | 1014 | * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm | 
|  | 1015 | * @clkdm: struct clockdomain * | 
|  | 1016 | * @clk: struct clk * of the disabled downstream clock | 
|  | 1017 | * | 
| Paul Walmsley | f0271d6 | 2010-01-26 20:13:02 -0700 | [diff] [blame] | 1018 | * Decrement the usecount of this clockdomain @clkdm when @clk is | 
|  | 1019 | * disabled.  Intended to be called by clk_disable() code.  If the | 
|  | 1020 | * clockdomain usecount goes to 0, put the clockdomain to sleep | 
|  | 1021 | * (software-supervised mode) or remove the clkdm autodependencies | 
|  | 1022 | * (hardware-supervised mode).  Returns -EINVAL if passed null | 
|  | 1023 | * pointers; -ERANGE if the @clkdm usecount underflows and debugging | 
|  | 1024 | * is enabled; or returns 0 upon success or if the clockdomain is in | 
|  | 1025 | * hwsup idle mode. | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1026 | */ | 
|  | 1027 | int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | 
|  | 1028 | { | 
|  | 1029 | int v; | 
|  | 1030 |  | 
|  | 1031 | /* | 
|  | 1032 | * XXX Rewrite this code to maintain a list of enabled | 
|  | 1033 | * downstream clocks for debugging purposes? | 
|  | 1034 | */ | 
|  | 1035 |  | 
| Paul Walmsley | 30962d9 | 2010-02-22 22:09:38 -0700 | [diff] [blame] | 1036 | if (!clkdm || !clk) | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1037 | return -EINVAL; | 
|  | 1038 |  | 
|  | 1039 | #ifdef DEBUG | 
|  | 1040 | if (atomic_read(&clkdm->usecount) == 0) { | 
|  | 1041 | WARN_ON(1); /* underflow */ | 
|  | 1042 | return -ERANGE; | 
|  | 1043 | } | 
|  | 1044 | #endif | 
|  | 1045 |  | 
|  | 1046 | if (atomic_dec_return(&clkdm->usecount) > 0) | 
|  | 1047 | return 0; | 
|  | 1048 |  | 
|  | 1049 | /* All downstream clocks of this clockdomain are now disabled */ | 
|  | 1050 |  | 
|  | 1051 | pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, | 
|  | 1052 | clk->name); | 
|  | 1053 |  | 
| Paul Walmsley | 30962d9 | 2010-02-22 22:09:38 -0700 | [diff] [blame] | 1054 | if (!clkdm->clkstctrl_reg) | 
|  | 1055 | return 0; | 
|  | 1056 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1057 | v = omap2_clkdm_clktrctrl_read(clkdm); | 
|  | 1058 |  | 
|  | 1059 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 1060 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) { | 
|  | 1061 | /* Disable HW transitions when we are changing deps */ | 
|  | 1062 | _omap2_clkdm_set_hwsup(clkdm, 0); | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1063 | _clkdm_del_autodeps(clkdm); | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 1064 | _omap2_clkdm_set_hwsup(clkdm, 1); | 
|  | 1065 | } else { | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1066 | omap2_clkdm_sleep(clkdm); | 
| Kalle Jokiniemi | a0219fb | 2009-10-14 16:40:37 -0600 | [diff] [blame] | 1067 | } | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1068 |  | 
| Peter 'p2' De Schrijver | fe617af | 2008-10-15 17:48:44 +0300 | [diff] [blame] | 1069 | pwrdm_clkdm_state_switch(clkdm); | 
|  | 1070 |  | 
| Paul Walmsley | d459bfe | 2008-08-19 11:08:43 +0300 | [diff] [blame] | 1071 | return 0; | 
|  | 1072 | } | 
|  | 1073 |  |