Blame view

arch/arm/mach-omap2/pm.c 6.92 KB
6f88e9bc2   Kevin Hilman   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   Thara Gopinath   OMAP3: PM: Progra...
16
  #include <linux/opp.h>
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
17
18
19
20
  
  #include <plat/omap-pm.h>
  #include <plat/omap_device.h>
  #include <plat/common.h>
536a4c8bb   Afzal Mohammed   arm: omap: am33x:...
21
  #include <plat/voltage.h>
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
22

72e06d087   Paul Walmsley   OMAP2+: powerdoma...
23
  #include "powerdomain.h"
1540f2140   Paul Walmsley   OMAP2+: clockdoma...
24
  #include "clockdomain.h"
0c0a5d61e   Thara Gopinath   OMAP3: PM: Adding...
25
  #include "pm.h"
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
26

6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
27
  static struct omap_device_pm_latency *pm_lats;
54d8f4c29   Nishanth Menon   OMAP2+: Ensure th...
28
29
30
  static int _init_omap_device(char *name);
  
  bool omap_pm_is_ready_status;
766e7afc3   Benoit Cousson   ARM: OMAP2+: pm: ...
31
  static int _init_omap_device(char *name)
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
32
33
  {
  	struct omap_hwmod *oh;
3528c58eb   Kevin Hilman   OMAP: omap_device...
34
  	struct platform_device *pdev;
6f88e9bc2   Kevin Hilman   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   Kevin Hilman   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   Kevin Hilman   OMAP: PM: create ...
44
45
  		 __func__, name))
  		return -ENODEV;
6f88e9bc2   Kevin Hilman   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   Benoit Cousson   ARM: OMAP2+: pm: ...
54
  	_init_omap_device("mpu");
2de0baefa   Sanjeev Premi   OMAP3: PM: Initia...
55
  	if (omap3_has_iva())
766e7afc3   Benoit Cousson   ARM: OMAP2+: pm: ...
56
  		_init_omap_device("iva");
2de0baefa   Sanjeev Premi   OMAP3: PM: Initia...
57

cbf27660e   Benoit Cousson   OMAP4: pm: Change...
58
  	if (cpu_is_omap44xx()) {
766e7afc3   Benoit Cousson   ARM: OMAP2+: pm: ...
59
60
61
  		_init_omap_device("l3_main_1");
  		_init_omap_device("dsp");
  		_init_omap_device("iva");
cbf27660e   Benoit Cousson   OMAP4: pm: Change...
62
  	} else {
766e7afc3   Benoit Cousson   ARM: OMAP2+: pm: ...
63
  		_init_omap_device("l3_main");
cbf27660e   Benoit Cousson   OMAP4: pm: Change...
64
  	}
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
65
  }
71a488dbc   Rajendra Nayak   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   Santosh Shilimkar   omap: pm: Move se...
69
70
  /*
   * This sets pwrdm state (other than mpu & core. Currently only ON &
33de32b3e   Rajendra Nayak   OMAP4: PM: Do not...
71
   * RET are supported.
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
72
73
74
75
   */
  int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
  {
  	u32 cur_state;
6349b96b4   Rajendra Nayak   OMAP2+: PM: Initi...
76
  	int sleep_switch = -1;
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
77
  	int ret = 0;
b86cfb52a   Rajendra Nayak   OMAP2+: PM: idle ...
78
  	int hwsup = 0;
eb6a2c755   Santosh Shilimkar   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   Rajendra Nayak   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   Rajendra Nayak   OMAP2+: PM: idle ...
98
  			hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
68b921ad7   Rajendra Nayak   OMAP: clockdomain...
99
  			clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
71a488dbc   Rajendra Nayak   OMAP4: PM: Use th...
100
101
  			sleep_switch = FORCEWAKEUP_SWITCH;
  		}
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
102
103
104
105
  	}
  
  	ret = pwrdm_set_next_pwrst(pwrdm, state);
  	if (ret) {
e9a5190aa   Johan Hovold   OMAP2+: PM: clean...
106
107
108
  		pr_err("%s: unable to set state of powerdomain: %s
  ",
  		       __func__, pwrdm->name);
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
109
110
  		goto err;
  	}
71a488dbc   Rajendra Nayak   OMAP4: PM: Use th...
111
112
  	switch (sleep_switch) {
  	case FORCEWAKEUP_SWITCH:
b86cfb52a   Rajendra Nayak   OMAP2+: PM: idle ...
113
  		if (hwsup)
5cd1937b6   Rajendra Nayak   OMAP: clockdomain...
114
  			clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
33de32b3e   Rajendra Nayak   OMAP4: PM: Do not...
115
  		else
68b921ad7   Rajendra Nayak   OMAP: clockdomain...
116
  			clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
71a488dbc   Rajendra Nayak   OMAP4: PM: Use th...
117
118
119
120
121
122
  		break;
  	case LOWPOWERSTATE_SWITCH:
  		pwrdm_set_lowpwrstchange(pwrdm);
  		break;
  	default:
  		return ret;
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
123
  	}
71a488dbc   Rajendra Nayak   OMAP4: PM: Use th...
124
  	pwrdm_state_switch(pwrdm);
eb6a2c755   Santosh Shilimkar   omap: pm: Move se...
125
126
127
  err:
  	return ret;
  }
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
128
  /*
1e2d2df32   Johan Hovold   OMAP2+: PM: fix t...
129
   * This API is to be called during init to set the various voltage
1482d8be5   Thara Gopinath   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   Johan Hovold   OMAP2+: PM: fix t...
133
   * opp entry and sets the voltage domain to the voltage specified
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
134
135
136
   * in the opp entry
   */
  static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
