Commit | Line | Data |
---|---|---|
c0407a96 PW |
1 | /* |
2 | * omap-pm-noop.c - OMAP power management interface - dummy version | |
3 | * | |
4 | * This code implements the OMAP power management interface to | |
5 | * drivers, CPUIdle, CPUFreq, and DSP Bridge. It is strictly for | |
6 | * debug/demonstration use, as it does nothing but printk() whenever a | |
7 | * function is called (when DEBUG is defined, below) | |
8 | * | |
9 | * Copyright (C) 2008-2009 Texas Instruments, Inc. | |
10 | * Copyright (C) 2008-2009 Nokia Corporation | |
11 | * Paul Walmsley | |
12 | * | |
13 | * Interface developed by (in alphabetical order): | |
14 | * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan | |
15 | * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff | |
16 | */ | |
17 | ||
18 | #undef DEBUG | |
19 | ||
20 | #include <linux/init.h> | |
21 | #include <linux/cpufreq.h> | |
22 | #include <linux/device.h> | |
c80705aa | 23 | #include <linux/platform_device.h> |
c0407a96 PW |
24 | |
25 | /* Interface documentation is in mach/omap-pm.h */ | |
ce491cf8 | 26 | #include <plat/omap-pm.h> |
c80705aa | 27 | #include <plat/omap_device.h> |
c0407a96 | 28 | |
6081dc34 | 29 | static bool off_mode_enabled; |
fc013873 | 30 | static int dummy_context_loss_counter; |
6081dc34 | 31 | |
c0407a96 PW |
32 | /* |
33 | * Device-driver-originated constraints (via board-*.c files) | |
34 | */ | |
35 | ||
564889c1 | 36 | int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t) |
c0407a96 PW |
37 | { |
38 | if (!dev || t < -1) { | |
564889c1 PW |
39 | WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); |
40 | return -EINVAL; | |
c0407a96 PW |
41 | }; |
42 | ||
43 | if (t == -1) | |
7852ec05 PW |
44 | pr_debug("OMAP PM: remove max MPU wakeup latency constraint: dev %s\n", |
45 | dev_name(dev)); | |
c0407a96 | 46 | else |
7852ec05 PW |
47 | pr_debug("OMAP PM: add max MPU wakeup latency constraint: dev %s, t = %ld usec\n", |
48 | dev_name(dev), t); | |
c0407a96 PW |
49 | |
50 | /* | |
51 | * For current Linux, this needs to map the MPU to a | |
52 | * powerdomain, then go through the list of current max lat | |
53 | * constraints on the MPU and find the smallest. If | |
54 | * the latency constraint has changed, the code should | |
55 | * recompute the state to enter for the next powerdomain | |
56 | * state. | |
57 | * | |
58 | * TI CDP code can call constraint_set here. | |
59 | */ | |
564889c1 PW |
60 | |
61 | return 0; | |
c0407a96 PW |
62 | } |
63 | ||
564889c1 | 64 | int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r) |
c0407a96 PW |
65 | { |
66 | if (!dev || (agent_id != OCP_INITIATOR_AGENT && | |
67 | agent_id != OCP_TARGET_AGENT)) { | |
564889c1 PW |
68 | WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); |
69 | return -EINVAL; | |
c0407a96 PW |
70 | }; |
71 | ||
72 | if (r == 0) | |
7852ec05 PW |
73 | pr_debug("OMAP PM: remove min bus tput constraint: dev %s for agent_id %d\n", |
74 | dev_name(dev), agent_id); | |
c0407a96 | 75 | else |
7852ec05 | 76 | pr_debug("OMAP PM: add min bus tput constraint: dev %s for agent_id %d: rate %ld KiB\n", |
c0407a96 PW |
77 | dev_name(dev), agent_id, r); |
78 | ||
79 | /* | |
80 | * This code should model the interconnect and compute the | |
81 | * required clock frequency, convert that to a VDD2 OPP ID, then | |
82 | * set the VDD2 OPP appropriately. | |
83 | * | |
84 | * TI CDP code can call constraint_set here on the VDD2 OPP. | |
85 | */ | |
564889c1 PW |
86 | |
87 | return 0; | |
c0407a96 PW |
88 | } |
89 | ||
564889c1 PW |
90 | int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, |
91 | long t) | |
c0407a96 | 92 | { |
564889c1 PW |
93 | if (!req_dev || !dev || t < -1) { |
94 | WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); | |
95 | return -EINVAL; | |
c0407a96 PW |
96 | }; |
97 | ||
98 | if (t == -1) | |
7852ec05 PW |
99 | pr_debug("OMAP PM: remove max device latency constraint: dev %s\n", |
100 | dev_name(dev)); | |
c0407a96 | 101 | else |
7852ec05 PW |
102 | pr_debug("OMAP PM: add max device latency constraint: dev %s, t = %ld usec\n", |
103 | dev_name(dev), t); | |
c0407a96 PW |
104 | |
105 | /* | |
106 | * For current Linux, this needs to map the device to a | |
107 | * powerdomain, then go through the list of current max lat | |
108 | * constraints on that powerdomain and find the smallest. If | |
109 | * the latency constraint has changed, the code should | |
110 | * recompute the state to enter for the next powerdomain | |
111 | * state. Conceivably, this code should also determine | |
112 | * whether to actually disable the device clocks or not, | |
113 | * depending on how long it takes to re-enable the clocks. | |
114 | * | |
115 | * TI CDP code can call constraint_set here. | |
116 | */ | |
564889c1 PW |
117 | |
118 | return 0; | |
c0407a96 PW |
119 | } |
120 | ||
564889c1 | 121 | int omap_pm_set_max_sdma_lat(struct device *dev, long t) |
c0407a96 PW |
122 | { |
123 | if (!dev || t < -1) { | |
564889c1 PW |
124 | WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); |
125 | return -EINVAL; | |
c0407a96 PW |
126 | }; |
127 | ||
128 | if (t == -1) | |
7852ec05 PW |
129 | pr_debug("OMAP PM: remove max DMA latency constraint: dev %s\n", |
130 | dev_name(dev)); | |
c0407a96 | 131 | else |
7852ec05 PW |
132 | pr_debug("OMAP PM: add max DMA latency constraint: dev %s, t = %ld usec\n", |
133 | dev_name(dev), t); | |
c0407a96 PW |
134 | |
135 | /* | |
136 | * For current Linux PM QOS params, this code should scan the | |
137 | * list of maximum CPU and DMA latencies and select the | |
138 | * smallest, then set cpu_dma_latency pm_qos_param | |
139 | * accordingly. | |
140 | * | |
141 | * For future Linux PM QOS params, with separate CPU and DMA | |
142 | * latency params, this code should just set the dma_latency param. | |
143 | * | |
144 | * TI CDP code can call constraint_set here. | |
145 | */ | |
146 | ||
564889c1 | 147 | return 0; |
c0407a96 PW |
148 | } |
149 | ||
fb8ce14c PW |
150 | int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r) |
151 | { | |
152 | if (!dev || !c || r < 0) { | |
153 | WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); | |
154 | return -EINVAL; | |
155 | } | |
156 | ||
157 | if (r == 0) | |
7852ec05 PW |
158 | pr_debug("OMAP PM: remove min clk rate constraint: dev %s\n", |
159 | dev_name(dev)); | |
fb8ce14c | 160 | else |
7852ec05 PW |
161 | pr_debug("OMAP PM: add min clk rate constraint: dev %s, rate = %ld Hz\n", |
162 | dev_name(dev), r); | |
fb8ce14c PW |
163 | |
164 | /* | |
165 | * Code in a real implementation should keep track of these | |
166 | * constraints on the clock, and determine the highest minimum | |
167 | * clock rate. It should iterate over each OPP and determine | |
168 | * whether the OPP will result in a clock rate that would | |
169 | * satisfy this constraint (and any other PM constraint in effect | |
170 | * at that time). Once it finds the lowest-voltage OPP that | |
171 | * meets those conditions, it should switch to it, or return | |
172 | * an error if the code is not capable of doing so. | |
173 | */ | |
174 | ||
175 | return 0; | |
176 | } | |
c0407a96 PW |
177 | |
178 | /* | |
179 | * DSP Bridge-specific constraints | |
180 | */ | |
181 | ||
182 | const struct omap_opp *omap_pm_dsp_get_opp_table(void) | |
183 | { | |
184 | pr_debug("OMAP PM: DSP request for OPP table\n"); | |
185 | ||
186 | /* | |
187 | * Return DSP frequency table here: The final item in the | |
188 | * array should have .rate = .opp_id = 0. | |
189 | */ | |
190 | ||
191 | return NULL; | |
192 | } | |
193 | ||
194 | void omap_pm_dsp_set_min_opp(u8 opp_id) | |
195 | { | |
196 | if (opp_id == 0) { | |
197 | WARN_ON(1); | |
198 | return; | |
199 | } | |
200 | ||
201 | pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id); | |
202 | ||
203 | /* | |
204 | * | |
205 | * For l-o dev tree, our VDD1 clk is keyed on OPP ID, so we | |
206 | * can just test to see which is higher, the CPU's desired OPP | |
207 | * ID or the DSP's desired OPP ID, and use whichever is | |
208 | * highest. | |
209 | * | |
210 | * In CDP12.14+, the VDD1 OPP custom clock that controls the DSP | |
211 | * rate is keyed on MPU speed, not the OPP ID. So we need to | |
212 | * map the OPP ID to the MPU speed for use with clk_set_rate() | |
213 | * if it is higher than the current OPP clock rate. | |
214 | * | |
215 | */ | |
216 | } | |
217 | ||
218 | ||
219 | u8 omap_pm_dsp_get_opp(void) | |
220 | { | |
221 | pr_debug("OMAP PM: DSP requests current DSP OPP ID\n"); | |
222 | ||
223 | /* | |
224 | * For l-o dev tree, call clk_get_rate() on VDD1 OPP clock | |
225 | * | |
226 | * CDP12.14+: | |
227 | * Call clk_get_rate() on the OPP custom clock, map that to an | |
228 | * OPP ID using the tables defined in board-*.c/chip-*.c files. | |
229 | */ | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | /* | |
235 | * CPUFreq-originated constraint | |
236 | * | |
237 | * In the future, this should be handled by custom OPP clocktype | |
238 | * functions. | |
239 | */ | |
240 | ||
241 | struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void) | |
242 | { | |
243 | pr_debug("OMAP PM: CPUFreq request for frequency table\n"); | |
244 | ||
245 | /* | |
246 | * Return CPUFreq frequency table here: loop over | |
247 | * all VDD1 clkrates, pull out the mpu_ck frequencies, build | |
248 | * table | |
249 | */ | |
250 | ||
251 | return NULL; | |
252 | } | |
253 | ||
254 | void omap_pm_cpu_set_freq(unsigned long f) | |
255 | { | |
256 | if (f == 0) { | |
257 | WARN_ON(1); | |
258 | return; | |
259 | } | |
260 | ||
261 | pr_debug("OMAP PM: CPUFreq requests CPU frequency to be set to %lu\n", | |
262 | f); | |
263 | ||
264 | /* | |
265 | * For l-o dev tree, determine whether MPU freq or DSP OPP id | |
266 | * freq is higher. Find the OPP ID corresponding to the | |
267 | * higher frequency. Call clk_round_rate() and clk_set_rate() | |
268 | * on the OPP custom clock. | |
269 | * | |
270 | * CDP should just be able to set the VDD1 OPP clock rate here. | |
271 | */ | |
272 | } | |
273 | ||
274 | unsigned long omap_pm_cpu_get_freq(void) | |
275 | { | |
276 | pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n"); | |
277 | ||
278 | /* | |
279 | * Call clk_get_rate() on the mpu_ck. | |
280 | */ | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
6081dc34 KH |
285 | /** |
286 | * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled | |
287 | * | |
288 | * Intended for use only by OMAP PM core code to notify this layer | |
289 | * that off mode has been enabled. | |
290 | */ | |
291 | void omap_pm_enable_off_mode(void) | |
292 | { | |
293 | off_mode_enabled = true; | |
294 | } | |
295 | ||
296 | /** | |
297 | * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled | |
298 | * | |
299 | * Intended for use only by OMAP PM core code to notify this layer | |
300 | * that off mode has been disabled. | |
301 | */ | |
302 | void omap_pm_disable_off_mode(void) | |
303 | { | |
304 | off_mode_enabled = false; | |
305 | } | |
306 | ||
c0407a96 PW |
307 | /* |
308 | * Device context loss tracking | |
309 | */ | |
310 | ||
6081dc34 KH |
311 | #ifdef CONFIG_ARCH_OMAP2PLUS |
312 | ||
fc013873 | 313 | int omap_pm_get_dev_context_loss_count(struct device *dev) |
c0407a96 | 314 | { |
c80705aa | 315 | struct platform_device *pdev = to_platform_device(dev); |
fc013873 | 316 | int count; |
c0407a96 | 317 | |
c80705aa | 318 | if (WARN_ON(!dev)) |
fc013873 | 319 | return -ENODEV; |
c0407a96 | 320 | |
3ec2decb | 321 | if (dev->pm_domain == &omap_device_pm_domain) { |
6081dc34 KH |
322 | count = omap_device_get_context_loss_count(pdev); |
323 | } else { | |
324 | WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context loss counter; device %s should be converted to omap_device", | |
325 | dev_name(dev)); | |
fc013873 | 326 | |
6081dc34 | 327 | count = dummy_context_loss_counter; |
fc013873 TV |
328 | |
329 | if (off_mode_enabled) { | |
330 | count++; | |
331 | /* | |
332 | * Context loss count has to be a non-negative value. | |
333 | * Clear the sign bit to get a value range from 0 to | |
334 | * INT_MAX. | |
335 | */ | |
336 | count &= INT_MAX; | |
337 | dummy_context_loss_counter = count; | |
338 | } | |
6081dc34 KH |
339 | } |
340 | ||
c80705aa KH |
341 | pr_debug("OMAP PM: context loss count for dev %s = %d\n", |
342 | dev_name(dev), count); | |
c0407a96 | 343 | |
c80705aa | 344 | return count; |
c0407a96 PW |
345 | } |
346 | ||
6081dc34 KH |
347 | #else |
348 | ||
fc013873 | 349 | int omap_pm_get_dev_context_loss_count(struct device *dev) |
6081dc34 KH |
350 | { |
351 | return dummy_context_loss_counter; | |
352 | } | |
353 | ||
354 | #endif | |
c0407a96 PW |
355 | |
356 | /* Should be called before clk framework init */ | |
53da4ce2 | 357 | int __init omap_pm_if_early_init(void) |
c0407a96 | 358 | { |
c0407a96 PW |
359 | return 0; |
360 | } | |
361 | ||
362 | /* Must be called after clock framework is initialized */ | |
363 | int __init omap_pm_if_init(void) | |
364 | { | |
365 | return 0; | |
366 | } | |
367 | ||
368 | void omap_pm_if_exit(void) | |
369 | { | |
370 | /* Deallocate CPUFreq frequency table here */ | |
371 | } | |
372 |