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