blob: e8e8d8872a0e7244c3029eadf3933dc7bd7b53d9 [file] [log] [blame]
Paul Walmsleyad67ef62008-08-19 11:08:40 +03001/*
2 * OMAP powerdomain control
3 *
4 * Copyright (C) 2007-2008 Texas Instruments, Inc.
Paul Walmsley55ed9692010-01-26 20:12:59 -07005 * Copyright (C) 2007-2009 Nokia Corporation
Paul Walmsleyad67ef62008-08-19 11:08:40 +03006 *
7 * Written by Paul Walmsley
8 *
Abhijit Pagare3a759f02010-01-26 20:12:53 -07009 * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
10 *
Paul Walmsleyad67ef62008-08-19 11:08:40 +030011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
Paul Walmsley33903eb2009-12-08 16:33:10 -070015#undef DEBUG
Paul Walmsleyad67ef62008-08-19 11:08:40 +030016
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/types.h>
20#include <linux/delay.h>
21#include <linux/spinlock.h>
22#include <linux/list.h>
23#include <linux/errno.h>
24#include <linux/err.h>
25#include <linux/io.h>
26
27#include <asm/atomic.h>
28
29#include "cm.h"
30#include "cm-regbits-34xx.h"
Abhijit Pagare3a759f02010-01-26 20:12:53 -070031#include "cm-regbits-44xx.h"
Paul Walmsleyad67ef62008-08-19 11:08:40 +030032#include "prm.h"
33#include "prm-regbits-34xx.h"
Abhijit Pagare3a759f02010-01-26 20:12:53 -070034#include "prm-regbits-44xx.h"
Paul Walmsleyad67ef62008-08-19 11:08:40 +030035
Tony Lindgrence491cf2009-10-20 09:40:47 -070036#include <plat/cpu.h>
37#include <plat/powerdomain.h>
38#include <plat/clockdomain.h>
Paul Walmsley55ed9692010-01-26 20:12:59 -070039#include <plat/prcm.h>
Paul Walmsleyad67ef62008-08-19 11:08:40 +030040
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +030041#include "pm.h"
42
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +030043enum {
44 PWRDM_STATE_NOW = 0,
45 PWRDM_STATE_PREV,
46};
47
Abhijit Pagare3a759f02010-01-26 20:12:53 -070048/* Variable holding value of the CPU dependent PWRSTCTRL Register Offset */
49static u16 pwrstctrl_reg_offs;
50
51/* Variable holding value of the CPU dependent PWRSTST Register Offset */
52static u16 pwrstst_reg_offs;
53
54/* OMAP3 and OMAP4 specific register bit initialisations
55 * Notice that the names here are not according to each power
56 * domain but the bit mapping used applies to all of them
57 */
58
59/* OMAP3 and OMAP4 Memory Onstate Masks (common across all power domains) */
60#define OMAP_MEM0_ONSTATE_MASK OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK
61#define OMAP_MEM1_ONSTATE_MASK OMAP3430_L1FLATMEMONSTATE_MASK
62#define OMAP_MEM2_ONSTATE_MASK OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK
63#define OMAP_MEM3_ONSTATE_MASK OMAP3430_L2FLATMEMONSTATE_MASK
64#define OMAP_MEM4_ONSTATE_MASK OMAP4430_OCP_NRET_BANK_ONSTATE_MASK
65
66/* OMAP3 and OMAP4 Memory Retstate Masks (common across all power domains) */
67#define OMAP_MEM0_RETSTATE_MASK OMAP3430_SHAREDL1CACHEFLATRETSTATE
68#define OMAP_MEM1_RETSTATE_MASK OMAP3430_L1FLATMEMRETSTATE
69#define OMAP_MEM2_RETSTATE_MASK OMAP3430_SHAREDL2CACHEFLATRETSTATE
70#define OMAP_MEM3_RETSTATE_MASK OMAP3430_L2FLATMEMRETSTATE
71#define OMAP_MEM4_RETSTATE_MASK OMAP4430_OCP_NRET_BANK_RETSTATE_MASK
72
73/* OMAP3 and OMAP4 Memory Status bits */
74#define OMAP_MEM0_STATEST_MASK OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK
75#define OMAP_MEM1_STATEST_MASK OMAP3430_L1FLATMEMSTATEST_MASK
76#define OMAP_MEM2_STATEST_MASK OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK
77#define OMAP_MEM3_STATEST_MASK OMAP3430_L2FLATMEMSTATEST_MASK
78#define OMAP_MEM4_STATEST_MASK OMAP4430_OCP_NRET_BANK_STATEST_MASK
79
Paul Walmsleyad67ef62008-08-19 11:08:40 +030080/* pwrdm_list contains all registered struct powerdomains */
81static LIST_HEAD(pwrdm_list);
82
83/*
84 * pwrdm_rwlock protects pwrdm_list add and del ops - also reused to
85 * protect pwrdm_clkdms[] during clkdm add/del ops
86 */
87static DEFINE_RWLOCK(pwrdm_rwlock);
88
89
90/* Private functions */
91
Paul Walmsleyad67ef62008-08-19 11:08:40 +030092static struct powerdomain *_pwrdm_lookup(const char *name)
93{
94 struct powerdomain *pwrdm, *temp_pwrdm;
95
96 pwrdm = NULL;
97
98 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
99 if (!strcmp(name, temp_pwrdm->name)) {
100 pwrdm = temp_pwrdm;
101 break;
102 }
103 }
104
105 return pwrdm;
106}
107
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300108static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
109{
110
111 int prev;
112 int state;
113
114 if (pwrdm == NULL)
115 return -EINVAL;
116
117 state = pwrdm_read_pwrst(pwrdm);
118
119 switch (flag) {
120 case PWRDM_STATE_NOW:
121 prev = pwrdm->state;
122 break;
123 case PWRDM_STATE_PREV:
124 prev = pwrdm_read_prev_pwrst(pwrdm);
125 if (pwrdm->state != prev)
126 pwrdm->state_counter[prev]++;
127 break;
128 default:
129 return -EINVAL;
130 }
131
132 if (state != prev)
133 pwrdm->state_counter[state]++;
134
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300135 pm_dbg_update_time(pwrdm, prev);
136
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300137 pwrdm->state = state;
138
139 return 0;
140}
141
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300142static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300143{
144 pwrdm_clear_all_prev_pwrst(pwrdm);
145 _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
146 return 0;
147}
148
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300149static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300150{
151 _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
152 return 0;
153}
154
155static __init void _pwrdm_setup(struct powerdomain *pwrdm)
156{
157 int i;
158
Paul Walmsley2354eb52009-12-08 16:33:12 -0700159 for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300160 pwrdm->state_counter[i] = 0;
161
162 pwrdm_wait_transition(pwrdm);
163 pwrdm->state = pwrdm_read_pwrst(pwrdm);
164 pwrdm->state_counter[pwrdm->state] = 1;
165
166}
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300167
168/* Public functions */
169
170/**
171 * pwrdm_init - set up the powerdomain layer
172 *
173 * Loop through the list of powerdomains, registering all that are
174 * available on the current CPU. If pwrdm_list is supplied and not
175 * null, all of the referenced powerdomains will be registered. No
176 * return value.
177 */
178void pwrdm_init(struct powerdomain **pwrdm_list)
179{
180 struct powerdomain **p = NULL;
181
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700182 if (cpu_is_omap24xx() | cpu_is_omap34xx()) {
183 pwrstctrl_reg_offs = OMAP2_PM_PWSTCTRL;
184 pwrstst_reg_offs = OMAP2_PM_PWSTST;
185 } else if (cpu_is_omap44xx()) {
186 pwrstctrl_reg_offs = OMAP4_PM_PWSTCTRL;
187 pwrstst_reg_offs = OMAP4_PM_PWSTST;
188 } else {
189 printk(KERN_ERR "Power Domain struct not supported for " \
190 "this CPU\n");
191 return;
192 }
193
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300194 if (pwrdm_list) {
195 for (p = pwrdm_list; *p; p++) {
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300196 pwrdm_register(*p);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300197 _pwrdm_setup(*p);
198 }
199 }
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300200}
201
202/**
203 * pwrdm_register - register a powerdomain
204 * @pwrdm: struct powerdomain * to register
205 *
206 * Adds a powerdomain to the internal powerdomain list. Returns
207 * -EINVAL if given a null pointer, -EEXIST if a powerdomain is
208 * already registered by the provided name, or 0 upon success.
209 */
210int pwrdm_register(struct powerdomain *pwrdm)
211{
212 unsigned long flags;
213 int ret = -EINVAL;
214
215 if (!pwrdm)
216 return -EINVAL;
217
218 if (!omap_chip_is(pwrdm->omap_chip))
219 return -EINVAL;
220
221 write_lock_irqsave(&pwrdm_rwlock, flags);
222 if (_pwrdm_lookup(pwrdm->name)) {
223 ret = -EEXIST;
224 goto pr_unlock;
225 }
226
227 list_add(&pwrdm->node, &pwrdm_list);
228
229 pr_debug("powerdomain: registered %s\n", pwrdm->name);
230 ret = 0;
231
232pr_unlock:
233 write_unlock_irqrestore(&pwrdm_rwlock, flags);
234
235 return ret;
236}
237
238/**
239 * pwrdm_unregister - unregister a powerdomain
240 * @pwrdm: struct powerdomain * to unregister
241 *
242 * Removes a powerdomain from the internal powerdomain list. Returns
243 * -EINVAL if pwrdm argument is NULL.
244 */
245int pwrdm_unregister(struct powerdomain *pwrdm)
246{
247 unsigned long flags;
248
249 if (!pwrdm)
250 return -EINVAL;
251
252 write_lock_irqsave(&pwrdm_rwlock, flags);
253 list_del(&pwrdm->node);
254 write_unlock_irqrestore(&pwrdm_rwlock, flags);
255
256 pr_debug("powerdomain: unregistered %s\n", pwrdm->name);
257
258 return 0;
259}
260
261/**
262 * pwrdm_lookup - look up a powerdomain by name, return a pointer
263 * @name: name of powerdomain
264 *
265 * Find a registered powerdomain by its name. Returns a pointer to the
266 * struct powerdomain if found, or NULL otherwise.
267 */
268struct powerdomain *pwrdm_lookup(const char *name)
269{
270 struct powerdomain *pwrdm;
271 unsigned long flags;
272
273 if (!name)
274 return NULL;
275
276 read_lock_irqsave(&pwrdm_rwlock, flags);
277 pwrdm = _pwrdm_lookup(name);
278 read_unlock_irqrestore(&pwrdm_rwlock, flags);
279
280 return pwrdm;
281}
282
283/**
Artem Bityutskiyee894b12009-10-01 10:01:55 +0300284 * pwrdm_for_each_nolock - call function on each registered clockdomain
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300285 * @fn: callback function *
286 *
287 * Call the supplied function for each registered powerdomain. The
288 * callback function can return anything but 0 to bail out early from
Artem Bityutskiyee894b12009-10-01 10:01:55 +0300289 * the iterator. Returns the last return value of the callback function, which
290 * should be 0 for success or anything else to indicate failure; or -EINVAL if
291 * the function pointer is null.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300292 */
Artem Bityutskiyee894b12009-10-01 10:01:55 +0300293int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
294 void *user)
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300295{
296 struct powerdomain *temp_pwrdm;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300297 int ret = 0;
298
299 if (!fn)
300 return -EINVAL;
301
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300302 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300303 ret = (*fn)(temp_pwrdm, user);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300304 if (ret)
305 break;
306 }
Artem Bityutskiyee894b12009-10-01 10:01:55 +0300307
308 return ret;
309}
310
311/**
312 * pwrdm_for_each - call function on each registered clockdomain
313 * @fn: callback function *
314 *
315 * This function is the same as 'pwrdm_for_each_nolock()', but keeps the
316 * &pwrdm_rwlock locked for reading, so no powerdomain structure manipulation
317 * functions should be called from the callback, although hardware powerdomain
318 * control functions are fine.
319 */
320int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
321 void *user)
322{
323 unsigned long flags;
324 int ret;
325
326 read_lock_irqsave(&pwrdm_rwlock, flags);
327 ret = pwrdm_for_each_nolock(fn, user);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300328 read_unlock_irqrestore(&pwrdm_rwlock, flags);
329
330 return ret;
331}
332
333/**
Paul Walmsley8420bb12008-08-19 11:08:44 +0300334 * pwrdm_add_clkdm - add a clockdomain to a powerdomain
335 * @pwrdm: struct powerdomain * to add the clockdomain to
336 * @clkdm: struct clockdomain * to associate with a powerdomain
337 *
338 * Associate the clockdomain 'clkdm' with a powerdomain 'pwrdm'. This
339 * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if
340 * presented with invalid pointers; -ENOMEM if memory could not be allocated;
341 * or 0 upon success.
342 */
343int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
344{
345 unsigned long flags;
346 int i;
347 int ret = -EINVAL;
348
349 if (!pwrdm || !clkdm)
350 return -EINVAL;
351
352 pr_debug("powerdomain: associating clockdomain %s with powerdomain "
353 "%s\n", clkdm->name, pwrdm->name);
354
355 write_lock_irqsave(&pwrdm_rwlock, flags);
356
357 for (i = 0; i < PWRDM_MAX_CLKDMS; i++) {
358 if (!pwrdm->pwrdm_clkdms[i])
359 break;
360#ifdef DEBUG
361 if (pwrdm->pwrdm_clkdms[i] == clkdm) {
362 ret = -EINVAL;
363 goto pac_exit;
364 }
365#endif
366 }
367
368 if (i == PWRDM_MAX_CLKDMS) {
369 pr_debug("powerdomain: increase PWRDM_MAX_CLKDMS for "
370 "pwrdm %s clkdm %s\n", pwrdm->name, clkdm->name);
371 WARN_ON(1);
372 ret = -ENOMEM;
373 goto pac_exit;
374 }
375
376 pwrdm->pwrdm_clkdms[i] = clkdm;
377
378 ret = 0;
379
380pac_exit:
381 write_unlock_irqrestore(&pwrdm_rwlock, flags);
382
383 return ret;
384}
385
386/**
387 * pwrdm_del_clkdm - remove a clockdomain from a powerdomain
388 * @pwrdm: struct powerdomain * to add the clockdomain to
389 * @clkdm: struct clockdomain * to associate with a powerdomain
390 *
391 * Dissociate the clockdomain 'clkdm' from the powerdomain
392 * 'pwrdm'. Returns -EINVAL if presented with invalid pointers;
393 * -ENOENT if the clkdm was not associated with the powerdomain, or 0
394 * upon success.
395 */
396int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
397{
398 unsigned long flags;
399 int ret = -EINVAL;
400 int i;
401
402 if (!pwrdm || !clkdm)
403 return -EINVAL;
404
405 pr_debug("powerdomain: dissociating clockdomain %s from powerdomain "
406 "%s\n", clkdm->name, pwrdm->name);
407
408 write_lock_irqsave(&pwrdm_rwlock, flags);
409
410 for (i = 0; i < PWRDM_MAX_CLKDMS; i++)
411 if (pwrdm->pwrdm_clkdms[i] == clkdm)
412 break;
413
414 if (i == PWRDM_MAX_CLKDMS) {
415 pr_debug("powerdomain: clkdm %s not associated with pwrdm "
416 "%s ?!\n", clkdm->name, pwrdm->name);
417 ret = -ENOENT;
418 goto pdc_exit;
419 }
420
421 pwrdm->pwrdm_clkdms[i] = NULL;
422
423 ret = 0;
424
425pdc_exit:
426 write_unlock_irqrestore(&pwrdm_rwlock, flags);
427
428 return ret;
429}
430
431/**
432 * pwrdm_for_each_clkdm - call function on each clkdm in a pwrdm
433 * @pwrdm: struct powerdomain * to iterate over
434 * @fn: callback function *
435 *
436 * Call the supplied function for each clockdomain in the powerdomain
437 * 'pwrdm'. The callback function can return anything but 0 to bail
438 * out early from the iterator. The callback function is called with
439 * the pwrdm_rwlock held for reading, so no powerdomain structure
440 * manipulation functions should be called from the callback, although
441 * hardware powerdomain control functions are fine. Returns -EINVAL
442 * if presented with invalid pointers; or passes along the last return
443 * value of the callback function, which should be 0 for success or
444 * anything else to indicate failure.
445 */
446int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
447 int (*fn)(struct powerdomain *pwrdm,
448 struct clockdomain *clkdm))
449{
450 unsigned long flags;
451 int ret = 0;
452 int i;
453
454 if (!fn)
455 return -EINVAL;
456
457 read_lock_irqsave(&pwrdm_rwlock, flags);
458
459 for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++)
460 ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
461
462 read_unlock_irqrestore(&pwrdm_rwlock, flags);
463
464 return ret;
465}
466
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300467/**
468 * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
469 * @pwrdm: struct powerdomain *
470 *
471 * Return the number of controllable memory banks in powerdomain pwrdm,
472 * starting with 1. Returns -EINVAL if the powerdomain pointer is null.
473 */
474int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm)
475{
476 if (!pwrdm)
477 return -EINVAL;
478
479 return pwrdm->banks;
480}
481
482/**
483 * pwrdm_set_next_pwrst - set next powerdomain power state
484 * @pwrdm: struct powerdomain * to set
485 * @pwrst: one of the PWRDM_POWER_* macros
486 *
487 * Set the powerdomain pwrdm's next power state to pwrst. The powerdomain
488 * may not enter this state immediately if the preconditions for this state
489 * have not been satisfied. Returns -EINVAL if the powerdomain pointer is
490 * null or if the power state is invalid for the powerdomin, or returns 0
491 * upon success.
492 */
493int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
494{
495 if (!pwrdm)
496 return -EINVAL;
497
498 if (!(pwrdm->pwrsts & (1 << pwrst)))
499 return -EINVAL;
500
501 pr_debug("powerdomain: setting next powerstate for %s to %0x\n",
502 pwrdm->name, pwrst);
503
504 prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK,
505 (pwrst << OMAP_POWERSTATE_SHIFT),
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700506 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300507
508 return 0;
509}
510
511/**
512 * pwrdm_read_next_pwrst - get next powerdomain power state
513 * @pwrdm: struct powerdomain * to get power state
514 *
515 * Return the powerdomain pwrdm's next power state. Returns -EINVAL
516 * if the powerdomain pointer is null or returns the next power state
517 * upon success.
518 */
519int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
520{
521 if (!pwrdm)
522 return -EINVAL;
523
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700524 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
525 pwrstctrl_reg_offs, OMAP_POWERSTATE_MASK);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300526}
527
528/**
529 * pwrdm_read_pwrst - get current powerdomain power state
530 * @pwrdm: struct powerdomain * to get power state
531 *
532 * Return the powerdomain pwrdm's current power state. Returns -EINVAL
533 * if the powerdomain pointer is null or returns the current power state
534 * upon success.
535 */
536int pwrdm_read_pwrst(struct powerdomain *pwrdm)
537{
538 if (!pwrdm)
539 return -EINVAL;
540
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700541 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
542 pwrstst_reg_offs, OMAP_POWERSTATEST_MASK);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300543}
544
545/**
546 * pwrdm_read_prev_pwrst - get previous powerdomain power state
547 * @pwrdm: struct powerdomain * to get previous power state
548 *
549 * Return the powerdomain pwrdm's previous power state. Returns -EINVAL
550 * if the powerdomain pointer is null or returns the previous power state
551 * upon success.
552 */
553int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
554{
555 if (!pwrdm)
556 return -EINVAL;
557
558 return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
559 OMAP3430_LASTPOWERSTATEENTERED_MASK);
560}
561
562/**
563 * pwrdm_set_logic_retst - set powerdomain logic power state upon retention
564 * @pwrdm: struct powerdomain * to set
565 * @pwrst: one of the PWRDM_POWER_* macros
566 *
567 * Set the next power state that the logic portion of the powerdomain
568 * pwrdm will enter when the powerdomain enters retention. This will
569 * be either RETENTION or OFF, if supported. Returns -EINVAL if the
570 * powerdomain pointer is null or the target power state is not not
571 * supported, or returns 0 upon success.
572 */
573int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
574{
575 if (!pwrdm)
576 return -EINVAL;
577
578 if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst)))
579 return -EINVAL;
580
581 pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n",
582 pwrdm->name, pwrst);
583
584 /*
585 * The register bit names below may not correspond to the
586 * actual names of the bits in each powerdomain's register,
587 * but the type of value returned is the same for each
588 * powerdomain.
589 */
590 prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE,
591 (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)),
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700592 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300593
594 return 0;
595}
596
597/**
598 * pwrdm_set_mem_onst - set memory power state while powerdomain ON
599 * @pwrdm: struct powerdomain * to set
600 * @bank: memory bank number to set (0-3)
601 * @pwrst: one of the PWRDM_POWER_* macros
602 *
603 * Set the next power state that memory bank x of the powerdomain
604 * pwrdm will enter when the powerdomain enters the ON state. Bank
605 * will be a number from 0 to 3, and represents different types of
606 * memory, depending on the powerdomain. Returns -EINVAL if the
607 * powerdomain pointer is null or the target power state is not not
608 * supported for this memory bank, -EEXIST if the target memory bank
609 * does not exist or is not controllable, or returns 0 upon success.
610 */
611int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
612{
613 u32 m;
614
615 if (!pwrdm)
616 return -EINVAL;
617
618 if (pwrdm->banks < (bank + 1))
619 return -EEXIST;
620
621 if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
622 return -EINVAL;
623
624 pr_debug("powerdomain: setting next memory powerstate for domain %s "
625 "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst);
626
627 /*
628 * The register bit names below may not correspond to the
629 * actual names of the bits in each powerdomain's register,
630 * but the type of value returned is the same for each
631 * powerdomain.
632 */
633 switch (bank) {
634 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700635 m = OMAP_MEM0_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300636 break;
637 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700638 m = OMAP_MEM1_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300639 break;
640 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700641 m = OMAP_MEM2_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300642 break;
643 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700644 m = OMAP_MEM3_ONSTATE_MASK;
645 break;
646 case 4:
647 m = OMAP_MEM4_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300648 break;
649 default:
650 WARN_ON(1); /* should never happen */
651 return -EEXIST;
652 }
653
654 prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)),
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700655 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300656
657 return 0;
658}
659
660/**
661 * pwrdm_set_mem_retst - set memory power state while powerdomain in RET
662 * @pwrdm: struct powerdomain * to set
663 * @bank: memory bank number to set (0-3)
664 * @pwrst: one of the PWRDM_POWER_* macros
665 *
666 * Set the next power state that memory bank x of the powerdomain
667 * pwrdm will enter when the powerdomain enters the RETENTION state.
668 * Bank will be a number from 0 to 3, and represents different types
669 * of memory, depending on the powerdomain. pwrst will be either
670 * RETENTION or OFF, if supported. Returns -EINVAL if the powerdomain
671 * pointer is null or the target power state is not not supported for
672 * this memory bank, -EEXIST if the target memory bank does not exist
673 * or is not controllable, or returns 0 upon success.
674 */
675int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
676{
677 u32 m;
678
679 if (!pwrdm)
680 return -EINVAL;
681
682 if (pwrdm->banks < (bank + 1))
683 return -EEXIST;
684
685 if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
686 return -EINVAL;
687
688 pr_debug("powerdomain: setting next memory powerstate for domain %s "
689 "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst);
690
691 /*
692 * The register bit names below may not correspond to the
693 * actual names of the bits in each powerdomain's register,
694 * but the type of value returned is the same for each
695 * powerdomain.
696 */
697 switch (bank) {
698 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700699 m = OMAP_MEM0_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300700 break;
701 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700702 m = OMAP_MEM1_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300703 break;
704 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700705 m = OMAP_MEM2_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300706 break;
707 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700708 m = OMAP_MEM3_RETSTATE_MASK;
709 break;
710 case 4:
711 m = OMAP_MEM4_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300712 break;
713 default:
714 WARN_ON(1); /* should never happen */
715 return -EEXIST;
716 }
717
718 prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700719 pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300720
721 return 0;
722}
723
724/**
725 * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state
726 * @pwrdm: struct powerdomain * to get current logic retention power state
727 *
728 * Return the current power state that the logic portion of
729 * powerdomain pwrdm will enter
730 * Returns -EINVAL if the powerdomain pointer is null or returns the
731 * current logic retention power state upon success.
732 */
733int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
734{
735 if (!pwrdm)
736 return -EINVAL;
737
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700738 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
739 pwrstst_reg_offs, OMAP3430_LOGICSTATEST);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300740}
741
742/**
743 * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state
744 * @pwrdm: struct powerdomain * to get previous logic power state
745 *
746 * Return the powerdomain pwrdm's logic power state. Returns -EINVAL
747 * if the powerdomain pointer is null or returns the previous logic
748 * power state upon success.
749 */
750int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm)
751{
752 if (!pwrdm)
753 return -EINVAL;
754
755 /*
756 * The register bit names below may not correspond to the
757 * actual names of the bits in each powerdomain's register,
758 * but the type of value returned is the same for each
759 * powerdomain.
760 */
761 return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
762 OMAP3430_LASTLOGICSTATEENTERED);
763}
764
765/**
766 * pwrdm_read_mem_pwrst - get current memory bank power state
767 * @pwrdm: struct powerdomain * to get current memory bank power state
768 * @bank: memory bank number (0-3)
769 *
770 * Return the powerdomain pwrdm's current memory power state for bank
771 * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
772 * the target memory bank does not exist or is not controllable, or
773 * returns the current memory power state upon success.
774 */
775int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
776{
777 u32 m;
778
779 if (!pwrdm)
780 return -EINVAL;
781
782 if (pwrdm->banks < (bank + 1))
783 return -EEXIST;
784
Thara Gopinath3863c742009-12-08 16:33:15 -0700785 if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
786 bank = 1;
787
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300788 /*
789 * The register bit names below may not correspond to the
790 * actual names of the bits in each powerdomain's register,
791 * but the type of value returned is the same for each
792 * powerdomain.
793 */
794 switch (bank) {
795 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700796 m = OMAP_MEM0_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300797 break;
798 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700799 m = OMAP_MEM1_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300800 break;
801 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700802 m = OMAP_MEM2_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300803 break;
804 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700805 m = OMAP_MEM3_STATEST_MASK;
806 break;
807 case 4:
808 m = OMAP_MEM4_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300809 break;
810 default:
811 WARN_ON(1); /* should never happen */
812 return -EEXIST;
813 }
814
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700815 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
816 pwrstst_reg_offs, m);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300817}
818
819/**
820 * pwrdm_read_prev_mem_pwrst - get previous memory bank power state
821 * @pwrdm: struct powerdomain * to get previous memory bank power state
822 * @bank: memory bank number (0-3)
823 *
824 * Return the powerdomain pwrdm's previous memory power state for bank
825 * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
826 * the target memory bank does not exist or is not controllable, or
827 * returns the previous memory power state upon success.
828 */
829int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
830{
831 u32 m;
832
833 if (!pwrdm)
834 return -EINVAL;
835
836 if (pwrdm->banks < (bank + 1))
837 return -EEXIST;
838
Thara Gopinath3863c742009-12-08 16:33:15 -0700839 if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
840 bank = 1;
841
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300842 /*
843 * The register bit names below may not correspond to the
844 * actual names of the bits in each powerdomain's register,
845 * but the type of value returned is the same for each
846 * powerdomain.
847 */
848 switch (bank) {
849 case 0:
850 m = OMAP3430_LASTMEM1STATEENTERED_MASK;
851 break;
852 case 1:
853 m = OMAP3430_LASTMEM2STATEENTERED_MASK;
854 break;
855 case 2:
856 m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK;
857 break;
858 case 3:
859 m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK;
860 break;
861 default:
862 WARN_ON(1); /* should never happen */
863 return -EEXIST;
864 }
865
866 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
867 OMAP3430_PM_PREPWSTST, m);
868}
869
870/**
871 * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
872 * @pwrdm: struct powerdomain * to clear
873 *
874 * Clear the powerdomain's previous power state register. Clears the
875 * entire register, including logic and memory bank previous power states.
876 * Returns -EINVAL if the powerdomain pointer is null, or returns 0 upon
877 * success.
878 */
879int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
880{
881 if (!pwrdm)
882 return -EINVAL;
883
884 /*
885 * XXX should get the powerdomain's current state here;
886 * warn & fail if it is not ON.
887 */
888
889 pr_debug("powerdomain: clearing previous power state reg for %s\n",
890 pwrdm->name);
891
892 prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST);
893
894 return 0;
895}
896
897/**
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600898 * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm
899 * @pwrdm: struct powerdomain *
900 *
901 * Enable automatic context save-and-restore upon power state change
902 * for some devices in a powerdomain. Warning: this only affects a
903 * subset of devices in a powerdomain; check the TRM closely. Returns
904 * -EINVAL if the powerdomain pointer is null or if the powerdomain
905 * does not support automatic save-and-restore, or returns 0 upon
906 * success.
907 */
908int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
909{
910 if (!pwrdm)
911 return -EINVAL;
912
913 if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
914 return -EINVAL;
915
916 pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n",
917 pwrdm->name);
918
919 prm_rmw_mod_reg_bits(0, 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700920 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600921
922 return 0;
923}
924
925/**
926 * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm
927 * @pwrdm: struct powerdomain *
928 *
929 * Disable automatic context save-and-restore upon power state change
930 * for some devices in a powerdomain. Warning: this only affects a
931 * subset of devices in a powerdomain; check the TRM closely. Returns
932 * -EINVAL if the powerdomain pointer is null or if the powerdomain
933 * does not support automatic save-and-restore, or returns 0 upon
934 * success.
935 */
936int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm)
937{
938 if (!pwrdm)
939 return -EINVAL;
940
941 if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
942 return -EINVAL;
943
944 pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n",
945 pwrdm->name);
946
947 prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, 0,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700948 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600949
950 return 0;
951}
952
953/**
954 * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR
955 * @pwrdm: struct powerdomain *
956 *
957 * Returns 1 if powerdomain 'pwrdm' supports hardware save-and-restore
958 * for some devices, or 0 if it does not.
959 */
960bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
961{
962 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
963}
964
965/**
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300966 * pwrdm_wait_transition - wait for powerdomain power transition to finish
967 * @pwrdm: struct powerdomain * to wait for
968 *
969 * If the powerdomain pwrdm is in the process of a state transition,
970 * spin until it completes the power transition, or until an iteration
971 * bailout value is reached. Returns -EINVAL if the powerdomain
972 * pointer is null, -EAGAIN if the bailout value was reached, or
973 * returns 0 upon success.
974 */
975int pwrdm_wait_transition(struct powerdomain *pwrdm)
976{
977 u32 c = 0;
978
979 if (!pwrdm)
980 return -EINVAL;
981
982 /*
983 * REVISIT: pwrdm_wait_transition() may be better implemented
984 * via a callback and a periodic timer check -- how long do we expect
985 * powerdomain transitions to take?
986 */
987
988 /* XXX Is this udelay() value meaningful? */
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700989 while ((prm_read_mod_reg(pwrdm->prcm_offs, pwrstst_reg_offs) &
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300990 OMAP_INTRANSITION) &&
991 (c++ < PWRDM_TRANSITION_BAILOUT))
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700992 udelay(1);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300993
Roel Kluin26870692009-06-19 19:08:30 -0600994 if (c > PWRDM_TRANSITION_BAILOUT) {
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300995 printk(KERN_ERR "powerdomain: waited too long for "
996 "powerdomain %s to complete transition\n", pwrdm->name);
997 return -EAGAIN;
998 }
999
1000 pr_debug("powerdomain: completed transition in %d loops\n", c);
1001
1002 return 0;
1003}
1004
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +03001005int pwrdm_state_switch(struct powerdomain *pwrdm)
1006{
1007 return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
1008}
1009
1010int pwrdm_clkdm_state_switch(struct clockdomain *clkdm)
1011{
1012 if (clkdm != NULL && clkdm->pwrdm.ptr != NULL) {
1013 pwrdm_wait_transition(clkdm->pwrdm.ptr);
1014 return pwrdm_state_switch(clkdm->pwrdm.ptr);
1015 }
1016
1017 return -EINVAL;
1018}
1019int pwrdm_clk_state_switch(struct clk *clk)
1020{
1021 if (clk != NULL && clk->clkdm != NULL)
1022 return pwrdm_clkdm_state_switch(clk->clkdm);
1023 return -EINVAL;
1024}
1025
1026int pwrdm_pre_transition(void)
1027{
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +03001028 pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +03001029 return 0;
1030}
1031
1032int pwrdm_post_transition(void)
1033{
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +03001034 pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +03001035 return 0;
1036}
Paul Walmsleyad67ef62008-08-19 11:08:40 +03001037