Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
powerdomain.c
Go to the documentation of this file.
1 /*
2  * OMAP powerdomain control
3  *
4  * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc.
5  * Copyright (C) 2007-2011 Nokia Corporation
6  *
7  * Written by Paul Walmsley
8  * Added OMAP4 specific support by Abhijit Pagare <[email protected]>
9  * State counting code by Tero Kristo <[email protected]>
10  *
11  * 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  */
15 #undef DEBUG
16 
17 #include <linux/kernel.h>
18 #include <linux/types.h>
19 #include <linux/list.h>
20 #include <linux/errno.h>
21 #include <linux/string.h>
22 #include <trace/events/power.h>
23 
24 #include "cm2xxx_3xxx.h"
25 #include "prcm44xx.h"
26 #include "cm44xx.h"
27 #include "prm2xxx_3xxx.h"
28 #include "prm44xx.h"
29 
30 #include <asm/cpu.h>
31 
32 #include <plat/prcm.h>
33 
34 #include "powerdomain.h"
35 #include "clockdomain.h"
36 
37 #include "soc.h"
38 #include "pm.h"
39 
40 #define PWRDM_TRACE_STATES_FLAG (1<<31)
41 
42 enum {
45 };
46 
47 
48 /* pwrdm_list contains all registered struct powerdomains */
49 static LIST_HEAD(pwrdm_list);
50 
51 static struct pwrdm_ops *arch_pwrdm;
52 
53 /* Private functions */
54 
55 static struct powerdomain *_pwrdm_lookup(const char *name)
56 {
57  struct powerdomain *pwrdm, *temp_pwrdm;
58 
59  pwrdm = NULL;
60 
61  list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
62  if (!strcmp(name, temp_pwrdm->name)) {
63  pwrdm = temp_pwrdm;
64  break;
65  }
66  }
67 
68  return pwrdm;
69 }
70 
79 static int _pwrdm_register(struct powerdomain *pwrdm)
80 {
81  int i;
82  struct voltagedomain *voltdm;
83 
84  if (!pwrdm || !pwrdm->name)
85  return -EINVAL;
86 
87  if (cpu_is_omap44xx() &&
89  pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n",
90  pwrdm->name);
91  return -EINVAL;
92  }
93 
94  if (_pwrdm_lookup(pwrdm->name))
95  return -EEXIST;
96 
97  voltdm = voltdm_lookup(pwrdm->voltdm.name);
98  if (!voltdm) {
99  pr_err("powerdomain: %s: voltagedomain %s does not exist\n",
100  pwrdm->name, pwrdm->voltdm.name);
101  return -EINVAL;
102  }
103  pwrdm->voltdm.ptr = voltdm;
104  INIT_LIST_HEAD(&pwrdm->voltdm_node);
105  voltdm_add_pwrdm(voltdm, pwrdm);
106 
107  list_add(&pwrdm->node, &pwrdm_list);
108 
109  /* Initialize the powerdomain's state counter */
110  for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
111  pwrdm->state_counter[i] = 0;
112 
113  pwrdm->ret_logic_off_counter = 0;
114  for (i = 0; i < pwrdm->banks; i++)
115  pwrdm->ret_mem_off_counter[i] = 0;
116 
117  pwrdm_wait_transition(pwrdm);
118  pwrdm->state = pwrdm_read_pwrst(pwrdm);
119  pwrdm->state_counter[pwrdm->state] = 1;
120 
121  pr_debug("powerdomain: registered %s\n", pwrdm->name);
122 
123  return 0;
124 }
125 
126 static void _update_logic_membank_counters(struct powerdomain *pwrdm)
127 {
128  int i;
129  u8 prev_logic_pwrst, prev_mem_pwrst;
130 
131  prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm);
132  if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
133  (prev_logic_pwrst == PWRDM_POWER_OFF))
134  pwrdm->ret_logic_off_counter++;
135 
136  for (i = 0; i < pwrdm->banks; i++) {
137  prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i);
138 
139  if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) &&
140  (prev_mem_pwrst == PWRDM_POWER_OFF))
141  pwrdm->ret_mem_off_counter[i]++;
142  }
143 }
144 
145 static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
146 {
147 
148  int prev, state, trace_state = 0;
149 
150  if (pwrdm == NULL)
151  return -EINVAL;
152 
153  state = pwrdm_read_pwrst(pwrdm);
154 
155  switch (flag) {
156  case PWRDM_STATE_NOW:
157  prev = pwrdm->state;
158  break;
159  case PWRDM_STATE_PREV:
160  prev = pwrdm_read_prev_pwrst(pwrdm);
161  if (pwrdm->state != prev)
162  pwrdm->state_counter[prev]++;
163  if (prev == PWRDM_POWER_RET)
164  _update_logic_membank_counters(pwrdm);
165  /*
166  * If the power domain did not hit the desired state,
167  * generate a trace event with both the desired and hit states
168  */
169  if (state != prev) {
170  trace_state = (PWRDM_TRACE_STATES_FLAG |
171  ((state & OMAP_POWERSTATE_MASK) << 8) |
172  ((prev & OMAP_POWERSTATE_MASK) << 0));
173  trace_power_domain_target(pwrdm->name, trace_state,
174  smp_processor_id());
175  }
176  break;
177  default:
178  return -EINVAL;
179  }
180 
181  if (state != prev)
182  pwrdm->state_counter[state]++;
183 
184  pm_dbg_update_time(pwrdm, prev);
185 
186  pwrdm->state = state;
187 
188  return 0;
189 }
190 
191 static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
192 {
194  _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
195  return 0;
196 }
197 
198 static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
199 {
200  _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
201  return 0;
202 }
203 
204 /* Public functions */
205 
217 {
218  if (!po)
219  return -EINVAL;
220 
221  if (arch_pwrdm)
222  return -EEXIST;
223 
224  arch_pwrdm = po;
225 
226  return 0;
227 }
228 
240 {
241  struct powerdomain **p = NULL;
242 
243  if (!arch_pwrdm)
244  return -EEXIST;
245 
246  if (!ps)
247  return -EINVAL;
248 
249  for (p = ps; *p; p++)
250  _pwrdm_register(*p);
251 
252  return 0;
253 }
254 
267 {
268  struct powerdomain *temp_p;
269 
270  if (list_empty(&pwrdm_list))
271  return -EACCES;
272 
273  list_for_each_entry(temp_p, &pwrdm_list, node)
275 
276  return 0;
277 }
278 
286 struct powerdomain *pwrdm_lookup(const char *name)
287 {
288  struct powerdomain *pwrdm;
289 
290  if (!name)
291  return NULL;
292 
293  pwrdm = _pwrdm_lookup(name);
294 
295  return pwrdm;
296 }
297 
308 int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
309  void *user)
310 {
311  struct powerdomain *temp_pwrdm;
312  int ret = 0;
313 
314  if (!fn)
315  return -EINVAL;
316 
317  list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
318  ret = (*fn)(temp_pwrdm, user);
319  if (ret)
320  break;
321  }
322 
323  return ret;
324 }
325 
336 int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
337 {
338  int i;
339  int ret = -EINVAL;
340 
341  if (!pwrdm || !clkdm)
342  return -EINVAL;
343 
344  pr_debug("powerdomain: %s: associating clockdomain %s\n",
345  pwrdm->name, clkdm->name);
346 
347  for (i = 0; i < PWRDM_MAX_CLKDMS; i++) {
348  if (!pwrdm->pwrdm_clkdms[i])
349  break;
350 #ifdef DEBUG
351  if (pwrdm->pwrdm_clkdms[i] == clkdm) {
352  ret = -EINVAL;
353  goto pac_exit;
354  }
355 #endif
356  }
357 
358  if (i == PWRDM_MAX_CLKDMS) {
359  pr_debug("powerdomain: %s: increase PWRDM_MAX_CLKDMS for clkdm %s\n",
360  pwrdm->name, clkdm->name);
361  WARN_ON(1);
362  ret = -ENOMEM;
363  goto pac_exit;
364  }
365 
366  pwrdm->pwrdm_clkdms[i] = clkdm;
367 
368  ret = 0;
369 
370 pac_exit:
371  return ret;
372 }
373 
384 int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
385 {
386  int ret = -EINVAL;
387  int i;
388 
389  if (!pwrdm || !clkdm)
390  return -EINVAL;
391 
392  pr_debug("powerdomain: %s: dissociating clockdomain %s\n",
393  pwrdm->name, clkdm->name);
394 
395  for (i = 0; i < PWRDM_MAX_CLKDMS; i++)
396  if (pwrdm->pwrdm_clkdms[i] == clkdm)
397  break;
398 
399  if (i == PWRDM_MAX_CLKDMS) {
400  pr_debug("powerdomain: %s: clkdm %s not associated?!\n",
401  pwrdm->name, clkdm->name);
402  ret = -ENOENT;
403  goto pdc_exit;
404  }
405 
406  pwrdm->pwrdm_clkdms[i] = NULL;
407 
408  ret = 0;
409 
410 pdc_exit:
411  return ret;
412 }
413 
427  int (*fn)(struct powerdomain *pwrdm,
428  struct clockdomain *clkdm))
429 {
430  int ret = 0;
431  int i;
432 
433  if (!fn)
434  return -EINVAL;
435 
436  for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++)
437  ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
438 
439  return ret;
440 }
441 
450 {
451  return pwrdm->voltdm.ptr;
452 }
453 
462 {
463  if (!pwrdm)
464  return -EINVAL;
465 
466  return pwrdm->banks;
467 }
468 
480 int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
481 {
482  int ret = -EINVAL;
483 
484  if (!pwrdm)
485  return -EINVAL;
486 
487  if (!(pwrdm->pwrsts & (1 << pwrst)))
488  return -EINVAL;
489 
490  pr_debug("powerdomain: %s: setting next powerstate to %0x\n",
491  pwrdm->name, pwrst);
492 
493  if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) {
494  /* Trace the pwrdm desired target state */
495  trace_power_domain_target(pwrdm->name, pwrst,
496  smp_processor_id());
497  /* Program the pwrdm desired target state */
498  ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst);
499  }
500 
501  return ret;
502 }
503 
513 {
514  int ret = -EINVAL;
515 
516  if (!pwrdm)
517  return -EINVAL;
518 
519  if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst)
520  ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
521 
522  return ret;
523 }
524 
534 int pwrdm_read_pwrst(struct powerdomain *pwrdm)
535 {
536  int ret = -EINVAL;
537 
538  if (!pwrdm)
539  return -EINVAL;
540 
541  if (pwrdm->pwrsts == PWRSTS_ON)
542  return PWRDM_POWER_ON;
543 
544  if (arch_pwrdm && arch_pwrdm->pwrdm_read_pwrst)
545  ret = arch_pwrdm->pwrdm_read_pwrst(pwrdm);
546 
547  return ret;
548 }
549 
559 {
560  int ret = -EINVAL;
561 
562  if (!pwrdm)
563  return -EINVAL;
564 
565  if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_pwrst)
566  ret = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm);
567 
568  return ret;
569 }
570 
582 int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
583 {
584  int ret = -EINVAL;
585 
586  if (!pwrdm)
587  return -EINVAL;
588 
589  if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst)))
590  return -EINVAL;
591 
592  pr_debug("powerdomain: %s: setting next logic powerstate to %0x\n",
593  pwrdm->name, pwrst);
594 
595  if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst)
596  ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst);
597 
598  return ret;
599 }
600 
616 int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
617 {
618  int ret = -EINVAL;
619 
620  if (!pwrdm)
621  return -EINVAL;
622 
623  if (pwrdm->banks < (bank + 1))
624  return -EEXIST;
625 
626  if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
627  return -EINVAL;
628 
629  pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n",
630  pwrdm->name, bank, pwrst);
631 
632  if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst)
633  ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst);
634 
635  return ret;
636 }
637 
654 int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
655 {
656  int ret = -EINVAL;
657 
658  if (!pwrdm)
659  return -EINVAL;
660 
661  if (pwrdm->banks < (bank + 1))
662  return -EEXIST;
663 
664  if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
665  return -EINVAL;
666 
667  pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n",
668  pwrdm->name, bank, pwrst);
669 
670  if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst)
671  ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst);
672 
673  return ret;
674 }
675 
686 {
687  int ret = -EINVAL;
688 
689  if (!pwrdm)
690  return -EINVAL;
691 
692  if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst)
693  ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
694 
695  return ret;
696 }
697 
707 {
708  int ret = -EINVAL;
709 
710  if (!pwrdm)
711  return -EINVAL;
712 
713  if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst)
714  ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm);
715 
716  return ret;
717 }
718 
728 {
729  int ret = -EINVAL;
730 
731  if (!pwrdm)
732  return -EINVAL;
733 
734  if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst)
735  ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm);
736 
737  return ret;
738 }
739 
750 int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
751 {
752  int ret = -EINVAL;
753 
754  if (!pwrdm)
755  return ret;
756 
757  if (pwrdm->banks < (bank + 1))
758  return ret;
759 
760  if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
761  bank = 1;
762 
763  if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_pwrst)
764  ret = arch_pwrdm->pwrdm_read_mem_pwrst(pwrdm, bank);
765 
766  return ret;
767 }
768 
780 int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
781 {
782  int ret = -EINVAL;
783 
784  if (!pwrdm)
785  return ret;
786 
787  if (pwrdm->banks < (bank + 1))
788  return ret;
789 
790  if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
791  bank = 1;
792 
793  if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_mem_pwrst)
794  ret = arch_pwrdm->pwrdm_read_prev_mem_pwrst(pwrdm, bank);
795 
796  return ret;
797 }
798 
809 int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank)
810 {
811  int ret = -EINVAL;
812 
813  if (!pwrdm)
814  return ret;
815 
816  if (pwrdm->banks < (bank + 1))
817  return ret;
818 
819  if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst)
820  ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank);
821 
822  return ret;
823 }
824 
835 {
836  int ret = -EINVAL;
837 
838  if (!pwrdm)
839  return ret;
840 
841  /*
842  * XXX should get the powerdomain's current state here;
843  * warn & fail if it is not ON.
844  */
845 
846  pr_debug("powerdomain: %s: clearing previous power state reg\n",
847  pwrdm->name);
848 
849  if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst)
850  ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm);
851 
852  return ret;
853 }
854 
867 {
868  int ret = -EINVAL;
869 
870  if (!pwrdm)
871  return ret;
872 
873  if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
874  return ret;
875 
876  pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name);
877 
878  if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar)
879  ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm);
880 
881  return ret;
882 }
883 
896 {
897  int ret = -EINVAL;
898 
899  if (!pwrdm)
900  return ret;
901 
902  if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
903  return ret;
904 
905  pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name);
906 
907  if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar)
908  ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm);
909 
910  return ret;
911 }
912 
920 bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
921 {
922  return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
923 }
924 
936 {
937  int ret = -EINVAL;
938 
939  if (!pwrdm)
940  return -EINVAL;
941 
942  if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
943  return -EINVAL;
944 
945  pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
946  pwrdm->name);
947 
948  if (arch_pwrdm && arch_pwrdm->pwrdm_set_lowpwrstchange)
949  ret = arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
950 
951  return ret;
952 }
953 
965 {
966  int ret = -EINVAL;
967 
968  if (!pwrdm)
969  return -EINVAL;
970 
971  if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition)
972  ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
973 
974  return ret;
975 }
976 
977 int pwrdm_state_switch(struct powerdomain *pwrdm)
978 {
979  int ret;
980 
981  ret = pwrdm_wait_transition(pwrdm);
982  if (!ret)
983  ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
984 
985  return ret;
986 }
987 
989 {
990  if (pwrdm)
991  _pwrdm_pre_transition_cb(pwrdm, NULL);
992  else
993  pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
994 
995  return 0;
996 }
997 
999 {
1000  if (pwrdm)
1001  _pwrdm_post_transition_cb(pwrdm, NULL);
1002  else
1003  pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
1004 
1005  return 0;
1006 }
1007 
1017 {
1018  int i, count;
1019 
1020  if (!pwrdm) {
1021  WARN(1, "powerdomain: %s: pwrdm is null\n", __func__);
1022  return -ENODEV;
1023  }
1024 
1025  count = pwrdm->state_counter[PWRDM_POWER_OFF];
1026  count += pwrdm->ret_logic_off_counter;
1027 
1028  for (i = 0; i < pwrdm->banks; i++)
1029  count += pwrdm->ret_mem_off_counter[i];
1030 
1031  /*
1032  * Context loss count has to be a non-negative value. Clear the sign
1033  * bit to get a value range from 0 to INT_MAX.
1034  */
1035  count &= INT_MAX;
1036 
1037  pr_debug("powerdomain: %s: context loss count = %d\n",
1038  pwrdm->name, count);
1039 
1040  return count;
1041 }
1042 
1056 {
1057  int i;
1058 
1059  if (IS_ERR_OR_NULL(pwrdm)) {
1060  pr_debug("powerdomain: %s: invalid powerdomain pointer\n",
1061  __func__);
1062  return 1;
1063  }
1064 
1065  if (pwrdm->pwrsts & PWRSTS_OFF)
1066  return 1;
1067 
1068  if (pwrdm->pwrsts & PWRSTS_RET) {
1069  if (pwrdm->pwrsts_logic_ret & PWRSTS_OFF)
1070  return 1;
1071 
1072  for (i = 0; i < pwrdm->banks; i++)
1073  if (pwrdm->pwrsts_mem_ret[i] & PWRSTS_OFF)
1074  return 1;
1075  }
1076 
1077  for (i = 0; i < pwrdm->banks; i++)
1078  if (pwrdm->pwrsts_mem_on[i] & PWRSTS_OFF)
1079  return 1;
1080 
1081  return 0;
1082 }