blob: dc03289d5deaee4b76982b420a0db5e10c6cc0e2 [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
Paul Walmsleyad67ef62008-08-19 11:08:40 +030083/* Private functions */
84
Paul Walmsleyad67ef62008-08-19 11:08:40 +030085static struct powerdomain *_pwrdm_lookup(const char *name)
86{
87 struct powerdomain *pwrdm, *temp_pwrdm;
88
89 pwrdm = NULL;
90
91 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
92 if (!strcmp(name, temp_pwrdm->name)) {
93 pwrdm = temp_pwrdm;
94 break;
95 }
96 }
97
98 return pwrdm;
99}
100
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700101/**
102 * _pwrdm_register - register a powerdomain
103 * @pwrdm: struct powerdomain * to register
104 *
105 * Adds a powerdomain to the internal powerdomain list. Returns
106 * -EINVAL if given a null pointer, -EEXIST if a powerdomain is
107 * already registered by the provided name, or 0 upon success.
108 */
109static int _pwrdm_register(struct powerdomain *pwrdm)
110{
111 int i;
112
113 if (!pwrdm)
114 return -EINVAL;
115
116 if (!omap_chip_is(pwrdm->omap_chip))
117 return -EINVAL;
118
119 if (_pwrdm_lookup(pwrdm->name))
120 return -EEXIST;
121
122 list_add(&pwrdm->node, &pwrdm_list);
123
124 /* Initialize the powerdomain's state counter */
Paul Walmsleycf57aa72010-01-26 20:13:01 -0700125 for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700126 pwrdm->state_counter[i] = 0;
127
128 pwrdm_wait_transition(pwrdm);
129 pwrdm->state = pwrdm_read_pwrst(pwrdm);
130 pwrdm->state_counter[pwrdm->state] = 1;
131
132 pr_debug("powerdomain: registered %s\n", pwrdm->name);
133
134 return 0;
135}
136
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300137static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
138{
139
140 int prev;
141 int state;
142
143 if (pwrdm == NULL)
144 return -EINVAL;
145
146 state = pwrdm_read_pwrst(pwrdm);
147
148 switch (flag) {
149 case PWRDM_STATE_NOW:
150 prev = pwrdm->state;
151 break;
152 case PWRDM_STATE_PREV:
153 prev = pwrdm_read_prev_pwrst(pwrdm);
154 if (pwrdm->state != prev)
155 pwrdm->state_counter[prev]++;
156 break;
157 default:
158 return -EINVAL;
159 }
160
161 if (state != prev)
162 pwrdm->state_counter[state]++;
163
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300164 pm_dbg_update_time(pwrdm, prev);
165
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300166 pwrdm->state = state;
167
168 return 0;
169}
170
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300171static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300172{
173 pwrdm_clear_all_prev_pwrst(pwrdm);
174 _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
175 return 0;
176}
177
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300178static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300179{
180 _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
181 return 0;
182}
183
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300184/* Public functions */
185
186/**
187 * pwrdm_init - set up the powerdomain layer
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700188 * @pwrdm_list: array of struct powerdomain pointers to register
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300189 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700190 * Loop through the array of powerdomains @pwrdm_list, registering all
191 * that are available on the current CPU. If pwrdm_list is supplied
192 * and not null, all of the referenced powerdomains will be
193 * registered. No return value. XXX pwrdm_list is not really a
194 * "list"; it is an array. Rename appropriately.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300195 */
196void pwrdm_init(struct powerdomain **pwrdm_list)
197{
198 struct powerdomain **p = NULL;
199
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700200 if (cpu_is_omap24xx() | cpu_is_omap34xx()) {
201 pwrstctrl_reg_offs = OMAP2_PM_PWSTCTRL;
202 pwrstst_reg_offs = OMAP2_PM_PWSTST;
203 } else if (cpu_is_omap44xx()) {
204 pwrstctrl_reg_offs = OMAP4_PM_PWSTCTRL;
205 pwrstst_reg_offs = OMAP4_PM_PWSTST;
206 } else {
207 printk(KERN_ERR "Power Domain struct not supported for " \
208 "this CPU\n");
209 return;
210 }
211
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300212 if (pwrdm_list) {
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700213 for (p = pwrdm_list; *p; p++)
214 _pwrdm_register(*p);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300215 }
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300216}
217
218/**
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300219 * pwrdm_lookup - look up a powerdomain by name, return a pointer
220 * @name: name of powerdomain
221 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700222 * Find a registered powerdomain by its name @name. Returns a pointer
223 * to the struct powerdomain if found, or NULL otherwise.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300224 */
225struct powerdomain *pwrdm_lookup(const char *name)
226{
227 struct powerdomain *pwrdm;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300228
229 if (!name)
230 return NULL;
231
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300232 pwrdm = _pwrdm_lookup(name);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300233
234 return pwrdm;
235}
236
237/**
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700238 * pwrdm_for_each - call function on each registered clockdomain
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300239 * @fn: callback function *
240 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700241 * Call the supplied function @fn for each registered powerdomain.
242 * The callback function @fn can return anything but 0 to bail out
243 * early from the iterator. Returns the last return value of the
244 * callback function, which should be 0 for success or anything else
245 * to indicate failure; or -EINVAL if the function pointer is null.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300246 */
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700247int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
248 void *user)
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300249{
250 struct powerdomain *temp_pwrdm;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300251 int ret = 0;
252
253 if (!fn)
254 return -EINVAL;
255
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300256 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300257 ret = (*fn)(temp_pwrdm, user);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300258 if (ret)
259 break;
260 }
Artem Bityutskiyee894b12009-10-01 10:01:55 +0300261
262 return ret;
263}
264
265/**
Paul Walmsley8420bb12008-08-19 11:08:44 +0300266 * pwrdm_add_clkdm - add a clockdomain to a powerdomain
267 * @pwrdm: struct powerdomain * to add the clockdomain to
268 * @clkdm: struct clockdomain * to associate with a powerdomain
269 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700270 * Associate the clockdomain @clkdm with a powerdomain @pwrdm. This
Paul Walmsley8420bb12008-08-19 11:08:44 +0300271 * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if
272 * presented with invalid pointers; -ENOMEM if memory could not be allocated;
273 * or 0 upon success.
274 */
275int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
276{
Paul Walmsley8420bb12008-08-19 11:08:44 +0300277 int i;
278 int ret = -EINVAL;
279
280 if (!pwrdm || !clkdm)
281 return -EINVAL;
282
283 pr_debug("powerdomain: associating clockdomain %s with powerdomain "
284 "%s\n", clkdm->name, pwrdm->name);
285
Paul Walmsley8420bb12008-08-19 11:08:44 +0300286 for (i = 0; i < PWRDM_MAX_CLKDMS; i++) {
287 if (!pwrdm->pwrdm_clkdms[i])
288 break;
289#ifdef DEBUG
290 if (pwrdm->pwrdm_clkdms[i] == clkdm) {
291 ret = -EINVAL;
292 goto pac_exit;
293 }
294#endif
295 }
296
297 if (i == PWRDM_MAX_CLKDMS) {
298 pr_debug("powerdomain: increase PWRDM_MAX_CLKDMS for "
299 "pwrdm %s clkdm %s\n", pwrdm->name, clkdm->name);
300 WARN_ON(1);
301 ret = -ENOMEM;
302 goto pac_exit;
303 }
304
305 pwrdm->pwrdm_clkdms[i] = clkdm;
306
307 ret = 0;
308
309pac_exit:
Paul Walmsley8420bb12008-08-19 11:08:44 +0300310 return ret;
311}
312
313/**
314 * pwrdm_del_clkdm - remove a clockdomain from a powerdomain
315 * @pwrdm: struct powerdomain * to add the clockdomain to
316 * @clkdm: struct clockdomain * to associate with a powerdomain
317 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700318 * Dissociate the clockdomain @clkdm from the powerdomain
319 * @pwrdm. Returns -EINVAL if presented with invalid pointers; -ENOENT
320 * if @clkdm was not associated with the powerdomain, or 0 upon
321 * success.
Paul Walmsley8420bb12008-08-19 11:08:44 +0300322 */
323int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
324{
Paul Walmsley8420bb12008-08-19 11:08:44 +0300325 int ret = -EINVAL;
326 int i;
327
328 if (!pwrdm || !clkdm)
329 return -EINVAL;
330
331 pr_debug("powerdomain: dissociating clockdomain %s from powerdomain "
332 "%s\n", clkdm->name, pwrdm->name);
333
Paul Walmsley8420bb12008-08-19 11:08:44 +0300334 for (i = 0; i < PWRDM_MAX_CLKDMS; i++)
335 if (pwrdm->pwrdm_clkdms[i] == clkdm)
336 break;
337
338 if (i == PWRDM_MAX_CLKDMS) {
339 pr_debug("powerdomain: clkdm %s not associated with pwrdm "
340 "%s ?!\n", clkdm->name, pwrdm->name);
341 ret = -ENOENT;
342 goto pdc_exit;
343 }
344
345 pwrdm->pwrdm_clkdms[i] = NULL;
346
347 ret = 0;
348
349pdc_exit:
Paul Walmsley8420bb12008-08-19 11:08:44 +0300350 return ret;
351}
352
353/**
354 * pwrdm_for_each_clkdm - call function on each clkdm in a pwrdm
355 * @pwrdm: struct powerdomain * to iterate over
356 * @fn: callback function *
357 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700358 * Call the supplied function @fn for each clockdomain in the powerdomain
359 * @pwrdm. The callback function can return anything but 0 to bail
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700360 * out early from the iterator. Returns -EINVAL if presented with
361 * invalid pointers; or passes along the last return value of the
362 * callback function, which should be 0 for success or anything else
363 * to indicate failure.
Paul Walmsley8420bb12008-08-19 11:08:44 +0300364 */
365int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
366 int (*fn)(struct powerdomain *pwrdm,
367 struct clockdomain *clkdm))
368{
Paul Walmsley8420bb12008-08-19 11:08:44 +0300369 int ret = 0;
370 int i;
371
372 if (!fn)
373 return -EINVAL;
374
Paul Walmsley8420bb12008-08-19 11:08:44 +0300375 for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++)
376 ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
377
Paul Walmsley8420bb12008-08-19 11:08:44 +0300378 return ret;
379}
380
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300381/**
382 * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
383 * @pwrdm: struct powerdomain *
384 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700385 * Return the number of controllable memory banks in powerdomain @pwrdm,
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300386 * starting with 1. Returns -EINVAL if the powerdomain pointer is null.
387 */
388int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm)
389{
390 if (!pwrdm)
391 return -EINVAL;
392
393 return pwrdm->banks;
394}
395
396/**
397 * pwrdm_set_next_pwrst - set next powerdomain power state
398 * @pwrdm: struct powerdomain * to set
399 * @pwrst: one of the PWRDM_POWER_* macros
400 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700401 * Set the powerdomain @pwrdm's next power state to @pwrst. The powerdomain
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300402 * may not enter this state immediately if the preconditions for this state
403 * have not been satisfied. Returns -EINVAL if the powerdomain pointer is
404 * null or if the power state is invalid for the powerdomin, or returns 0
405 * upon success.
406 */
407int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
408{
409 if (!pwrdm)
410 return -EINVAL;
411
412 if (!(pwrdm->pwrsts & (1 << pwrst)))
413 return -EINVAL;
414
415 pr_debug("powerdomain: setting next powerstate for %s to %0x\n",
416 pwrdm->name, pwrst);
417
418 prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK,
419 (pwrst << OMAP_POWERSTATE_SHIFT),
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700420 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300421
422 return 0;
423}
424
425/**
426 * pwrdm_read_next_pwrst - get next powerdomain power state
427 * @pwrdm: struct powerdomain * to get power state
428 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700429 * Return the powerdomain @pwrdm's next power state. Returns -EINVAL
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300430 * if the powerdomain pointer is null or returns the next power state
431 * upon success.
432 */
433int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
434{
435 if (!pwrdm)
436 return -EINVAL;
437
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700438 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
439 pwrstctrl_reg_offs, OMAP_POWERSTATE_MASK);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300440}
441
442/**
443 * pwrdm_read_pwrst - get current powerdomain power state
444 * @pwrdm: struct powerdomain * to get power state
445 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700446 * Return the powerdomain @pwrdm's current power state. Returns -EINVAL
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300447 * if the powerdomain pointer is null or returns the current power state
448 * upon success.
449 */
450int pwrdm_read_pwrst(struct powerdomain *pwrdm)
451{
452 if (!pwrdm)
453 return -EINVAL;
454
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700455 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
456 pwrstst_reg_offs, OMAP_POWERSTATEST_MASK);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300457}
458
459/**
460 * pwrdm_read_prev_pwrst - get previous powerdomain power state
461 * @pwrdm: struct powerdomain * to get previous power state
462 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700463 * Return the powerdomain @pwrdm's previous power state. Returns -EINVAL
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300464 * if the powerdomain pointer is null or returns the previous power state
465 * upon success.
466 */
467int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
468{
469 if (!pwrdm)
470 return -EINVAL;
471
472 return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
473 OMAP3430_LASTPOWERSTATEENTERED_MASK);
474}
475
476/**
477 * pwrdm_set_logic_retst - set powerdomain logic power state upon retention
478 * @pwrdm: struct powerdomain * to set
479 * @pwrst: one of the PWRDM_POWER_* macros
480 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700481 * Set the next power state @pwrst that the logic portion of the
482 * powerdomain @pwrdm will enter when the powerdomain enters retention.
483 * This will be either RETENTION or OFF, if supported. Returns
484 * -EINVAL if the powerdomain pointer is null or the target power
485 * state is not not supported, or returns 0 upon success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300486 */
487int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
488{
489 if (!pwrdm)
490 return -EINVAL;
491
492 if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst)))
493 return -EINVAL;
494
495 pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n",
496 pwrdm->name, pwrst);
497
498 /*
499 * The register bit names below may not correspond to the
500 * actual names of the bits in each powerdomain's register,
501 * but the type of value returned is the same for each
502 * powerdomain.
503 */
504 prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE,
505 (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)),
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_set_mem_onst - set memory power state while powerdomain ON
513 * @pwrdm: struct powerdomain * to set
514 * @bank: memory bank number to set (0-3)
515 * @pwrst: one of the PWRDM_POWER_* macros
516 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700517 * Set the next power state @pwrst that memory bank @bank of the
518 * powerdomain @pwrdm will enter when the powerdomain enters the ON
519 * state. @bank will be a number from 0 to 3, and represents different
520 * types of memory, depending on the powerdomain. Returns -EINVAL if
521 * the powerdomain pointer is null or the target power state is not
522 * not supported for this memory bank, -EEXIST if the target memory
523 * bank does not exist or is not controllable, or returns 0 upon
524 * success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300525 */
526int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
527{
528 u32 m;
529
530 if (!pwrdm)
531 return -EINVAL;
532
533 if (pwrdm->banks < (bank + 1))
534 return -EEXIST;
535
536 if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
537 return -EINVAL;
538
539 pr_debug("powerdomain: setting next memory powerstate for domain %s "
540 "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst);
541
542 /*
543 * The register bit names below may not correspond to the
544 * actual names of the bits in each powerdomain's register,
545 * but the type of value returned is the same for each
546 * powerdomain.
547 */
548 switch (bank) {
549 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700550 m = OMAP_MEM0_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300551 break;
552 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700553 m = OMAP_MEM1_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300554 break;
555 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700556 m = OMAP_MEM2_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300557 break;
558 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700559 m = OMAP_MEM3_ONSTATE_MASK;
560 break;
561 case 4:
562 m = OMAP_MEM4_ONSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300563 break;
564 default:
565 WARN_ON(1); /* should never happen */
566 return -EEXIST;
567 }
568
569 prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)),
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700570 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300571
572 return 0;
573}
574
575/**
576 * pwrdm_set_mem_retst - set memory power state while powerdomain in RET
577 * @pwrdm: struct powerdomain * to set
578 * @bank: memory bank number to set (0-3)
579 * @pwrst: one of the PWRDM_POWER_* macros
580 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700581 * Set the next power state @pwrst that memory bank @bank of the
582 * powerdomain @pwrdm will enter when the powerdomain enters the
583 * RETENTION state. Bank will be a number from 0 to 3, and represents
584 * different types of memory, depending on the powerdomain. @pwrst
585 * will be either RETENTION or OFF, if supported. Returns -EINVAL if
586 * the powerdomain pointer is null or the target power state is not
587 * not supported for this memory bank, -EEXIST if the target memory
588 * bank does not exist or is not controllable, or returns 0 upon
589 * success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300590 */
591int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
592{
593 u32 m;
594
595 if (!pwrdm)
596 return -EINVAL;
597
598 if (pwrdm->banks < (bank + 1))
599 return -EEXIST;
600
601 if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
602 return -EINVAL;
603
604 pr_debug("powerdomain: setting next memory powerstate for domain %s "
605 "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst);
606
607 /*
608 * The register bit names below may not correspond to the
609 * actual names of the bits in each powerdomain's register,
610 * but the type of value returned is the same for each
611 * powerdomain.
612 */
613 switch (bank) {
614 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700615 m = OMAP_MEM0_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300616 break;
617 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700618 m = OMAP_MEM1_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300619 break;
620 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700621 m = OMAP_MEM2_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300622 break;
623 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700624 m = OMAP_MEM3_RETSTATE_MASK;
625 break;
626 case 4:
627 m = OMAP_MEM4_RETSTATE_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300628 break;
629 default:
630 WARN_ON(1); /* should never happen */
631 return -EEXIST;
632 }
633
634 prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700635 pwrstctrl_reg_offs);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300636
637 return 0;
638}
639
640/**
641 * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state
642 * @pwrdm: struct powerdomain * to get current logic retention power state
643 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700644 * Return the power state that the logic portion of powerdomain @pwrdm
645 * will enter when the powerdomain enters retention. Returns -EINVAL
646 * if the powerdomain pointer is null or returns the logic retention
647 * power state upon success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300648 */
649int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
650{
651 if (!pwrdm)
652 return -EINVAL;
653
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700654 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
655 pwrstst_reg_offs, OMAP3430_LOGICSTATEST);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300656}
657
658/**
659 * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state
660 * @pwrdm: struct powerdomain * to get previous logic power state
661 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700662 * Return the powerdomain @pwrdm's previous logic power state. Returns
663 * -EINVAL if the powerdomain pointer is null or returns the previous
664 * logic power state upon success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300665 */
666int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm)
667{
668 if (!pwrdm)
669 return -EINVAL;
670
671 /*
672 * The register bit names below may not correspond to the
673 * actual names of the bits in each powerdomain's register,
674 * but the type of value returned is the same for each
675 * powerdomain.
676 */
677 return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
678 OMAP3430_LASTLOGICSTATEENTERED);
679}
680
681/**
682 * pwrdm_read_mem_pwrst - get current memory bank power state
683 * @pwrdm: struct powerdomain * to get current memory bank power state
684 * @bank: memory bank number (0-3)
685 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700686 * Return the powerdomain @pwrdm's current memory power state for bank
687 * @bank. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300688 * the target memory bank does not exist or is not controllable, or
689 * returns the current memory power state upon success.
690 */
691int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
692{
693 u32 m;
694
695 if (!pwrdm)
696 return -EINVAL;
697
698 if (pwrdm->banks < (bank + 1))
699 return -EEXIST;
700
Thara Gopinath3863c742009-12-08 16:33:15 -0700701 if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
702 bank = 1;
703
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300704 /*
705 * The register bit names below may not correspond to the
706 * actual names of the bits in each powerdomain's register,
707 * but the type of value returned is the same for each
708 * powerdomain.
709 */
710 switch (bank) {
711 case 0:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700712 m = OMAP_MEM0_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300713 break;
714 case 1:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700715 m = OMAP_MEM1_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300716 break;
717 case 2:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700718 m = OMAP_MEM2_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300719 break;
720 case 3:
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700721 m = OMAP_MEM3_STATEST_MASK;
722 break;
723 case 4:
724 m = OMAP_MEM4_STATEST_MASK;
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300725 break;
726 default:
727 WARN_ON(1); /* should never happen */
728 return -EEXIST;
729 }
730
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700731 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
732 pwrstst_reg_offs, m);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300733}
734
735/**
736 * pwrdm_read_prev_mem_pwrst - get previous memory bank power state
737 * @pwrdm: struct powerdomain * to get previous memory bank power state
738 * @bank: memory bank number (0-3)
739 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700740 * Return the powerdomain @pwrdm's previous memory power state for
741 * bank @bank. Returns -EINVAL if the powerdomain pointer is null,
742 * -EEXIST if the target memory bank does not exist or is not
743 * controllable, or returns the previous memory power state upon
744 * success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300745 */
746int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
747{
748 u32 m;
749
750 if (!pwrdm)
751 return -EINVAL;
752
753 if (pwrdm->banks < (bank + 1))
754 return -EEXIST;
755
Thara Gopinath3863c742009-12-08 16:33:15 -0700756 if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
757 bank = 1;
758
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300759 /*
760 * The register bit names below may not correspond to the
761 * actual names of the bits in each powerdomain's register,
762 * but the type of value returned is the same for each
763 * powerdomain.
764 */
765 switch (bank) {
766 case 0:
767 m = OMAP3430_LASTMEM1STATEENTERED_MASK;
768 break;
769 case 1:
770 m = OMAP3430_LASTMEM2STATEENTERED_MASK;
771 break;
772 case 2:
773 m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK;
774 break;
775 case 3:
776 m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK;
777 break;
778 default:
779 WARN_ON(1); /* should never happen */
780 return -EEXIST;
781 }
782
783 return prm_read_mod_bits_shift(pwrdm->prcm_offs,
784 OMAP3430_PM_PREPWSTST, m);
785}
786
787/**
788 * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
789 * @pwrdm: struct powerdomain * to clear
790 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700791 * Clear the powerdomain's previous power state register @pwrdm.
792 * Clears the entire register, including logic and memory bank
793 * previous power states. Returns -EINVAL if the powerdomain pointer
794 * is null, or returns 0 upon success.
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300795 */
796int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
797{
798 if (!pwrdm)
799 return -EINVAL;
800
801 /*
802 * XXX should get the powerdomain's current state here;
803 * warn & fail if it is not ON.
804 */
805
806 pr_debug("powerdomain: clearing previous power state reg for %s\n",
807 pwrdm->name);
808
809 prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST);
810
811 return 0;
812}
813
814/**
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600815 * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm
816 * @pwrdm: struct powerdomain *
817 *
818 * Enable automatic context save-and-restore upon power state change
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700819 * for some devices in the powerdomain @pwrdm. Warning: this only
820 * affects a subset of devices in a powerdomain; check the TRM
821 * closely. Returns -EINVAL if the powerdomain pointer is null or if
822 * the powerdomain does not support automatic save-and-restore, or
823 * returns 0 upon success.
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600824 */
825int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
826{
827 if (!pwrdm)
828 return -EINVAL;
829
830 if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
831 return -EINVAL;
832
833 pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n",
834 pwrdm->name);
835
836 prm_rmw_mod_reg_bits(0, 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700837 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600838
839 return 0;
840}
841
842/**
843 * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm
844 * @pwrdm: struct powerdomain *
845 *
846 * Disable automatic context save-and-restore upon power state change
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700847 * for some devices in the powerdomain @pwrdm. Warning: this only
848 * affects a subset of devices in a powerdomain; check the TRM
849 * closely. Returns -EINVAL if the powerdomain pointer is null or if
850 * the powerdomain does not support automatic save-and-restore, or
851 * returns 0 upon success.
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600852 */
853int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm)
854{
855 if (!pwrdm)
856 return -EINVAL;
857
858 if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
859 return -EINVAL;
860
861 pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n",
862 pwrdm->name);
863
864 prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, 0,
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700865 pwrdm->prcm_offs, pwrstctrl_reg_offs);
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600866
867 return 0;
868}
869
870/**
871 * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR
872 * @pwrdm: struct powerdomain *
873 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700874 * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore
Paul Walmsley0b7cbfb2008-06-25 18:09:37 -0600875 * for some devices, or 0 if it does not.
876 */
877bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
878{
879 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
880}
881
882/**
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300883 * pwrdm_wait_transition - wait for powerdomain power transition to finish
884 * @pwrdm: struct powerdomain * to wait for
885 *
Paul Walmsleyf0271d62010-01-26 20:13:02 -0700886 * If the powerdomain @pwrdm is in the process of a state transition,
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300887 * spin until it completes the power transition, or until an iteration
888 * bailout value is reached. Returns -EINVAL if the powerdomain
889 * pointer is null, -EAGAIN if the bailout value was reached, or
890 * returns 0 upon success.
891 */
892int pwrdm_wait_transition(struct powerdomain *pwrdm)
893{
894 u32 c = 0;
895
896 if (!pwrdm)
897 return -EINVAL;
898
899 /*
900 * REVISIT: pwrdm_wait_transition() may be better implemented
901 * via a callback and a periodic timer check -- how long do we expect
902 * powerdomain transitions to take?
903 */
904
905 /* XXX Is this udelay() value meaningful? */
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700906 while ((prm_read_mod_reg(pwrdm->prcm_offs, pwrstst_reg_offs) &
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300907 OMAP_INTRANSITION) &&
908 (c++ < PWRDM_TRANSITION_BAILOUT))
Abhijit Pagare3a759f02010-01-26 20:12:53 -0700909 udelay(1);
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300910
Roel Kluin26870692009-06-19 19:08:30 -0600911 if (c > PWRDM_TRANSITION_BAILOUT) {
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300912 printk(KERN_ERR "powerdomain: waited too long for "
913 "powerdomain %s to complete transition\n", pwrdm->name);
914 return -EAGAIN;
915 }
916
917 pr_debug("powerdomain: completed transition in %d loops\n", c);
918
919 return 0;
920}
921
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300922int pwrdm_state_switch(struct powerdomain *pwrdm)
923{
924 return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
925}
926
927int pwrdm_clkdm_state_switch(struct clockdomain *clkdm)
928{
929 if (clkdm != NULL && clkdm->pwrdm.ptr != NULL) {
930 pwrdm_wait_transition(clkdm->pwrdm.ptr);
931 return pwrdm_state_switch(clkdm->pwrdm.ptr);
932 }
933
934 return -EINVAL;
935}
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300936
937int pwrdm_pre_transition(void)
938{
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300939 pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300940 return 0;
941}
942
943int pwrdm_post_transition(void)
944{
Peter 'p2' De Schrijver6199ab22008-10-15 18:13:49 +0300945 pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300946 return 0;
947}
Paul Walmsleyad67ef62008-08-19 11:08:40 +0300948