clockdomain2xxx_3xxx.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * OMAP2 and OMAP3 clockdomain control
  3. *
  4. * Copyright (C) 2008-2010 Texas Instruments, Inc.
  5. * Copyright (C) 2008-2010 Nokia Corporation
  6. *
  7. * Derived from mach-omap2/clockdomain.c written by Paul Walmsley
  8. * Rajendra Nayak <rnayak@ti.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. */
  14. #include <linux/types.h>
  15. #include <plat/prcm.h>
  16. #include "soc.h"
  17. #include "prm.h"
  18. #include "prm2xxx_3xxx.h"
  19. #include "cm.h"
  20. #include "cm2xxx_3xxx.h"
  21. #include "cm-regbits-24xx.h"
  22. #include "cm-regbits-34xx.h"
  23. #include "prm-regbits-24xx.h"
  24. #include "clockdomain.h"
  25. static int omap2_clkdm_add_wkdep(struct clockdomain *clkdm1,
  26. struct clockdomain *clkdm2)
  27. {
  28. omap2_prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
  29. clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
  30. return 0;
  31. }
  32. static int omap2_clkdm_del_wkdep(struct clockdomain *clkdm1,
  33. struct clockdomain *clkdm2)
  34. {
  35. omap2_prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
  36. clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
  37. return 0;
  38. }
  39. static int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1,
  40. struct clockdomain *clkdm2)
  41. {
  42. return omap2_prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
  43. PM_WKDEP, (1 << clkdm2->dep_bit));
  44. }
  45. static int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
  46. {
  47. struct clkdm_dep *cd;
  48. u32 mask = 0;
  49. for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) {
  50. if (!cd->clkdm)
  51. continue; /* only happens if data is erroneous */
  52. /* PRM accesses are slow, so minimize them */
  53. mask |= 1 << cd->clkdm->dep_bit;
  54. atomic_set(&cd->wkdep_usecount, 0);
  55. }
  56. omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
  57. PM_WKDEP);
  58. return 0;
  59. }
  60. static int omap3_clkdm_add_sleepdep(struct clockdomain *clkdm1,
  61. struct clockdomain *clkdm2)
  62. {
  63. omap2_cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
  64. clkdm1->pwrdm.ptr->prcm_offs,
  65. OMAP3430_CM_SLEEPDEP);
  66. return 0;
  67. }
  68. static int omap3_clkdm_del_sleepdep(struct clockdomain *clkdm1,
  69. struct clockdomain *clkdm2)
  70. {
  71. omap2_cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
  72. clkdm1->pwrdm.ptr->prcm_offs,
  73. OMAP3430_CM_SLEEPDEP);
  74. return 0;
  75. }
  76. static int omap3_clkdm_read_sleepdep(struct clockdomain *clkdm1,
  77. struct clockdomain *clkdm2)
  78. {
  79. return omap2_prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
  80. OMAP3430_CM_SLEEPDEP, (1 << clkdm2->dep_bit));
  81. }
  82. static int omap3_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
  83. {
  84. struct clkdm_dep *cd;
  85. u32 mask = 0;
  86. for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) {
  87. if (!cd->clkdm)
  88. continue; /* only happens if data is erroneous */
  89. /* PRM accesses are slow, so minimize them */
  90. mask |= 1 << cd->clkdm->dep_bit;
  91. atomic_set(&cd->sleepdep_usecount, 0);
  92. }
  93. omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
  94. OMAP3430_CM_SLEEPDEP);
  95. return 0;
  96. }
  97. static int omap2_clkdm_sleep(struct clockdomain *clkdm)
  98. {
  99. omap2_cm_set_mod_reg_bits(OMAP24XX_FORCESTATE_MASK,
  100. clkdm->pwrdm.ptr->prcm_offs,
  101. OMAP2_PM_PWSTCTRL);
  102. return 0;
  103. }
  104. static int omap2_clkdm_wakeup(struct clockdomain *clkdm)
  105. {
  106. omap2_cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE_MASK,
  107. clkdm->pwrdm.ptr->prcm_offs,
  108. OMAP2_PM_PWSTCTRL);
  109. return 0;
  110. }
  111. static void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
  112. {
  113. if (atomic_read(&clkdm->usecount) > 0)
  114. _clkdm_add_autodeps(clkdm);
  115. omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  116. clkdm->clktrctrl_mask);
  117. }
  118. static void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
  119. {
  120. omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  121. clkdm->clktrctrl_mask);
  122. if (atomic_read(&clkdm->usecount) > 0)
  123. _clkdm_del_autodeps(clkdm);
  124. }
  125. static void _enable_hwsup(struct clockdomain *clkdm)
  126. {
  127. if (cpu_is_omap24xx())
  128. omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  129. clkdm->clktrctrl_mask);
  130. else if (cpu_is_omap34xx())
  131. omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  132. clkdm->clktrctrl_mask);
  133. }
  134. static void _disable_hwsup(struct clockdomain *clkdm)
  135. {
  136. if (cpu_is_omap24xx())
  137. omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  138. clkdm->clktrctrl_mask);
  139. else if (cpu_is_omap34xx())
  140. omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  141. clkdm->clktrctrl_mask);
  142. }
  143. static int omap3_clkdm_sleep(struct clockdomain *clkdm)
  144. {
  145. omap3xxx_cm_clkdm_force_sleep(clkdm->pwrdm.ptr->prcm_offs,
  146. clkdm->clktrctrl_mask);
  147. return 0;
  148. }
  149. static int omap3_clkdm_wakeup(struct clockdomain *clkdm)
  150. {
  151. omap3xxx_cm_clkdm_force_wakeup(clkdm->pwrdm.ptr->prcm_offs,
  152. clkdm->clktrctrl_mask);
  153. return 0;
  154. }
  155. static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
  156. {
  157. bool hwsup = false;
  158. if (!clkdm->clktrctrl_mask)
  159. return 0;
  160. hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  161. clkdm->clktrctrl_mask);
  162. if (hwsup) {
  163. /* Disable HW transitions when we are changing deps */
  164. _disable_hwsup(clkdm);
  165. _clkdm_add_autodeps(clkdm);
  166. _enable_hwsup(clkdm);
  167. } else {
  168. if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
  169. omap2_clkdm_wakeup(clkdm);
  170. }
  171. return 0;
  172. }
  173. static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
  174. {
  175. bool hwsup = false;
  176. if (!clkdm->clktrctrl_mask)
  177. return 0;
  178. hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  179. clkdm->clktrctrl_mask);
  180. if (hwsup) {
  181. /* Disable HW transitions when we are changing deps */
  182. _disable_hwsup(clkdm);
  183. _clkdm_del_autodeps(clkdm);
  184. _enable_hwsup(clkdm);
  185. } else {
  186. if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
  187. omap2_clkdm_sleep(clkdm);
  188. }
  189. return 0;
  190. }
  191. static void omap3_clkdm_allow_idle(struct clockdomain *clkdm)
  192. {
  193. if (atomic_read(&clkdm->usecount) > 0)
  194. _clkdm_add_autodeps(clkdm);
  195. omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  196. clkdm->clktrctrl_mask);
  197. }
  198. static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
  199. {
  200. omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  201. clkdm->clktrctrl_mask);
  202. if (atomic_read(&clkdm->usecount) > 0)
  203. _clkdm_del_autodeps(clkdm);
  204. }
  205. static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
  206. {
  207. bool hwsup = false;
  208. if (!clkdm->clktrctrl_mask)
  209. return 0;
  210. /*
  211. * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
  212. * more details on the unpleasant problem this is working
  213. * around
  214. */
  215. if ((clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) &&
  216. (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
  217. omap3_clkdm_wakeup(clkdm);
  218. return 0;
  219. }
  220. hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  221. clkdm->clktrctrl_mask);
  222. if (hwsup) {
  223. /* Disable HW transitions when we are changing deps */
  224. _disable_hwsup(clkdm);
  225. _clkdm_add_autodeps(clkdm);
  226. _enable_hwsup(clkdm);
  227. } else {
  228. if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
  229. omap3_clkdm_wakeup(clkdm);
  230. }
  231. return 0;
  232. }
  233. static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
  234. {
  235. bool hwsup = false;
  236. if (!clkdm->clktrctrl_mask)
  237. return 0;
  238. /*
  239. * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
  240. * more details on the unpleasant problem this is working
  241. * around
  242. */
  243. if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING &&
  244. !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
  245. _enable_hwsup(clkdm);
  246. return 0;
  247. }
  248. hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
  249. clkdm->clktrctrl_mask);
  250. if (hwsup) {
  251. /* Disable HW transitions when we are changing deps */
  252. _disable_hwsup(clkdm);
  253. _clkdm_del_autodeps(clkdm);
  254. _enable_hwsup(clkdm);
  255. } else {
  256. if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
  257. omap3_clkdm_sleep(clkdm);
  258. }
  259. return 0;
  260. }
  261. struct clkdm_ops omap2_clkdm_operations = {
  262. .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
  263. .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
  264. .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
  265. .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
  266. .clkdm_sleep = omap2_clkdm_sleep,
  267. .clkdm_wakeup = omap2_clkdm_wakeup,
  268. .clkdm_allow_idle = omap2_clkdm_allow_idle,
  269. .clkdm_deny_idle = omap2_clkdm_deny_idle,
  270. .clkdm_clk_enable = omap2_clkdm_clk_enable,
  271. .clkdm_clk_disable = omap2_clkdm_clk_disable,
  272. };
  273. struct clkdm_ops omap3_clkdm_operations = {
  274. .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
  275. .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
  276. .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
  277. .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
  278. .clkdm_add_sleepdep = omap3_clkdm_add_sleepdep,
  279. .clkdm_del_sleepdep = omap3_clkdm_del_sleepdep,
  280. .clkdm_read_sleepdep = omap3_clkdm_read_sleepdep,
  281. .clkdm_clear_all_sleepdeps = omap3_clkdm_clear_all_sleepdeps,
  282. .clkdm_sleep = omap3_clkdm_sleep,
  283. .clkdm_wakeup = omap3_clkdm_wakeup,
  284. .clkdm_allow_idle = omap3_clkdm_allow_idle,
  285. .clkdm_deny_idle = omap3_clkdm_deny_idle,
  286. .clkdm_clk_enable = omap3xxx_clkdm_clk_enable,
  287. .clkdm_clk_disable = omap3xxx_clkdm_clk_disable,
  288. };