0f7aa005c   Benoit Cousson   ARM: OMAP2+: pm: ...
137
  					 const char *oh_name)
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
138
139
140
141
  {
  	struct voltagedomain *voltdm;
  	struct clk *clk;
  	struct opp *opp;
0f7aa005c   Benoit Cousson   ARM: OMAP2+: pm: ...
142
  	struct device *dev;
1c1cb2057   Nishanth Menon   OMAP2+: PM: init_...
143
144
  	unsigned long freq_cur, freq_valid, bootup_volt;
  	int ret = -EINVAL;
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
145

0f7aa005c   Benoit Cousson   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   Kevin Hilman   OMAP2+: voltage: ...
153
  	voltdm = voltdm_lookup(vdd_name);
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
154
  	if (IS_ERR(voltdm)) {
e9a5190aa   Johan Hovold   OMAP2+: PM: clean...
155
156
  		pr_err("%s: unable to get vdd pointer for vdd_%s
  ",
1482d8be5   Thara Gopinath   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   Johan Hovold   OMAP2+: PM: clean...
163
164
  		pr_err("%s: unable to get clk %s
  ", __func__, clk_name);
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
165
166
  		goto exit;
  	}
1c1cb2057   Nishanth Menon   OMAP2+: PM: init_...
167
168
  	freq_cur = clk->rate;
  	freq_valid = freq_cur;
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
169

b2274ddfd   Nishanth Menon   OMAP2+: PM: secur...
170
  	rcu_read_lock();
1c1cb2057   Nishanth Menon   OMAP2+: PM: init_...
171
  	opp = opp_find_freq_ceil(dev, &freq_valid);
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
172
  	if (IS_ERR(opp)) {
1c1cb2057   Nishanth Menon   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   Thara Gopinath   OMAP3: PM: Progra...
182
183
184
  	}
  
  	bootup_volt = opp_get_voltage(opp);
b2274ddfd   Nishanth Menon   OMAP2+: PM: secur...
185
  	rcu_read_unlock();
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
186
  	if (!bootup_volt) {
e9a5190aa   Johan Hovold   OMAP2+: PM: clean...
187
  		pr_err("%s: unable to find voltage corresponding "
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
188
189
  			"to the bootup OPP for vdd_%s
  ", __func__, vdd_name);
1c1cb2057   Nishanth Menon   OMAP2+: PM: init_...
190
191
  		ret = -ENOENT;
  		goto exit_ck;
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
192
  	}
1c1cb2057   Nishanth Menon   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   Thara Gopinath   OMAP3: PM: Progra...
243
244
  
  exit:
e9a5190aa   Johan Hovold   OMAP2+: PM: clean...
245
246
  	pr_err("%s: unable to set vdd_%s
  ", __func__, vdd_name);
1482d8be5   Thara Gopinath   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   Vaibhav Bedia   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   Thara Gopinath   OMAP3: PM: Progra...
260
  }
1376ee1d1   Thara Gopinath   OMAP4: PM: Progra...
261
262
263
264
  static void __init omap4_init_voltages(void)
  {
  	if (!cpu_is_omap44xx())
  		return;
0f7aa005c   Benoit Cousson   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   Thara Gopinath   OMAP4: PM: Progra...
268
  }
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
269
270
  static int __init omap2_common_pm_init(void)
  {
476b679a5   Benoit Cousson   arm/dts: OMAP3+: ...
271
272
  	if (!of_have_populated_dt())
  		omap2_init_processor_devices();
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
273
274
275
276
  	omap_pm_if_init();
  
  	return 0;
  }
1cbbe37ac   Thara Gopinath   OMAP: pm.c correc...
277
  postcore_initcall(omap2_common_pm_init);
6f88e9bc2   Kevin Hilman   OMAP: PM: create ...
278

2f34ce81b   Thara Gopinath   OMAP3: PM: Adding...
279
280
  static int __init omap2_common_pm_late_init(void)
  {
fbc319f67   Thara Gopinath   OMAP3: PM: Regist...
281
282
  	/* Init the OMAP TWL parameters */
  	omap3_twl_init();
7bc3ed9ae   Thara Gopinath   OMAP4: Register v...
283
  	omap4_twl_init();
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
284

fbc319f67   Thara Gopinath   OMAP3: PM: Regist...
285
  	/* Init the voltage layer */
2f34ce81b   Thara Gopinath   OMAP3: PM: Adding...
286
  	omap_voltage_late_init();
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
287
288
289
  
  	/* Initialize the voltages */
  	omap3_init_voltages();
1376ee1d1   Thara Gopinath   OMAP4: PM: Progra...
290
  	omap4_init_voltages();
1482d8be5   Thara Gopinath   OMAP3: PM: Progra...
291

fbc319f67   Thara Gopinath   OMAP3: PM: Regist...
292
  	/* Smartreflex device init */
0c0a5d61e   Thara Gopinath   OMAP3: PM: Adding...
293
  	omap_devinit_smartreflex();
2f34ce81b   Thara Gopinath   OMAP3: PM: Adding...
294

54d8f4c29   Nishanth Menon   OMAP2+: Ensure th...
295
296
297
  	omap_pm_is_ready_status = true;
  	/* let the other CPU know as well */
  	smp_wmb();
2f34ce81b   Thara Gopinath   OMAP3: PM: Adding...
298
299
300
  	return 0;
  }
  late_initcall(omap2_common_pm_late_init);