Blame view
arch/arm/mach-omap2/pm.c
6.92 KB
6f88e9bc2 OMAP: PM: create ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * pm.c - Common OMAP2+ power management-related code * * Copyright (C) 2010 Texas Instruments, Inc. * Copyright (C) 2010 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> #include <linux/err.h> |
1482d8be5 OMAP3: PM: Progra... |
16 |
#include <linux/opp.h> |
6f88e9bc2 OMAP: PM: create ... |
17 18 19 20 |
#include <plat/omap-pm.h> #include <plat/omap_device.h> #include <plat/common.h> |
536a4c8bb arm: omap: am33x:... |
21 |
#include <plat/voltage.h> |
6f88e9bc2 OMAP: PM: create ... |
22 |
|
72e06d087 OMAP2+: powerdoma... |
23 |
#include "powerdomain.h" |
1540f2140 OMAP2+: clockdoma... |
24 |
#include "clockdomain.h" |
0c0a5d61e OMAP3: PM: Adding... |
25 |
#include "pm.h" |
eb6a2c755 omap: pm: Move se... |
26 |
|
6f88e9bc2 OMAP: PM: create ... |
27 |
static struct omap_device_pm_latency *pm_lats; |
54d8f4c29 OMAP2+: Ensure th... |
28 29 30 |
static int _init_omap_device(char *name); bool omap_pm_is_ready_status; |
766e7afc3 ARM: OMAP2+: pm: ... |
31 |
static int _init_omap_device(char *name) |
6f88e9bc2 OMAP: PM: create ... |
32 33 |
{ struct omap_hwmod *oh; |
3528c58eb OMAP: omap_device... |
34 |
struct platform_device *pdev; |
6f88e9bc2 OMAP: PM: create ... |
35 36 37 38 39 40 |
oh = omap_hwmod_lookup(name); if (WARN(!oh, "%s: could not find omap_hwmod for %s ", __func__, name)) return -ENODEV; |
3528c58eb OMAP: omap_device... |
41 42 43 |
pdev = omap_device_build(oh->name, 0, oh, NULL, 0, pm_lats, 0, false); if (WARN(IS_ERR(pdev), "%s: could not build omap_device for %s ", |
6f88e9bc2 OMAP: PM: create ... |
44 45 |
__func__, name)) return -ENODEV; |
6f88e9bc2 OMAP: PM: create ... |
46 47 48 49 50 51 52 53 |
return 0; } /* * Build omap_devices for processors and bus. */ static void omap2_init_processor_devices(void) { |
766e7afc3 ARM: OMAP2+: pm: ... |
54 |
_init_omap_device("mpu"); |
2de0baefa OMAP3: PM: Initia... |
55 |
if (omap3_has_iva()) |
766e7afc3 ARM: OMAP2+: pm: ... |
56 |
_init_omap_device("iva"); |
2de0baefa OMAP3: PM: Initia... |
57 |
|
cbf27660e OMAP4: pm: Change... |
58 |
if (cpu_is_omap44xx()) { |
766e7afc3 ARM: OMAP2+: pm: ... |
59 60 61 |
_init_omap_device("l3_main_1"); _init_omap_device("dsp"); _init_omap_device("iva"); |
cbf27660e OMAP4: pm: Change... |
62 |
} else { |
766e7afc3 ARM: OMAP2+: pm: ... |
63 |
_init_omap_device("l3_main"); |
cbf27660e OMAP4: pm: Change... |
64 |
} |
6f88e9bc2 OMAP: PM: create ... |
65 |
} |
71a488dbc OMAP4: PM: Use th... |
66 67 68 |
/* Types of sleep_switch used in omap_set_pwrdm_state */ #define FORCEWAKEUP_SWITCH 0 #define LOWPOWERSTATE_SWITCH 1 |
eb6a2c755 omap: pm: Move se... |
69 70 |
/* * This sets pwrdm state (other than mpu & core. Currently only ON & |
33de32b3e OMAP4: PM: Do not... |
71 |
* RET are supported. |
eb6a2c755 omap: pm: Move se... |
72 73 74 75 |
*/ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) { u32 cur_state; |
6349b96b4 OMAP2+: PM: Initi... |
76 |
int sleep_switch = -1; |
eb6a2c755 omap: pm: Move se... |
77 |
int ret = 0; |
b86cfb52a OMAP2+: PM: idle ... |
78 |
int hwsup = 0; |
eb6a2c755 omap: pm: Move se... |
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
if (pwrdm == NULL || IS_ERR(pwrdm)) return -EINVAL; while (!(pwrdm->pwrsts & (1 << state))) { if (state == PWRDM_POWER_OFF) return ret; state--; } cur_state = pwrdm_read_next_pwrst(pwrdm); if (cur_state == state) return ret; if (pwrdm_read_pwrst(pwrdm) < PWRDM_POWER_ON) { |
71a488dbc OMAP4: PM: Use th... |
94 95 96 97 |
if ((pwrdm_read_pwrst(pwrdm) > state) && (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { sleep_switch = LOWPOWERSTATE_SWITCH; } else { |
b86cfb52a OMAP2+: PM: idle ... |
98 |
hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]); |
68b921ad7 OMAP: clockdomain... |
99 |
clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); |
71a488dbc OMAP4: PM: Use th... |
100 101 |
sleep_switch = FORCEWAKEUP_SWITCH; } |
eb6a2c755 omap: pm: Move se... |
102 103 104 105 |
} ret = pwrdm_set_next_pwrst(pwrdm, state); if (ret) { |
e9a5190aa OMAP2+: PM: clean... |
106 107 108 |
pr_err("%s: unable to set state of powerdomain: %s ", __func__, pwrdm->name); |
eb6a2c755 omap: pm: Move se... |
109 110 |
goto err; } |
71a488dbc OMAP4: PM: Use th... |
111 112 |
switch (sleep_switch) { case FORCEWAKEUP_SWITCH: |
b86cfb52a OMAP2+: PM: idle ... |
113 |
if (hwsup) |
5cd1937b6 OMAP: clockdomain... |
114 |
clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); |
33de32b3e OMAP4: PM: Do not... |
115 |
else |
68b921ad7 OMAP: clockdomain... |
116 |
clkdm_sleep(pwrdm->pwrdm_clkdms[0]); |
71a488dbc OMAP4: PM: Use th... |
117 118 119 120 121 122 |
break; case LOWPOWERSTATE_SWITCH: pwrdm_set_lowpwrstchange(pwrdm); break; default: return ret; |
eb6a2c755 omap: pm: Move se... |
123 |
} |
71a488dbc OMAP4: PM: Use th... |
124 |
pwrdm_state_switch(pwrdm); |
eb6a2c755 omap: pm: Move se... |
125 126 127 |
err: return ret; } |
1482d8be5 OMAP3: PM: Progra... |
128 |
/* |
1e2d2df32 OMAP2+: PM: fix t... |
129 |
* This API is to be called during init to set the various voltage |
1482d8be5 OMAP3: PM: Progra... |
130 131 132 |
* domains to the voltage as per the opp table. Typically we boot up * at the nominal voltage. So this function finds out the rate of * the clock associated with the voltage domain, finds out the correct |
1e2d2df32 OMAP2+: PM: fix t... |
133 |
* opp entry and sets the voltage domain to the voltage specified |
1482d8be5 OMAP3: PM: Progra... |
134 135 136 |
* in the opp entry */ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, |
0f7aa005c ARM: OMAP2+: pm: ... |
137 |
const char *oh_name) |
1482d8be5 OMAP3: PM: Progra... |
138 139 140 141 |
{ struct voltagedomain *voltdm; struct clk *clk; struct opp *opp; |
0f7aa005c ARM: OMAP2+: pm: ... |
142 |
struct device *dev; |
1c1cb2057 OMAP2+: PM: init_... |
143 144 |
unsigned long freq_cur, freq_valid, bootup_volt; int ret = -EINVAL; |
1482d8be5 OMAP3: PM: Progra... |
145 |
|
0f7aa005c ARM: OMAP2+: pm: ... |
146 147 148 149 150 151 152 |
dev = omap_device_get_by_hwmod_name(oh_name); if (IS_ERR(dev)) { pr_err("%s: Unable to get dev pointer for hwmod %s ", __func__, oh_name); goto exit; } |
81a604823 OMAP2+: voltage: ... |
153 |
voltdm = voltdm_lookup(vdd_name); |
1482d8be5 OMAP3: PM: Progra... |
154 |
if (IS_ERR(voltdm)) { |
e9a5190aa OMAP2+: PM: clean... |
155 156 |
pr_err("%s: unable to get vdd pointer for vdd_%s ", |
1482d8be5 OMAP3: PM: Progra... |
157 158 159 160 161 162 |
__func__, vdd_name); goto exit; } clk = clk_get(NULL, clk_name); if (IS_ERR(clk)) { |
e9a5190aa OMAP2+: PM: clean... |
163 164 |
pr_err("%s: unable to get clk %s ", __func__, clk_name); |
1482d8be5 OMAP3: PM: Progra... |
165 166 |
goto exit; } |
1c1cb2057 OMAP2+: PM: init_... |
167 168 |
freq_cur = clk->rate; freq_valid = freq_cur; |
1482d8be5 OMAP3: PM: Progra... |
169 |
|
b2274ddfd OMAP2+: PM: secur... |
170 |
rcu_read_lock(); |
1c1cb2057 OMAP2+: PM: init_... |
171 |
opp = opp_find_freq_ceil(dev, &freq_valid); |
1482d8be5 OMAP3: PM: Progra... |
172 |
if (IS_ERR(opp)) { |
1c1cb2057 OMAP2+: PM: init_... |
173 174 175 176 177 178 179 180 181 |
opp = opp_find_freq_floor(dev, &freq_valid); if (IS_ERR(opp)) { rcu_read_unlock(); pr_err("%s: no boot OPP match for %ld on vdd_%s ", __func__, freq_cur, vdd_name); ret = -ENOENT; goto exit_ck; } |
1482d8be5 OMAP3: PM: Progra... |
182 183 184 |
} bootup_volt = opp_get_voltage(opp); |
b2274ddfd OMAP2+: PM: secur... |
185 |
rcu_read_unlock(); |
1482d8be5 OMAP3: PM: Progra... |
186 |
if (!bootup_volt) { |
e9a5190aa OMAP2+: PM: clean... |
187 |
pr_err("%s: unable to find voltage corresponding " |
1482d8be5 OMAP3: PM: Progra... |
188 189 |
"to the bootup OPP for vdd_%s ", __func__, vdd_name); |
1c1cb2057 OMAP2+: PM: init_... |
190 191 |
ret = -ENOENT; goto exit_ck; |
1482d8be5 OMAP3: PM: Progra... |
192 |
} |
1c1cb2057 OMAP2+: PM: init_... |
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
/* * Frequency and Voltage have to be sequenced: if we move from * a lower frequency to higher frequency, raise voltage, followed by * frequency, and vice versa. we assume that the voltage at boot * is the required voltage for the frequency it was set for. * NOTE: * we can check the frequency, but there is numerous ways to set * voltage. We play the safe path and just set the voltage. */ if (freq_cur < freq_valid) { ret = voltdm_scale(voltdm, bootup_volt); if (ret) { pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s ", __func__, vdd_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } /* Set freq only if there is a difference in freq */ if (freq_valid != freq_cur) { ret = clk_set_rate(clk, freq_valid); if (ret) { pr_err("%s: Fail set clk-%s(f=%ld v=%ld)on vdd%s ", __func__, clk_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } if (freq_cur >= freq_valid) { ret = voltdm_scale(voltdm, bootup_volt); if (ret) { pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s ", __func__, clk_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } ret = 0; exit_ck: clk_put(clk); if (!ret) return 0; |
1482d8be5 OMAP3: PM: Progra... |
243 244 |
exit: |
e9a5190aa OMAP2+: PM: clean... |
245 246 |
pr_err("%s: unable to set vdd_%s ", __func__, vdd_name); |
1482d8be5 OMAP3: PM: Progra... |
247 248 249 250 251 252 253 |
return -EINVAL; } static void __init omap3_init_voltages(void) { if (!cpu_is_omap34xx()) return; |
e68ac3cd1 arm:omap:am33xx: ... |
254 255 256 257 258 259 |
if (cpu_is_am33xx()) { omap2_set_init_voltage("mpu", "dpll_mpu_ck", "mpu"); } else { omap2_set_init_voltage("mpu_iva", "dpll1_ck", "mpu"); omap2_set_init_voltage("core", "l3_ick", "l3_main"); } |
1482d8be5 OMAP3: PM: Progra... |
260 |
} |
1376ee1d1 OMAP4: PM: Progra... |
261 262 263 264 |
static void __init omap4_init_voltages(void) { if (!cpu_is_omap44xx()) return; |
0f7aa005c ARM: OMAP2+: pm: ... |
265 266 267 |
omap2_set_init_voltage("mpu", "dpll_mpu_ck", "mpu"); omap2_set_init_voltage("core", "l3_div_ck", "l3_main_1"); omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva"); |
1376ee1d1 OMAP4: PM: Progra... |
268 |
} |
6f88e9bc2 OMAP: PM: create ... |
269 270 |
static int __init omap2_common_pm_init(void) { |
476b679a5 arm/dts: OMAP3+: ... |
271 272 |
if (!of_have_populated_dt()) omap2_init_processor_devices(); |
6f88e9bc2 OMAP: PM: create ... |
273 274 275 276 |
omap_pm_if_init(); return 0; } |
1cbbe37ac OMAP: pm.c correc... |
277 |
postcore_initcall(omap2_common_pm_init); |
6f88e9bc2 OMAP: PM: create ... |
278 |
|
2f34ce81b OMAP3: PM: Adding... |
279 280 |
static int __init omap2_common_pm_late_init(void) { |
fbc319f67 OMAP3: PM: Regist... |
281 282 |
/* Init the OMAP TWL parameters */ omap3_twl_init(); |
7bc3ed9ae OMAP4: Register v... |
283 |
omap4_twl_init(); |
1482d8be5 OMAP3: PM: Progra... |
284 |
|
fbc319f67 OMAP3: PM: Regist... |
285 |
/* Init the voltage layer */ |
2f34ce81b OMAP3: PM: Adding... |
286 |
omap_voltage_late_init(); |
1482d8be5 OMAP3: PM: Progra... |
287 288 289 |
/* Initialize the voltages */ omap3_init_voltages(); |
1376ee1d1 OMAP4: PM: Progra... |
290 |
omap4_init_voltages(); |
1482d8be5 OMAP3: PM: Progra... |
291 |
|
fbc319f67 OMAP3: PM: Regist... |
292 |
/* Smartreflex device init */ |
0c0a5d61e OMAP3: PM: Adding... |
293 |
omap_devinit_smartreflex(); |
2f34ce81b OMAP3: PM: Adding... |
294 |
|
54d8f4c29 OMAP2+: Ensure th... |
295 296 297 |
omap_pm_is_ready_status = true; /* let the other CPU know as well */ smp_wmb(); |
2f34ce81b OMAP3: PM: Adding... |
298 299 300 |
return 0; } late_initcall(omap2_common_pm_late_init); |